<?xml version='1.0' encoding='UTF-8'?><rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/" xmlns:blogger="http://schemas.google.com/blogger/2008" xmlns:georss="http://www.georss.org/georss" xmlns:gd="http://schemas.google.com/g/2005" xmlns:thr="http://purl.org/syndication/thread/1.0" version="2.0"><channel><atom:id>tag:blogger.com,1999:blog-4389461234607418203</atom:id><lastBuildDate>Wed, 18 Mar 2026 07:29:01 +0000</lastBuildDate><category>開放原始碼</category><category>自由軟體</category><category>Linux</category><category>程式心得筆記</category><category>系統研發手札</category><category>心情筆記</category><category>JavaScript</category><category>C/C++</category><category>技術新知</category><category>Web 相關技術</category><category>Embedded System</category><category>Node.js</category><category>心得分享、講座</category><category>NodeJS</category><category>Hacking 心得筆記</category><category>Linux硬體驅動</category><category>Linux Kernel</category><category>有趣新構思</category><category>Web</category><category>LXDE</category><category>作業系統實作</category><category>User Interface</category><category>組合語言</category><category>X11</category><category>Android</category><category>patch</category><category>雲端(Cloud)</category><category>Express Web Framwork</category><category>Glib</category><category>io.js</category><category>商業</category><category>手機平台</category><category>Debian</category><category>HanGee 國民機運動</category><category>Desktop</category><category>ECMAScript 6</category><category>顧問咨詢</category><category>黑客松</category><category>Beagleboard</category><category>GTK</category><category>Hackathon</category><category>Hackathon Taiwan</category><category>Qt</category><category>devkit8000</category><category>活動</category><category>GNOME</category><category>Cross-compile</category><category>JSDX</category><category>Koa</category><category>NPM</category><category>QML</category><category>Web Framework</category><category>Window Manager</category><category>Clutter</category><category>Mandice</category><category>Moblin</category><category>MongoDB</category><category>Stem OS</category><category>數值方法</category><category>Java</category><category>PostgreSQL</category><category>USB</category><category>Webkit</category><category>推薦好書</category><category>消費經驗</category><category>8051</category><category>ACPI</category><category>AT Command</category><category>Chrome OS</category><category>Cloud</category><category>Cluster叢集架構</category><category>Cubieboard</category><category>DBus</category><category>ECMAScript</category><category>ECMAScript 7</category><category>Emdebian</category><category>FUSE</category><category>Flux</category><category>GSM/GPRS/HSDPA</category><category>Git</category><category>Juice</category><category>MIPS</category><category>Maker</category><category>MakerCup</category><category>Microservice</category><category>OpenGL</category><category>OwaNEXT</category><category>PL/pgSQL</category><category>RedTea Web Framework</category><category>Socket.IO</category><category>Startup</category><category>ULLab</category><category>V8</category><category>WebSocket</category><category>XPUD</category><category>libuv</category><category>哲學</category><category>微服務</category><category>旅行(Travel)</category><category>無線網路相關</category><category>物聯網</category><category>顧問資詢</category><category>0xdroid</category><category>3D</category><category>Autotool</category><category>Bash</category><category>CI/CD</category><category>Chewing</category><category>Container</category><category>Dalvik VM</category><category>Debug</category><category>DevOps</category><category>E17</category><category>Filesystem</category><category>Flat</category><category>Framebuffer</category><category>Functional Programming</category><category>GStreamer</category><category>Generator</category><category>GitLab</category><category>Google</category><category>HTML5</category><category>Hosting Platform</category><category>IT Transformation</category><category>IT 轉型</category><category>Isomorphic</category><category>Kamalan Web Framework</category><category>Kinect</category><category>Kubernetes</category><category>MT7688</category><category>MTK</category><category>MessageQueue</category><category>NodeSchool</category><category>OSDC</category><category>Op</category><category>Open Hardware</category><category>OpenCV</category><category>Opens</category><category>PHP</category><category>QEMU</category><category>Raspberry Pi</category><category>React</category><category>SCIM</category><category>Security</category><category>Session</category><category>Tablet</category><category>VM</category><category>Webpack</category><category>bittorrent</category><category>epaper</category><category>ibus</category><category>jpeglib</category><category>oFono</category><category>openmoko</category><category>python</category><category>sbc8100</category><title>Fred&#39;s blog</title><description>Never Stop Researching New Goods</description><link>https://fred-zone.blogspot.com/</link><managingEditor>noreply@blogger.com (Fred Chien)</managingEditor><generator>Blogger</generator><openSearch:totalResults>405</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>1000</openSearch:itemsPerPage><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-4312205774314829066</guid><pubDate>Thu, 31 Oct 2019 13:12:00 +0000</pubDate><atom:updated>2019-10-31T21:15:41.859+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">IT Transformation</category><category domain="http://www.blogger.com/atom/ns#">IT 轉型</category><category domain="http://www.blogger.com/atom/ns#">Microservice</category><category domain="http://www.blogger.com/atom/ns#">微服務</category><category domain="http://www.blogger.com/atom/ns#">技術新知</category><category domain="http://www.blogger.com/atom/ns#">雲端(Cloud)</category><title>從 IT 轉型看微服務</title><description>&lt;p&gt;從電腦、資訊科技開始普及後，每過一段時間，企業界就會開始出現新的 IT 轉型計畫，以整頓整個資訊系統、商業運行模式，然後試圖找出下一個世代的新方向。每一個具規模的企業，都生怕與新世代的技術和商業模式脫鉤，於是砸重金、拚人力，說什麼也要追上世界的腳步。當然，中間的技術供應鏈也不斷在改變，上一世代的王者或是贏家，在下一個世代可能不一定跟得上，因此身為終端用戶的企業，每當面臨新的 IT 轉型計畫，往往也跟無頭蒼蠅般， 不知所措甚至不小心會做錯了決定。&lt;/p&gt;

&lt;p&gt;回到十幾二十年前，你也許可以有機會一次性把一個大系統整個替換掉。但在今天資訊系統高度介入管理的時代，版本更迭無數次的平台，沒有一個系統能在一夕之間被整個換掉，哪怕你的品管和測試做得非常足夠，甚至是超標達成，也哪怕你事先的需求訪談談到祖宗十八代都瞭然於心。&lt;/p&gt;

&lt;p&gt;這常常是系統設計者和規劃者的誤區，看見許多表面問題，於是設計了一套全新、看起來完美的解決方案。而剩下沒照顧到的部分，都覺得只要能配合測試就可以邊走邊補足。事實上，現代資訊系統的維度和複雜度，已經超出人類的想像，太多的例外、不可控、溝通執行問題，遠遠超過理論上的執行方法。&lt;/p&gt;

&lt;p&gt;所以從系統規劃設計的角度來看，我們總是要慢慢來，一段段改善、替換，最終達成全面換血。而且越是龐大複雜且重要的系統，越要切割得越細，透過降維度的方法，來慢慢替換。所以我們在討論IT轉型，從一個肥大的系統換到另一個肥大的系統，已經不會是選項，系統架構的去耦合、高擴展性甚至是服務不中斷才是考量的重點。&lt;/p&gt;

&lt;p&gt;這也是為什麼「微服務架構」在近年來特別熱門，微服務真正在討論的不是容器化，而是在討論的是怎麼讓一個龐大的系統，更容易受到管控、維護和擴充，除了可以更容易實現災難隔離、例外狀況、整體高可用性，也可以更有彈性做各種變化。&lt;/p&gt;

&lt;p&gt;更重要的是，微服務架構下，其系統風險管理上，從在開發階段開始，每個部分就已經受到隔離和管控。也由於每個被拆解後的系統都非常小，遵循 SRP 原則，在開發維護上相對容易，也可以保證每個單元有更低的問題發生率。至少，你不會因為一顆螺絲或換一顆螺絲，導致整個塔倒塌。&lt;/p&gt;

&lt;p&gt;不過微服務架構卻也不是說要做就能做，對於規劃設計者來說，他必須很了解各種新舊技術議題、有能力做實務執行面的評估，也需要耐心做各種服務拆解梳理和引入正確的 Pattern 及技術，甚至很多時候要有走一步是一步且可以快速反應決策的能力。這些都不是隨便讓工程師去上個 DDD（Domain-driven Design）或是各種微服務設計課程，回來就能掌握的技能。&lt;/p&gt;

&lt;p&gt;以目前業界情況來說，由於微服務帶來的衝擊範疇，涉及了系統設計、開發管理甚至是業務規劃，將橫跨基礎設施（Infrastructure）、應用開發（Application Development）到終端使用者（End-user）的負責團隊。由於要相互配合的人太多，以致導入會是一個長期計劃，所以對於多數有意導入微服務的企業而言，到目前為止，最多也只能做到容器化而已。畢竟，微服務架構說白了，就是去中心化的分散式架構，這對傳統的系統整合設計人員、開發人員來說，都是相對陌生的領域。&lt;/p&gt;

&lt;p&gt;真正要能普及微服務，必須等到從企業內部到外部廠商，都能充分理解和懂得微服務架構方法，才能真正的落實，而這段路才剛開始而已。&lt;/p&gt;

&lt;p&gt;對了，別忘了微服務後，下一段還有邊緣運算（Edge Computing）等著大家。&lt;/p&gt;</description><link>https://fred-zone.blogspot.com/2019/10/it.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>3</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-2990881197078040512</guid><pubDate>Tue, 03 Sep 2019 17:46:00 +0000</pubDate><atom:updated>2019-09-04T01:46:15.214+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">CI/CD</category><category domain="http://www.blogger.com/atom/ns#">DevOps</category><category domain="http://www.blogger.com/atom/ns#">GitLab</category><category domain="http://www.blogger.com/atom/ns#">Kubernetes</category><category domain="http://www.blogger.com/atom/ns#">Microservice</category><category domain="http://www.blogger.com/atom/ns#">微服務</category><title>GitLab DevOps：Kubernetes 整合的 Applications 裝不起來怎麼辦？</title><description>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-osXbPeuJC5oLIaxZUAQHM2bTfK8NCMnhaM1ofPGbArKlkM_2Do0u2wQhntFTBlaQNhTLcXzYkpiYtFVE8FO3rK7JjX159CR-hlOQOc4Z_RGFuBimj6PU14XtBjo96CS0zstkzziwa-A/s1600/gitlab-oops.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-osXbPeuJC5oLIaxZUAQHM2bTfK8NCMnhaM1ofPGbArKlkM_2Do0u2wQhntFTBlaQNhTLcXzYkpiYtFVE8FO3rK7JjX159CR-hlOQOc4Z_RGFuBimj6PU14XtBjo96CS0zstkzziwa-A/s1600/gitlab-oops.png&quot; data-original-width=&quot;1024&quot; data-original-height=&quot;768&quot; /&gt;&lt;/a&gt;&lt;/div&gt;

&lt;p&gt;我們的平民化旅遊黑卡秘書服務 LiMaGo，系統已經開發了兩年，有一定的複雜度，一直以來在維護上其實都有一定困難。此外，不知道是好消息還是壞消息，由於我們開始拓展日本等其他國家的業務，也加強了行銷通路，所以業務數字持續增加，導致各種功能和後勤機制也持續在擴張。還有，許多隱藏尚未公開的新計畫，整個系統複雜度不斷的再增加，慢慢的一切都開始混亂。&lt;/p&gt;

&lt;p&gt;所以，為了重整系統，以及重建開發團隊的各種資源和管理流程，開始試圖引入了 Kubernetes 和 GitLab，並試著將 DevOps 流程給搭建起來。畢竟，自己的另外一家公司，是微服務和容器化的解決方案供應商，自己手上的東西要是沒有導入，也太講不過去。還好轉換的問題不太大，LiMaGo 的系統從第一天設計，就是採用微服務架構（Micro-service Architecture），甚至當時還設計了一套框架「Engined」，可以在 Node.js 上更容易開發微服務架構，只要專心在容器化和 DevOps 的工作上即可。&lt;/p&gt;

&lt;p&gt;於是動手把 Kubernetes 架好，也安裝好 GitLab CE。下一步，就是把 Kubernetes 的設定加入 GitLab，讓 GitLab 可以把 GitLab Runner 和 DevOps 相關機制跟手上的 Kubernetes 整合起來。&lt;/p&gt;

&lt;h2 id=&quot;toc_1&quot;&gt;糟糕！Applictions 裝不起來！&lt;/h2&gt;

&lt;p&gt;原本一切很順利，但在 GitLab 設定頁裡面的最後一個步驟「在 Kubernetes 上安裝 Applications」遭遇到莫名失敗，一開始是 Ingress 裝不起來，後來發現 Prometheus 也裝不起來，如下圖：&lt;/p&gt;

&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgq9LabcJqNx3uKmQUs-HZ1WPzGIuvyJ_tJyAXpNdv6HjryVJFZI355gfPgCSA9_C1Br920DG5L9uzKSMnFT0_mDQX_BC1i6sA05u-DlFlf2vkpjLCVpNW5mYbcjotajaIhY_UqhW6ABC4/s1600/%25E8%259E%25A2%25E5%25B9%2595%25E5%25BF%25AB%25E7%2585%25A7+2019-09-04+%25E4%25B8%258A%25E5%258D%258812.20.31.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgq9LabcJqNx3uKmQUs-HZ1WPzGIuvyJ_tJyAXpNdv6HjryVJFZI355gfPgCSA9_C1Br920DG5L9uzKSMnFT0_mDQX_BC1i6sA05u-DlFlf2vkpjLCVpNW5mYbcjotajaIhY_UqhW6ABC4/s640/%25E8%259E%25A2%25E5%25B9%2595%25E5%25BF%25AB%25E7%2585%25A7+2019-09-04+%25E4%25B8%258A%25E5%258D%258812.20.31.png&quot; width=&quot;640&quot; height=&quot;230&quot; data-original-width=&quot;1600&quot; data-original-height=&quot;575&quot; /&gt;&lt;/a&gt;&lt;/div&gt;

&lt;p&gt;透過 &lt;code&gt;kubectl&lt;/code&gt; 直接去 Kubernetes 裡面挖 logs ，得到了錯誤訊息：&lt;/p&gt;

&lt;div&gt;&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;$ kubectl logs install-prometheus -n gitlab-managed-apps -f
+ helm init --upgrade
Creating /root/.helm
Creating /root/.helm/repository
Creating /root/.helm/repository/cache
Creating /root/.helm/repository/local
Creating /root/.helm/plugins
Creating /root/.helm/starters
Creating /root/.helm/cache/archive
Creating /root/.helm/repository/repositories.yaml
Adding stable repo with URL: https://kubernetes-charts.storage.googleapis.com
Adding local repo with URL: http://127.0.0.1:8879/charts
$HELM_HOME has been configured at /root/.helm.

Tiller (the Helm server-side component) has been upgraded to the current version.
+ seq 1 30
+ helm version --tls --tls-ca-cert /data/helm/prometheus/config/ca.pem --tls-cert /data/helm/prometheus/config/cert.pem --tls-key /data/helm/prometheus/config/key.pem
Client: &amp;amp;version.Version{SemVer:&amp;quot;v2.14.3&amp;quot;, GitCommit:&amp;quot;0e7f3b6637f7af8fcfddb3d2941fcc7cbebb0085&amp;quot;, GitTreeState:&amp;quot;clean&amp;quot;}
Server: &amp;amp;version.Version{SemVer:&amp;quot;v2.14.3&amp;quot;, GitCommit:&amp;quot;0e7f3b6637f7af8fcfddb3d2941fcc7cbebb0085&amp;quot;, GitTreeState:&amp;quot;clean&amp;quot;}
+ break
+ helm upgrade prometheus stable/prometheus --install --reset-values --tls --tls-ca-cert /data/helm/prometheus/config/ca.pem --tls-cert /data/helm/prometheus/config/cert.pem --tls-key /data/helm/prometheus/config/key.pem --version 6.7.3 --set &amp;#39;rbac.create=true,rbac.enabled=true&amp;#39; --namespace gitlab-managed-apps -f /data/helm/prometheus/config/values.yaml
E0902 10:50:18.229952      28 portforward.go:372] error copying from remote stream to local connection: readfrom tcp4 127.0.0.1:42953-&amp;gt;127.0.0.1:44820: write tcp4 127.0.0.1:42953-&amp;gt;127.0.0.1:44820: write: broken pipe
Error: UPGRADE FAILED: &amp;quot;prometheus&amp;quot; has no deployed releases
UPGRADE FAILED
Error: &amp;quot;prometheus&amp;quot; has no deployed releases&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;雖然乍看起來是連線的問題，但我確定網路應該沒什麼問題，而且我手動操作 helm 都是正常的。而且在 GitLab 頁面上，不管我怎麼重試，問題依舊。&lt;/p&gt;

&lt;h2 id=&quot;toc_2&quot;&gt;Workaround: 直接連線到 Tiller 解決&lt;/h2&gt;

&lt;p&gt;上網試圖找了一些解決方法，卻都找不太到（大概是我關鍵字下的不對），最後決定試圖去直接操作 gitlab-managed-apps 底下的 Tiller 看看。於是找到了一則 GitLab 的 Issue，討論怎麼連線到 GitLab 所自動安裝出來的 Tiller：&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://gitlab.com/gitlab-org/gitlab-ce/issues/56591#workaround-for-now&quot;&gt;https://gitlab.com/gitlab-org/gitlab-ce/issues/56591#workaround-for-now&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;文中所說的連線方法如下：&lt;/p&gt;

&lt;div&gt;&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;export TILLER_NAMESPACE=&amp;quot;gitlab-managed-apps&amp;quot;

# 取得 gitlab-managed-apps namespace 底下的 Tiller 憑證和金鑰
kubectl get secrets/tiller-secret -n &amp;quot;$TILLER_NAMESPACE&amp;quot; -o &amp;quot;jsonpath={.data[&amp;#39;ca\.crt&amp;#39;]}&amp;quot; | base64 --decode &amp;gt; tiller-ca.crt
kubectl get secrets/tiller-secret -n &amp;quot;$TILLER_NAMESPACE&amp;quot; -o &amp;quot;jsonpath={.data[&amp;#39;tls\.crt&amp;#39;]}&amp;quot; | base64 --decode &amp;gt; tiller.crt
kubectl get secrets/tiller-secret -n &amp;quot;$TILLER_NAMESPACE&amp;quot; -o &amp;quot;jsonpath={.data[&amp;#39;tls\.key&amp;#39;]}&amp;quot; | base64 --decode &amp;gt; tiller.key

# 連線到 Tiller 並取得已安裝的套件清單
helm list --tiller-connection-timeout 30 --tls --tls-ca-cert tiller-ca.crt --tls-cert tiller.crt --tls-key tiller.key --all --tiller-namespace gitlab-managed-apps
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;幸運的是，用憑證和金鑰可以順利連線並取出 helm 的資訊，然後看到 ingress 和 prometheus 都出了問題：&lt;/p&gt;

&lt;div&gt;&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;$ helm list --tiller-connection-timeout 30 --tls --tls-ca-cert tiller-ca.crt --tls-cert tiller.crt --tls-key tiller.key --all --tiller-namespace gitlab-managed-apps
NAME        REVISION    UPDATED                     STATUS      CHART               APP VERSION NAMESPACE
ingress     1           Mon Sep  2 16:51:47 2019    FAILED      nginx-ingress-1.1.2 0.21.0      gitlab-managed-apps
prometheus  1           Mon Sep  2 17:42:41 2019    FAILED      prometheus-6.7.3    2.2.1       gitlab-managed-apps
runner      1           Mon Sep  2 17:33:55 2019    DEPLOYED    gitlab-runner-0.7.0 12.1.0      gitlab-managed-apps&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;就在我打算手動砍掉失敗的項目，然後重新安裝時，突然發現其實有人在討論類似的問題，雖然碰到的問題跟我不一樣：&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://gitlab.com/gitlab-org/gitlab-ce/issues/65326#note_198244170&quot;&gt;https://gitlab.com/gitlab-org/gitlab-ce/issues/65326#note_198244170&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;下面剛好有篇原發問者自己的回文，和我的思路一樣，說他砍掉後重新安裝就解決了問題：&lt;/p&gt;

&lt;div&gt;&lt;pre&gt;&lt;code class=&quot;language-shell&quot;&gt;helm delete --purge prometheus --tls --tls-ca-cert tiller-ca.crt --tls-cert tiller.crt --tls-key tiller.key --tiller-namespace gitlab-managed-apps&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;很好！我也搞定了！&lt;/p&gt;
</description><link>https://fred-zone.blogspot.com/2019/09/gitlab-devopskubernetes-applications.html</link><author>noreply@blogger.com (Fred Chien)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-osXbPeuJC5oLIaxZUAQHM2bTfK8NCMnhaM1ofPGbArKlkM_2Do0u2wQhntFTBlaQNhTLcXzYkpiYtFVE8FO3rK7JjX159CR-hlOQOc4Z_RGFuBimj6PU14XtBjo96CS0zstkzziwa-A/s72-c/gitlab-oops.png" height="72" width="72"/><thr:total>1</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-6604842568019681841</guid><pubDate>Sun, 22 Jul 2018 09:21:00 +0000</pubDate><atom:updated>2018-07-22T17:28:27.026+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Hacking 心得筆記</category><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">有趣新構思</category><category domain="http://www.blogger.com/atom/ns#">程式心得筆記</category><title>有趣的洗牌演算法</title><description>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhMwj2VdAxPbULTzoltJq7t96TUNDLY1L75Yu80yiOt4yn5HpnPHPS12HI7baMCARqmV1YIJFWODJYJcynpSc2hv9aeQYq2wG7-97obKJIhoDGvBuDw4-fUyJvUrZFHDAzhCnmcXBT2dXc/s1600/shuffle.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;768&quot; data-original-width=&quot;1024&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhMwj2VdAxPbULTzoltJq7t96TUNDLY1L75Yu80yiOt4yn5HpnPHPS12HI7baMCARqmV1YIJFWODJYJcynpSc2hv9aeQYq2wG7-97obKJIhoDGvBuDw4-fUyJvUrZFHDAzhCnmcXBT2dXc/s1600/shuffle.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
最近因為一些專案，所以需要實做一些撲克牌的洗牌機制。雖然這個動作看起來簡單，但其實對於開發者來說相當有趣，因為真的除了做這種牌類遊戲之外，平常很少用到這樣演算法，也由於有太多種做法，不免著迷於其中。&lt;br /&gt;
&lt;br /&gt;
洗牌目的就是讓結果隨機、不能預期，只不過雖然很多遊戲同樣都是圍繞在亂數產生上面，但撲克牌遊戲（或麻將遊戲）最大的不同，就是同一排組每次發出來的牌，一但發過了就不會再出現一次。這一點，和每次都可以出一到六點數的骰子遊戲，就完全不一樣，不是隨機出一個亂數就可以搞定。&lt;br /&gt;
&lt;h2 id=&quot;toc_1&quot;&gt;
準備工作：先準備個牌組&lt;/h2&gt;
開始前，先準備四種花色、A 到 K 的牌組，使我們可以以 0 至 51 的號碼去取得任意一張牌。&lt;br /&gt;
&lt;div&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const suits = [ &#39;S&#39;, &#39;H&#39;, &#39;D&#39;, &#39;C&#39; ];
const points = [ &#39;A&#39;, 2, 3, 4, 5, 6, 7, 8, 9, 10, &#39;J&#39;, &#39;Q&#39;, &#39;K&#39; ];
const cards = [];

for (let i = 0; i &amp;lt; 4; i++) {
    for (let j = 0; j &amp;lt; 13; j++) {
        cards.push(points[j] + suits[i]);
    }
}

console.log(cards[10]); // 10S（黑桃10）&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2 id=&quot;toc_2&quot;&gt;
方法一：硬幹&lt;/h2&gt;
最直覺的方法，不外乎就是不斷產生 52 張牌的亂數（0 ~ 51），然後檢查這張牌發過沒，如果牌發過了就重新產生一個新的亂數，持續這個步驟。&lt;br /&gt;
&lt;div&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;let shuffledCards = [];

while(shuffledCards.length != 52) {

    // 取得 0 ~ 51 的亂數
    let idx = Math.floor(Math.random() * 51);
    let card = cards[idx];
    
    // 檢查這張牌是否已經出現過
    if (shuffledCards.indexOf(card) !== -1)
        continue;

    // 沒出現過則放入陣列
    shuffledCards.push(card);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
雖然這樣的做法可以達成目的，但太不優雅，每次都要去掃一次過往的發牌紀錄，雖然 CPU 很快，這些繁瑣的檢查工作很快能完成，但這對開發者來說，做法太過噁心。&lt;br /&gt;
&lt;h2 id=&quot;toc_3&quot;&gt;
方法二：隨機抽牌&lt;/h2&gt;
這種方法也是種直覺會想到的方法，隨機把牌從牌組內抽出，使原本牌組的牌越來越少，直到原本的牌組被抽光。簡單來說，就是第一次抽 0 到 51 之間的一張牌，第二次抽 0 到 50 之間的一張牌，以此類推。因為牌越抽越少，我們每次產生出來的亂數範圍就越來越小。&lt;br /&gt;
&lt;div&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;let shuffledCards = [];

for (let left = 51; left &amp;gt;= 0; left--) {

    // 取得 0 至剩餘撲克牌數量之間的亂數
    let idx = Math.floor(Math.random() * left);
    let card = cards[idx];
    
    // 將該張牌從原牌組移除
    cards.splice(idx, 1);
    
    // 放入陣列
    shuffledCards.push(card);
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
這種方法似乎感覺比第一種方法好多了，但一直在破壞原有的牌組陣列，這又不太妥當。對於一個慣 C 來說，乍看下尤為不舒服，因為一般來說，普通陣列是不容許這樣一直變更大小的，記憶體的重配置總是會讓程式效率大幅下降。當然，如果採用的是鏈結（Linked-list）的做法，當然就沒什麼問題，而且這個例子也不是用 C 語言的陣列實作。&lt;br /&gt;
&lt;h2 id=&quot;toc_4&quot;&gt;
方法三：隨機交換&lt;/h2&gt;
隨機交換的方法，就是幫 0 到 51 位置上的每張牌，隨機選一個排組上的位置進行兩張牌的交換，換言之就是幫每張牌隨機選一個新位置。&lt;br /&gt;
&lt;div&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;// 複製一份牌組陣列
let shuffledCards = Object.assign({}, cards);

for (let i = 0; i &amp;lt; 52; i++) {

    // 隨機取一個位置
    let idx = Math.floor(Math.random() * 51);
    
    // 交換兩張牌
    let temp = shuffledCards[idx];
    shuffledCards[idx] = shuffledCards[i];
    shuffledCards[i] = temp;
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
如果選到了自己怎麼辦？不換位置也是種隨機的結果。&lt;br /&gt;
&lt;h2 id=&quot;toc_5&quot;&gt;
後記&lt;/h2&gt;
對開發者來說，琢磨這樣的演算法應用，相當有樂趣，可以暫時從工作的苦悶中跳出。追求達成同樣目的，但各種不同做法，有時候其實也是在磨練自己對各種邏輯思維，以及程式技法的掌握度。</description><link>https://fred-zone.blogspot.com/2018/07/blog-post.html</link><author>noreply@blogger.com (Fred Chien)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhMwj2VdAxPbULTzoltJq7t96TUNDLY1L75Yu80yiOt4yn5HpnPHPS12HI7baMCARqmV1YIJFWODJYJcynpSc2hv9aeQYq2wG7-97obKJIhoDGvBuDw4-fUyJvUrZFHDAzhCnmcXBT2dXc/s72-c/shuffle.png" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-6731515761576817962</guid><pubDate>Tue, 12 Sep 2017 10:55:00 +0000</pubDate><atom:updated>2017-09-13T03:34:44.005+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">Node.js</category><category domain="http://www.blogger.com/atom/ns#">Web</category><category domain="http://www.blogger.com/atom/ns#">Web 相關技術</category><category domain="http://www.blogger.com/atom/ns#">心得分享、講座</category><category domain="http://www.blogger.com/atom/ns#">程式心得筆記</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><title>打造自己的 Node.js Transform Stream</title><description>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhwvLD46JMINtdPkctqsLNQuVxnPbI_ru8LNtp1pa7iyC988YZt49O0zmPy-NWg7zH2odPLJ8Mq9UpakDW7j1jdBuj-Qlv3Xp4Q71wUBlanpKRpFwliAtrlqN3QTK2xzAoYZodBk_ldky8/s1600/TransformStream.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; data-original-height=&quot;768&quot; data-original-width=&quot;1024&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhwvLD46JMINtdPkctqsLNQuVxnPbI_ru8LNtp1pa7iyC988YZt49O0zmPy-NWg7zH2odPLJ8Mq9UpakDW7j1jdBuj-Qlv3Xp4Q71wUBlanpKRpFwliAtrlqN3QTK2xzAoYZodBk_ldky8/s1600/TransformStream.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;

&lt;p&gt;熟悉並學習實作 Node.js Stream，在 Node.js 開發者生涯裡是一件很重要的事，尤其在資料處理的工作上更是需要運用 Stream。在這些應該用的情境下，若不懂得使用 Stream，我們所開發出來的程式其執行效能及穩定性會相當令人擔心。&lt;/p&gt;

&lt;p&gt;而如果你從未自己實作過 Stream，從 Transform Stream 開始入手是一個好選擇，也是一個非常實用的開發技巧。&lt;/p&gt;

&lt;p&gt;更多關於 Stream 的說明，可以參閱 Node.js 官網上的文件：&lt;a href=&quot;https://nodejs.org/api/stream.html&quot;&gt;https://nodejs.org/api/stream.html&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;toc_1&quot;&gt;什麼是 Transform Stream？&lt;/h2&gt;

&lt;p&gt;你可能知道 Node.js 裡有多種 Stream 的機制，但其實主要是 ReadableStream 和 WritableStream 兩種基本 Stream 的組成和變化。而對一般開發者來說，最常自己實作的是 Transform Stream，你可以想像這是一個產品生產線上的加工器，進入 Transform Stream 的資料會被加工後輸出。&lt;/p&gt;

&lt;p&gt;而以一個 Stream 而言，Transform Stream 同時具有 ReadableStream（讀入）和 WritableStream（輸出）的特性，俗話說「左耳進右耳出」就是其最佳的寫照。&lt;/p&gt;

&lt;p&gt;舉一個 Node.js 官方的例子，利用 Gzip 的 Transform Stream 將通過的資料流進行壓縮：&lt;/p&gt;

&lt;div&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const zlib = require(&amp;#39;zlib&amp;#39;);
const gzip = zlib.createGzip();
const fs = require(&amp;#39;fs&amp;#39;);
const inp = fs.createReadStream(&amp;#39;input.txt&amp;#39;);
const out = fs.createWriteStream(&amp;#39;input.txt.gz&amp;#39;);

inp.pipe(gzip).pipe(out);&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2 id=&quot;toc_2&quot;&gt;實作第一個 Transform Stream&lt;/h2&gt;

&lt;p&gt;先不必暸解太多 Stream 的專有名詞和機制，若想要實作一個標準的「耳邊風」Stream，程式碼如下：&lt;/p&gt;

&lt;div&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const Transform = require(&amp;#39;stream&amp;#39;).Transform;
const util = require(&amp;#39;util&amp;#39;);

const MyTransform = module.exports = function(options) {

    // 直接呼叫時建立一個實例
    if (!(this instanceof MyTransform))
        return new Parser(options);

    // 呼叫原始 Transform 的 constructor
    Transform.call(this, options);
};

// 繼承 Transform
util.inherits(MyTransform, Transform);

// 當每一筆資料進來時
MyTransform.prototype._transform = function(data, encoding, callback) {

    // 將輸入進來的資料直接推送出去
    this.push(data);
    
    // 完成這筆資料的處理工作
    callback();
};&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;實際結合檔案讀取寫入，以使用這個 Transform Stream：&lt;/p&gt;

&lt;div&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const fs = require(&amp;#39;fs&amp;#39;);
const myStream = new MyTransform();

// 讀取檔案
const input = fs.createReadStream(&amp;#39;/my/file&amp;#39;);
const output = fs.createWriteStream(&amp;#39;/my/file.out&amp;#39;);

// 導入 myStream，輸出後寫入 file.out
input.pipe(myStream).pipe(output);&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2 id=&quot;toc_3&quot;&gt;範例：累積並打包一批資料輸出&lt;/h2&gt;

&lt;p&gt;有些實際應用中，我們需要累積一定量的資料，然後打包成一包輸出，尤其像是我們在做開放資料的處理時，總是會將整理好的資料一批批的批次寫入資料庫。會這樣做的原因，是因為一筆寫一次太耗時（與資料庫來回的時間），一次 50、100 或更多筆資料寫入資料庫，會有更好的效率。&lt;/p&gt;

&lt;p&gt;這時我們可以寫一個自己的 Transform Stream 來做到這件事，如每輸入 10 筆資料後，打包成一個陣列輸出：&lt;/p&gt;

&lt;div&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const Transform = require(&amp;#39;stream&amp;#39;).Transform;
const util = require(&amp;#39;util&amp;#39;);

const BatchStream = module.exports = function(options) {

    // 直接呼叫時建立一個實例
    if (!(this instanceof MyTransform))
        return new Parser(options);

    // 啟用 object mode，讓此 Stream 不是用文字或 Binary 格式，而是以物件形式輸入、輸出資料
    let opts = Object.assign(options, {
        objectMode: true
    });

    // 呼叫原始 Transform 的 constructor
    Transform.call(this, options);
    
    // 建立一個暫存區陣列
    this.batch = [];
};

// 繼承 Transform
util.inherits(BatchStream, Transform);

// 當每一筆資料進來時
BatchStream.prototype._transform = function(data, encoding, callback) {

    // 放入暫存區
    this.batch.push(data);
    
    // 每 10 筆推送出去一次
    if (this.batch.length === 10) {
        this.push(this.batch);
        
        // 清空暫存區
        this.batch = [];
    }
    
    // 完成這筆資料
    callback();
};

// 當前一個 Stream 的資料輸入已經全部完成時
BatchStream.prototype._flush = function(callback) {

    // 將尚未推送出去的資料送出去
    if (this.batch.length &amp;gt; 0) {
        this.push(this.batch);
        
        // 清空暫存區
        this.batch = [];
    }

    // 完成
    callback();
};&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2 id=&quot;toc_4&quot;&gt;實用的 Transform Stream 簡易用法&lt;/h2&gt;

&lt;p&gt;如果每一個 Transform Stream 都要先設計定義一個原型物件後才能使用，那也太煩人，在實際開發上會相當不便。這時可以運用簡單的方法，建立一個客製化的 Transform Stream：&lt;/p&gt;

&lt;div&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const fs = require(&amp;#39;fs&amp;#39;);
const Transform = require(&amp;#39;stream&amp;#39;).Transform;
const myStream = new MyTransform();

// 讀取檔案
const input = fs.createReadStream(&amp;#39;/my/file&amp;#39;);
const output = fs.createWriteStream(&amp;#39;/my/file.out&amp;#39;);

// 導入 myStream，輸出後寫入 file.out
input
    .pipe(new Transform({
        transform(data, encoding, callback) {
        
            // 將輸入進來的資料直接推送出去
            this.push(data);
            
            // 完成這筆資料的處理工作
            callback();
        }
    })
    .pipe(output);&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2 id=&quot;toc_5&quot;&gt;精簡！使用 ECMAScript 新支援的 class 關鍵字&lt;/h2&gt;

&lt;p&gt;在各種新一代的 JavaScript 引擎上，已經可以使用 &lt;code&gt;class&lt;/code&gt; 關鍵字來定義物件了，我們也可以使用 class 來定義自己的 Transform Stream，程式碼會看起來精簡許多：&lt;/p&gt;

&lt;div&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const Transform = require(&amp;#39;stream&amp;#39;).Transform;

class MyTransform extends Transform {

    constructor(options) {
        super(options)
    }

    // 當每一筆資料進來時
    _transform(data, encoding, callback) {
    
        // 將輸入進來的資料直接推送出去
        this.push(data);
        
        // 完成這筆資料的處理工作
        callback();
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2 id=&quot;toc_6&quot;&gt;再精簡一點的技巧：用 callback 推送資料&lt;/h2&gt;

&lt;p&gt;用 &lt;code&gt;this.push()&lt;/code&gt; 來推送一筆資料出去，有時確實還太囉唆，我們可以用 &lt;code&gt;callback()&lt;/code&gt; 一次搞定：&lt;/p&gt;

&lt;div&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const Transform = require(&amp;#39;stream&amp;#39;).Transform;

class MyTransform extends Transform {

    constructor(options) {
        super(options)
    }

    // 當每一筆資料進來時
    _transform(data, encoding, callback) {
    
        // 完成這筆資料的處理工作，同時將輸入進來的資料直接推送出去
        callback(null, data);
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2 id=&quot;toc_7&quot;&gt;疑難排解：怪 Bug？為什麼程式會提前結束、資料有漏？&lt;/h2&gt;

&lt;p&gt;很多人在玩弄 Stream 時，實作自己的 Transform Stream 時會發現，時常掉資料，或是資料還沒跑完，應用程式就提前結束，感覺相當不穩定。&lt;/p&gt;

&lt;p&gt;通常，這得提到 Stream 本身的機制才能夠很完善的說明原因，但簡單來說，Stream 本身會在資料滿載處理不過來時暫停運作（可參考 Node.js Stream 的 highWaterMark 設定），所以如果 Stream 後面沒有下一家 Stream 接手消化資料，這條資料流就會堵塞卡死。&lt;/p&gt;

&lt;p&gt;所以，通常發生這樣的情況，肯定是因為你沒有幫自己的 Transform Stream 設定下一家該往哪去，例如：&lt;/p&gt;

&lt;div&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;input.pipe(myStream); // 下面沒有了&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;尤其是，當 Stream 暫停運作後，事件引擎就沒有新的事件在跑。眾所皆知，當 JavaScript 的事件引擎沒有事件時，Node.js 整個應用程式自然就會結束。&lt;/p&gt;

&lt;p&gt;此外，Node.js Stream 的設計上，除了用 &lt;code&gt;.pipe()&lt;/code&gt; 來設定下一家是誰之外，還有另一種辦法可排泄資料，那就是使用 &lt;code&gt;.on()&lt;/code&gt; 監聽 &lt;code&gt;data&lt;/code&gt; 事件：&lt;/p&gt;

&lt;div&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;myStream.on(&amp;#39;data&amp;#39;, function(data) {
    // ...
});&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;但通常不建議這樣使用，尤其是當你接到資料時，需要做許多非同步（Asynchronous）的複雜工作時。當資料量大時，這樣監聽事件的做法並無法做資料節流，會導致你一瞬間觸發許多非同步工作，進而將你的系統資源耗盡。若在分秒算錢的雲端系統上，你會得到爆量的結果，不是伺服器負荷不過來，就是把你的錢燒盡。&lt;/p&gt;

&lt;p&gt;&lt;em&gt;註：如果你想實作一個沒有後面又可以運作的 Stream，你必須要參考 WritableStream 的實作方式，理論上做法大同小異。&lt;/em&gt;&lt;/p&gt;

&lt;h2 id=&quot;toc_8&quot;&gt;後記&lt;/h2&gt;

&lt;p&gt;之前有人抱怨，搞不太懂 Stream 在做什麼事，或到底怎麼實際開發使用。&lt;/p&gt;

&lt;p&gt;其實 Stream 是一個看似單純，但細節很多的機制，而且開發過程中會在觀察者與非觀察者切換，對許多初學者來說更是一大挑戰，更別說會碰上一些掉資料等看似奇怪的行為。所以，很能理解不少人為什麼看不太懂網路上各種說明 Stream 機制的文章，也看不到太多人使用 Stream 機制在實際的開發上。&lt;/p&gt;

&lt;p&gt;所以本文以較通俗簡單的範例和方法來說明 Stream 的使用，至於比較嚴格的定義或原理性的說明，就留給讀者自己去翻閱官方相關文件了。:-)&lt;/p&gt;

</description><link>https://fred-zone.blogspot.com/2017/09/nodejs-transform-stream.html</link><author>noreply@blogger.com (Fred Chien)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhwvLD46JMINtdPkctqsLNQuVxnPbI_ru8LNtp1pa7iyC988YZt49O0zmPy-NWg7zH2odPLJ8Mq9UpakDW7j1jdBuj-Qlv3Xp4Q71wUBlanpKRpFwliAtrlqN3QTK2xzAoYZodBk_ldky8/s72-c/TransformStream.png" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-9115396021874306868</guid><pubDate>Mon, 01 May 2017 09:29:00 +0000</pubDate><atom:updated>2017-05-01T17:42:16.720+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Android</category><category domain="http://www.blogger.com/atom/ns#">Java</category><category domain="http://www.blogger.com/atom/ns#">Linux硬體驅動</category><category domain="http://www.blogger.com/atom/ns#">USB</category><category domain="http://www.blogger.com/atom/ns#">程式心得筆記</category><category domain="http://www.blogger.com/atom/ns#">系統研發手札</category><title>救火奇兵之 Android USB Host API 反應遲緩</title><description>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEifog1skGzqMl_5EkZUybW4X582HdxD-O6Af_yTSL1p4WdTvuFV7aqE2PAr_YK4DIX4JhFHW5b_ZJe7i_vDnHKg9Xr0xgonld9chbrjwIvYU8MhCTp21suvUjj0TGpiqgJ8qcEsmssuStA/s1600/AndroidFire.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEifog1skGzqMl_5EkZUybW4X582HdxD-O6Af_yTSL1p4WdTvuFV7aqE2PAr_YK4DIX4JhFHW5b_ZJe7i_vDnHKg9Xr0xgonld9chbrjwIvYU8MhCTp21suvUjj0TGpiqgJ8qcEsmssuStA/s1600/AndroidFire.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;話說，Android 在某個版本後，開始提供了 USB Host API，這代表開發者可以不必再用 NDK 和硬梆梆的 C 語言去開發 USB 裝置的驅動程式，而可以完全用 Java 來開發。但是，現實往往沒有這麼美好。&lt;/p&gt;

&lt;p&gt;日前，就協助了一個案子，解決了一個 USB 裝置驅動程式的問題，起因就是客戶用了 Android USB Host API 去控制 USB 裝置，但發現 USB 裝置的回應一直不如預期，有時像是掉資料，有時像是沒反應。而同樣的控制邏輯，用純 C 開發的驅動程式配上 libusb 就完全正常，所以我們相信肯定不是控制邏輯上的問題。&lt;/p&gt;

&lt;p&gt;剛開始，大家都懷疑是 Java 本身的問題，懷疑是不是 JVM 執行驅動程式太慢，而造成接收 USB 裝置的資料時來不及。但我一直保持著懷疑，因為 USB 裝置回傳的資料並不多，如果 JVM 本身的效能連處理這幾 KB 的資料量都如此差，就實在是太可笑了，我無論如何不相信。&lt;/p&gt;

&lt;p&gt;還好最後還是解決了，雖然過程曲折。&lt;/p&gt;

&lt;h2 id=&quot;toc_1&quot;&gt;USB Request Block 的 16KB 限制&lt;/h2&gt;

&lt;p&gt;事實上，每次最多傳送 16KB 資料，是一個 bulk transfer 的 URB 限制，使用 Android USB Host API 就會直接遭遇到這個問題，所以不管用什麼方法，怎麼收資料，只要資料太大，你最多一次就只能收到 16KB。&lt;/p&gt;

&lt;h2 id=&quot;toc_2&quot;&gt;多次收資料所發現的延遲問題&lt;/h2&gt;

&lt;p&gt;當然，既然一次最多只能收 16KB，我們可以分多次向 USB 裝置要求收資料，但就會發現會莫名掉資料。從 USB 的分析器上來看，該有的命令都有，但就是有掉，後續的資料不管怎麼取都是 0。&lt;/p&gt;

&lt;p&gt;後續資料為 0，在這個案子的 USB 裝置設計上是可以理解的狀況，因為該 USB 裝置只會保留資料一小段時間，然後就會清空，所以若之後跟它要任何資訊，他都會回傳空的東西回來。這很明顯，就是我們要資料的過程時間，已經超過了該 USB 裝置正常的情況。&lt;/p&gt;

&lt;p&gt;而從收到的資料來看，有收到的資料，經驗證過後發現是斷斷續續的，中間有漏資料。經過測試，發現是每個命令之間的間距時間太長，因為該 USB 裝置會不斷復寫一段緩衝區，如果我們太慢去要資料，那段緩衝區就會被新的資料蓋掉，理所當然的，我們就會漏掉一些資料。&lt;/p&gt;

&lt;p&gt;經過各種測試紀錄，很明顯的，Android USB Host API 並沒有這麼聽我們的話，每當我們下命令或進行控制時，他並沒有馬上送到 USB 裝置，會有一些延遲，這才導致這樣的後果。&lt;/p&gt;

&lt;h2 id=&quot;toc_3&quot;&gt;硬幹 usbfs 的系統程式&lt;/h2&gt;

&lt;p&gt;如果在這件事上， Android USB Host API 的遲緩導致沒辦法滿足我們的需要，我們只好繞過去自幹了。&lt;/p&gt;

&lt;p&gt;但其實並不困難，不管怎麼說，Android 其實就是 Linux，底層肯定是透過 usbfs 去控制 USB 裝置，我們甚至可以不需要 libusb 和其他 framework，而直接去跟 usbfs 要資料。更何況我們只是要收資料而已，用 C 寫一小段程式去直接處理 URB 就可以解決，然後用 NDK 包裝成 JNI 即可。&lt;/p&gt;

&lt;p&gt;於是有下面的實作，一個與 libusb 內部實作原理相同，但更為簡化的版本：&lt;/p&gt;

&lt;div&gt;&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;linux/usbdevice_fs.h&amp;gt;
#include &amp;lt;sys/ioctl.h&amp;gt;

// We have 32 URBs
#define NUM_URBS       32
#define BUFFER_SIZE    16384

char *getURBs(int fd, int ep)
{
    struct usbdevfs_urb urbs[NUM_URBS];
    struct usbdevfs_bulktransfer bt;
    int len = 307200;
    int sizeCount = len;
    unsigned int urb_num = 0;
    
    // Allocate buffer for image
    char *buf = (char *)malloc(len * sizeof(char));
    
    /* Send out initial URBs */
    memset(urbs, 0, sizeof urbs);
    for (unsigned int i = 0; i &amp;lt; NUM_URBS; i++) {
        urbs[i].type = USBDEVFS_URB_TYPE_BULK;
        urbs[i].endpoint = ep;
        urbs[i].buffer = buf + (i * BUFFER_SIZE);
        urbs[i].buffer_length = (sizeCount &amp;lt; BUFFER_SIZE) ? sizeCount : BUFFER_SIZE;
        urbs[i].actual_length = (sizeCount &amp;lt; BUFFER_SIZE) ? sizeCount : BUFFER_SIZE;
    
        if (sizeCount &amp;gt; BUFFER_SIZE)
            sizeCount -= BUFFER_SIZE;
    
        if (ioctl(fd, USBDEVFS_SUBMITURB, &amp;amp;urbs[i]) &amp;lt; 0) {
            free(buf);
            return NULL;
        }
    }
    
    /* Wait for completions */
    while(urb_num &amp;lt; NUM_URBS) {
    
        struct usbdevfs_urb *urb;
    
        if (ioctl(fd, USBDEVFS_REAPURB, &amp;amp;urb) &amp;lt; 0) {
            free(buf);
            return NULL;
        }
    
        // Completed early
        if (urb-&amp;gt;actual_length &amp;lt; BUFFER_SIZE)
            break;
    
        urb_num++;
    }

    return buf;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2 id=&quot;toc_4&quot;&gt;後記&lt;/h2&gt;

&lt;p&gt;很久沒當救火隊長了，偶爾當當救火奇兵，也算是練練腦袋，還好腦袋還算靈活。&lt;/p&gt;
</description><link>https://fred-zone.blogspot.com/2017/05/android-usb-host-api.html</link><author>noreply@blogger.com (Fred Chien)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEifog1skGzqMl_5EkZUybW4X582HdxD-O6Af_yTSL1p4WdTvuFV7aqE2PAr_YK4DIX4JhFHW5b_ZJe7i_vDnHKg9Xr0xgonld9chbrjwIvYU8MhCTp21suvUjj0TGpiqgJ8qcEsmssuStA/s72-c/AndroidFire.png" height="72" width="72"/><thr:total>3</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-4339449978771897446</guid><pubDate>Wed, 12 Apr 2017 13:38:00 +0000</pubDate><atom:updated>2017-04-13T01:10:26.495+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">ECMAScript 7</category><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">技術新知</category><category domain="http://www.blogger.com/atom/ns#">程式心得筆記</category><title>JavaScript async/await 的奇淫技巧</title><description>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjB1F-dmJZrdwAT7eO34QTDgo35A_baTpa41khLdwgF4LmwOIEDEHggd44BBBjcai_QZz-S4S1j6P5ZDsUxoXK7aziUqzSnm29d4DlGoWaWy-u7qfAGwgwoNM1u9CXfqSAOiScKm19cUXU/s1600/JS-AsyncAwaitSkill.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjB1F-dmJZrdwAT7eO34QTDgo35A_baTpa41khLdwgF4LmwOIEDEHggd44BBBjcai_QZz-S4S1j6P5ZDsUxoXK7aziUqzSnm29d4DlGoWaWy-u7qfAGwgwoNM1u9CXfqSAOiScKm19cUXU/s1600/JS-AsyncAwaitSkill.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;h1 id=&quot;toc_0&quot;&gt;
JavaScript async/await 的奇淫技巧&lt;/h1&gt;
&lt;br /&gt;
話說，最新的 ECMAScript 已經引入了 async/await 語法，讓開發者可以更容易控制非同步的程式邏輯，換言之，我們可以減少許多 callback 的使用，讓 JavaScript 這種單線程、事件驅動的程式語言更易讀、好寫。&lt;br /&gt;
&lt;br /&gt;
關於 async/await 的基礎使用，有興趣的人可以參考舊文「&lt;a href=&quot;http://fred-zone.blogspot.tw/2016/07/javascript-async.html&quot;&gt;JavaScript 好用的 async 異步函數！&lt;/a&gt;」，而本文將探討更多實際使用上的小技巧。&lt;br /&gt;
&lt;br /&gt;
另外，瀏覽器不一定有支援 async/await，你可以在新版的 Node.js 上面測試本文的內容。&lt;br /&gt;
&lt;br /&gt;
&lt;h2 id=&quot;toc_1&quot;&gt;
呼叫 async 函數與一般的函數沒有差別&lt;/h2&gt;
&lt;br /&gt;
想像一下，async 函數就是一個在執行後會回傳 Promise 物件的「普通函數」，和一般常見的函數的使用差異，僅僅只是 async 函數在執行後「不是回傳函數執行結果」。這代表我們可以把 async 函數當作一般函數來呼叫使用，用法一模一樣。&lt;br /&gt;
&lt;br /&gt;
&lt;h2 id=&quot;toc_2&quot;&gt;
async/await 與 Promise 是可以共通的&lt;/h2&gt;
&lt;br /&gt;
非常有趣，async 函數與 Promise 其實能夠共通，這代表我們可以玩一些特別的組合技。所以，若要把 async/await 玩得通透，建議你盡量熟悉 Promise 的各種用法。&lt;br /&gt;
&lt;br /&gt;
&lt;h2 id=&quot;toc_3&quot;&gt;
實現 delay 函數&lt;/h2&gt;
&lt;br /&gt;
過去因為單線程和事件驅動的關係，JavaScript 不可能實現一個沒有嚴重副作用的 delay 函數，所以取而代之的是使用 &lt;code&gt;setTimeout()&lt;/code&gt; 加上 callback 來實現「一定時間後執行什麼工作」的需要。&lt;br /&gt;
&lt;br /&gt;
不過來到 async/await 的世界後，我們可以一行行描述程式邏輯，無論是不是同步（Synchronous）的程式碼，所以我們可以用 Promise 來包裝 &lt;code&gt;setTimeout()&lt;/code&gt;，以實現一個在 async 函數裡可以跑的 delay 函數：&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;// 實現一個等待函數
const delay = (interval) =&amp;gt; {
    return new Promise((resolve) =&amp;gt; {
        setTimeout(resolve, interval);
    });
};

const main = async () =&amp;gt; {
    console.log(&#39;Starting...&#39;);

    // 等待五秒
    await delay(5000);
    
    console.log(&#39;Done after five seconds&#39;)
};

main();&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;h2 id=&quot;toc_4&quot;&gt;
與 .map() 的組合技&lt;/h2&gt;
&lt;br /&gt;
JavaScript 陣列裡常使用的 &lt;code&gt;.map()&lt;/code&gt; 方法，但是 &lt;code&gt;.map()&lt;/code&gt; 方法內的處理函數是同步的（synchronous），也就是如果我們想在裡面跑非同步的邏輯，是沒辦法等到我們非同步的工作完成的。&lt;br /&gt;
&lt;br /&gt;
假設我們有一個陣列，然後使用 &lt;code&gt;.map()&lt;/code&gt; 方法操作它：&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const arr = [ 1, 2, 3, 4, 5 ];

let results = arr.map((num) =&amp;gt; {
    return num + 1;
});

// [ 2, 3, 4, 5, 6 ]
console.log(results);&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
通常，如果我們想引入非同步邏輯，我們可以這樣做，直接代換 &lt;code&gt;.map()&lt;/code&gt; 內的處理函數就可以：&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const asyncWorker = async (num) =&amp;gt; {
    // 非同步的工作，會做一段時間
};

let results = arr.map(async (num) =&amp;gt; {
    // 等待非同步工作完成
    await asyncWorker(num);
    
    return num + 1;
});&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
特別注意，引入 async 以後，results 會是一堆的 Promise 物件，而不是一個數值陣列。而且 &lt;code&gt;.map&lt;/code&gt; 並不會等 &lt;code&gt;asyncWorker()&lt;/code&gt; 這個非同步的工作做完才回傳，你可以想像這是一種「射後不理」的機制。&lt;br /&gt;
&lt;br /&gt;
&lt;h2 id=&quot;toc_5&quot;&gt;
等 .map() 裡的所有工作處理完&lt;/h2&gt;
&lt;br /&gt;
既然 async 函數被執行後，會回傳一個 Promise，這代表我們可以藉由 Promise 物件來得知工作什麼時候完成。所以我們可以這樣做：&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const asyncWorker = async (num) =&amp;gt; {
    // 非同步的工作，會做一段時間
};

let jobs = arr.map(async (num) =&amp;gt; {
    // 等待非同步工作完成
    await asyncWorker(num);
    
    return num + 1;
});

// 當所有工作完成後，顯示執行內容
Promise.all(jobs).then((results) =&amp;gt; {
    // [ 2, 3, 4, 5, 6 ]
    console.log(results);
});&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;h2 id=&quot;toc_6&quot;&gt;
用 await 取代 promise.then() 的使用方式&lt;/h2&gt;
&lt;br /&gt;
前面說到可以運用 &lt;code&gt;Promise.all()&lt;/code&gt; 方法來等待所有的非同步工作完成，但最終還是回到了 callback 的模式進行等待。而且，總是有懶惰鬼開發者會把這些程式碼寫成一行，非常不好讀：&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;Promise.all(arr.map(async (num) =&amp;gt; {
    // 等待非同步工作完成
    await asyncWorker(num);
    
    return num + 1;
})).then((results) =&amp;gt; {
    // [ 2, 3, 4, 5, 6 ]
    console.log(results);
});&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
既然已經有 async/await 的環境，很多人會盡量讓自己的 &lt;code&gt;context&lt;/code&gt; 處於 async 函數的環境之下，這時我們就可以用 await 來取代 Promise 的 &lt;code&gt;.then()&lt;/code&gt; 方法：&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const main = async () =&amp;gt; {

    // 改用 await 等待 Promise 內的工作全部完成
    let results = await Promise.all(arr.map(async (num) =&amp;gt; {
        // 等待非同步工作完成
        await asyncWorker(num);
        
        return num + 1;
    }));
    
    // [ 2, 3, 4, 5, 6 ]
    console.log(results);
};

main();&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;h2 id=&quot;toc_7&quot;&gt;
與 .reduce() 的組合技&lt;/h2&gt;
&lt;br /&gt;
如同 &lt;code&gt;.map()&lt;/code&gt; 方法，&lt;code&gt;.reduce()&lt;/code&gt; 是另一個常見的陣列處理方法之一，它也同樣不是一個非同步的方法。若引入 async/await，可以讓 &lt;code&gt;.reduce()&lt;/code&gt; 擴展為一個依序處理非同步工作的工具，讓非同步工作一個處理完後下一個才接著做。&lt;br /&gt;
&lt;br /&gt;
一個原始的 &lt;code&gt;.reduce()&lt;/code&gt; 使用大概如下：&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const arr = [ 1, 2, 3, 4, 5 ];

// 將陣列所有數值一一加總
let result = arr.reduce((accumulation, num) =&amp;gt; {
    return accumulation + num;
}, 0);

// 15
console.log(result);&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
若引入 async/await，會變成這樣的形式：&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const arr = [ 1, 2, 3, 4, 5 ];

const main = async () =&amp;gt; {

    // 將陣列所有數值一一加總
    let result = await arr.reduce(async (prev, num) =&amp;gt; {
    
        // 等待前一個工作完成，並得到前個工作的結果
        let accumulation = await prev;
    
        return accumulation + num;
    }, Promise.resolve(0));
    
    // 15
    console.log(result);
};

main();&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
這時裡面可以跑各式各樣非同步工作，如前面所提到的 delay 函數：&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const arr = [ 1, 2, 3, 4, 5 ];

const main = async () =&amp;gt; {

    // 將陣列所有數值一一加總
    let result = await arr.reduce(async (prev, num) =&amp;gt; {
    
        // 等待前一個工作完成，並得到前個工作的結果
        let accumulation = await prev;
        
        // 非同步工作：等一秒
        await delay(1000);
        
        return accumulation + num;
    }, Promise.resolve(0));
    
    // 15
    console.log(result);
};

main();&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;h2 id=&quot;toc_8&quot;&gt;
後記&lt;/h2&gt;
&lt;br /&gt;
懂得使用 async/await 和 Promise 之後，其實有很多的玩法，邏輯的描述也更為多元和簡單，強烈建議一定要熟悉他。:-)</description><link>https://fred-zone.blogspot.com/2017/04/javascript-asyncawait.html</link><author>noreply@blogger.com (Fred Chien)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjB1F-dmJZrdwAT7eO34QTDgo35A_baTpa41khLdwgF4LmwOIEDEHggd44BBBjcai_QZz-S4S1j6P5ZDsUxoXK7aziUqzSnm29d4DlGoWaWy-u7qfAGwgwoNM1u9CXfqSAOiScKm19cUXU/s72-c/JS-AsyncAwaitSkill.png" height="72" width="72"/><thr:total>2</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-6071644767624217112</guid><pubDate>Sat, 08 Apr 2017 07:22:00 +0000</pubDate><atom:updated>2017-04-08T15:24:40.793+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">ECMAScript 6</category><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">技術新知</category><category domain="http://www.blogger.com/atom/ns#">程式心得筆記</category><title>自幹 JavaScript 的 Tail Call Optimization</title><description>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9NUEa9Nli3IONgmyWc0THE8cHsZm_MtmXsTEqxs2B4RBB5WRUzJ-RYdZN9rQOpSX8veEVch_75xr1gL-PZphjwYJnwJJuIp3-Qpxe7l8zQgf-EahhLJqpdSytqTwdKy7sOQmjTtWysss/s1600/JS-TailCallOptimization.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9NUEa9Nli3IONgmyWc0THE8cHsZm_MtmXsTEqxs2B4RBB5WRUzJ-RYdZN9rQOpSX8veEVch_75xr1gL-PZphjwYJnwJJuIp3-Qpxe7l8zQgf-EahhLJqpdSytqTwdKy7sOQmjTtWysss/s1600/JS-TailCallOptimization.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
ECMAScript 6 開始，規範中出現了一項被稱為「尾呼叫優化（Tail Call Optimization, TCO）」的優化技術，這讓開發者可以在函數的執行過程中，減少 Stack Frame 的數量，進而提升效能。TCO 尤其是在遞迴這種不停呼叫自己或新函數的工作上，能得到最大的優化效益，能提升遞迴的執行效能如同迴圈一樣。&lt;br /&gt;
&lt;br /&gt;
只不過很可惜的是，截至本文發稿前，大多數瀏覽器及 JavaScript 引擎尚未支援這項技術。但我們還是可以自幹並模擬一個 TCO 的行為，雖然比起語言本身、編譯器（Compiler）及虛擬機（VM）層面的實現，效果差了些，但仍然可以減少 Stack Frame 的數量避免達到 Stack Frame 的數量上限。&lt;br /&gt;
&lt;br /&gt;
&lt;h2 id=&quot;toc_1&quot;&gt;什麼時候會啟用尾呼叫優化機制？&lt;/h2&gt;&lt;br /&gt;
如果 JavaScript 引擎有支援，通常一個函數執行到最後一行 &lt;code&gt;return&lt;/code&gt; 時，是回傳另一個函數的執行結果，就會啟用 TCO 機制，如：&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const f = () =&amp;gt; {
    return 999;
};

const g = () =&amp;gt; {
    // 執行並直接回傳 f 函數的執行結果：會啟用尾呼叫優化機制
    return f();
};

g();&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
但要注意的是，回傳的「必定為函數的直接回傳值」，所以下面這些寫法不會啟用 TCO 機制：&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;// 不會啟用 TCO 機制的設計
const g = () =&amp;gt; {
    return f() + 1;
};

// 不會啟用 TCO 機制的設計
const g = () =&amp;gt; {
    let ret = f();

    return ret;
};&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
&lt;h2 id=&quot;toc_2&quot;&gt;創造一個跑不完的函數&lt;/h2&gt;&lt;br /&gt;
首先我們先創造一個肯定跑不完的遞迴，然後改善它：&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const func = (x) =&amp;gt; {

    // 讓他跑 10000000 次
    if (x === 10000000)
        return x;

    return func(x + 1);
};

let ret = func(0);&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
理論上，如果你直接執行上述程式碼，會得到 stack size 超過上限的錯誤訊息：&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;&lt;code class=&quot;language-none&quot;&gt;RangeError: Maximum call stack size exceeded&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
&lt;h2 id=&quot;toc_3&quot;&gt;簡單模擬實現自己的 TCO 效果&lt;/h2&gt;&lt;br /&gt;
簡單來說，尾呼叫優化對開發者最主要的效果，就是避免每次呼叫函數時，就產生一個新的 Stack Frame，所以這是我們實現的主要訴求。而我們可以實現的作法，是讓函數的遞迴呼叫轉換成迴圈形式執行，避免不斷增加 Stack Frame，使遞迴可以無窮的跑下去。&lt;br /&gt;
&lt;br /&gt;
我們可以創造一個閉包，來包裝我們的函數：&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const tco = (fn) =&amp;gt; {

    return (...args) =&amp;gt; {

        let f = fn.bind(this, ...args);

        // 每次執行函數後，若得到的回傳值是函數物件，則繼續執行下去
        while(f instanceof Function) {
            f = f();
        }

        // 沒有需要繼續執行的函數，回傳結果
        return f;
    }
};&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
然後，我們用此閉包把待優化的函數包裝起來，並做點修改：&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const func = (x) =&amp;gt; {

    // 讓他跑 10000000 次
    if (x === 10000000)
        return x;

    // 不直接執行函數，改為綁定參數後產生並回傳一個函數物件
    return func.bind(this, x + 1);
};

// 包裝我們的遞迴函數
const improvedFunc = tco(func);

// 執行方法和舊函數一樣
let ret = improvedFunc(0);&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
執行優化後的函數，就不會再出現 stack size 的錯誤了。&lt;br /&gt;
&lt;br /&gt;
&lt;h2 id=&quot;toc_4&quot;&gt;自己自幹的限制&lt;/h2&gt;&lt;br /&gt;
需要一提的是，在我們自己實現的版本中，我們無法做到合併所有的 Stack，也無法減少函數的來回跳轉數量，這些是屬於虛擬機和語言層面才能做到的設計。&lt;br /&gt;
&lt;br /&gt;
&lt;h2 id=&quot;toc_5&quot;&gt;使用遞迴時要注意事件引擎鎖死的問題&lt;/h2&gt;&lt;br /&gt;
要記得，雖然 TCO 可以解決 stack size 上限的問題，但 JavaScript 仍然依賴著事件引擎，而密集運算會造成事件引擎鎖死，所以在一般的應用中，我們應該避免運用太深的遞迴，除非你確定你的應用程式沒有其他需要事件機制的地方，或是真的要拿 JavaScript 做密集運算的工作。&lt;br /&gt;
&lt;br /&gt;
&lt;h2 id=&quot;toc_6&quot;&gt;後記&lt;/h2&gt;&lt;br /&gt;
一如程式語言的發展趨勢，所有現代化的程式語言都向 Functional Programming 靠攏。考量到為了讓函數可以無窮的遞迴執行下去，尾呼叫優化（Tail Call Optimization, TCO）就是一個重要的機制。</description><link>https://fred-zone.blogspot.com/2017/04/javascript-tail-call-optimization.html</link><author>noreply@blogger.com (Fred Chien)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9NUEa9Nli3IONgmyWc0THE8cHsZm_MtmXsTEqxs2B4RBB5WRUzJ-RYdZN9rQOpSX8veEVch_75xr1gL-PZphjwYJnwJJuIp3-Qpxe7l8zQgf-EahhLJqpdSytqTwdKy7sOQmjTtWysss/s72-c/JS-TailCallOptimization.png" height="72" width="72"/><thr:total>1</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-5587683538871658425</guid><pubDate>Tue, 04 Apr 2017 14:21:00 +0000</pubDate><atom:updated>2017-04-05T00:19:11.955+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Functional Programming</category><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">技術新知</category><category domain="http://www.blogger.com/atom/ns#">程式心得筆記</category><title>實現 JavaScript 的 Memoization</title><description>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiNsDm5Nu14_DXJm6HrTrOrH0v5dHsVjBLQ5KKPSr44Z4lzAIEI41nwFtR9URIrBQfF7qafZ4pcPyeiGKLCCwbgnqt0XUjiuUEEnAIQykFINS9AcmS0RgOdX9-5kHK92ZJXIPMPNQHiRkk/s1600/JS-memorization.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiNsDm5Nu14_DXJm6HrTrOrH0v5dHsVjBLQ5KKPSr44Z4lzAIEI41nwFtR9URIrBQfF7qafZ4pcPyeiGKLCCwbgnqt0XUjiuUEEnAIQykFINS9AcmS0RgOdX9-5kHK92ZJXIPMPNQHiRkk/s1600/JS-memorization.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;函數式程式設計（Functional Programming）是近年來越來越被軟體開發者常提及的話題，許多人討論它時，不外乎說其就是在程式設計中引入了數學方法，彷彿有神奇又高深的理論加持一般。事實上，對於一般開發者而言，函數式程式設計比較通俗且直接的好處，就是讓開發者可以在「函數」的層面和維度，進行邏輯或是效能上的優化。所以說，比起命令化的執行程式、管理物件，怎麼去設計和管理函數這件事，就是函數式程式設計所關心的重點。&lt;br /&gt;
&lt;br /&gt;
JavaScript 這語言在設計上，天生就支援 first-class function，這代表函數在 JavaScript 是一種資料型態，可以被當成普通物件傳遞、處理，這讓開發者在使用 JavaScript 時可以時不時引入 Functional Programming 的技巧和概念。&lt;br /&gt;
&lt;br /&gt;
本文將介紹 Functional Programming 中大量被使用的 Memoization 機制，然後我們如何在 JavaScript 中引入並實地使用它，無論你會不會 Functional Programming，這都是一個可以常用於日常開發中的優化技巧。&lt;br /&gt;
&lt;br /&gt;
&lt;h2 id=&quot;toc_1&quot;&gt;Memoization 是什麼？&lt;/h2&gt;&lt;br /&gt;
用一般程式開發的說法就是快取（Cache）機制，只不過 Memoization 是針對「函數」進行快取。快取的好處在於我們只要執行過一次工作後，之後在執行相同工作前，就能提前知道執行結果為何，所以我們可以不用「真正的」去執行工作，而直接取用執行結果就好，可大量提升程式執行的效能。&lt;br /&gt;
&lt;br /&gt;
同樣快取概念套用在函數上，若我們給予特定「輸入（Input）」到一個函數中，而該函數會回傳一個特定的「輸出（Output）」，理論上函數執行一次後，下次再使用這個函數時，只要「輸入」和過去一樣，我們就能提前知道結果。&lt;br /&gt;
&lt;br /&gt;
而這樣對函數進行的快取機制，就是所謂的 Memoization。&lt;br /&gt;
&lt;br /&gt;
&lt;h2 id=&quot;toc_2&quot;&gt;只能使用在純函數&lt;/h2&gt;&lt;br /&gt;
你可能會聽一些人說，只有「純函數」才能引入快取機制，然後開始討論數學上所謂函數的定義，然後你就聽到昏了，後面在講什麼你就都聽不進去了。&lt;br /&gt;
&lt;br /&gt;
但如果撇除函數的數學定義，若白話來說，能被快取的東西，就是能被預測的東西，這代表函數的執行結果也要能被預測，也就是一樣的輸入值，就會有一樣的輸出結果。&lt;br /&gt;
&lt;br /&gt;
所以，如果一個函數每次執行，代入的輸入值一樣，但回傳結果卻是可能不一樣，這就不是一個純函數，像是取亂數的函數就屬此類，我們無法對其引入快取機制，而且也沒有任何引入的意義。&lt;br /&gt;
&lt;br /&gt;
&lt;h2 id=&quot;toc_3&quot;&gt;從一個簡單的函數開始&lt;/h2&gt;&lt;br /&gt;
要實現 Memoization 之前，我們得先創造一個有用的函數，然後才能討論怎麼快取它。現在假設我們有一個函數，專門用來查詢一個資料陣列，我們會這樣設計：&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const getData = (records, id) =&amp;gt; {

    // 找到指定的 element 並回傳
    return records.find((data) =&amp;gt; {
    
        // 找到指定的 ID 就回傳 true
        return (data.id === id);
    });
};

// 想像這是一個有上萬筆資料的陣列
const list = [ ... ];

// 取得 list 裡 id 為 helloId 的資料
let data = getData(list, &#39;helloId&#39;);&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
&lt;h2 id=&quot;toc_4&quot;&gt;創造一個函數，專門查詢特定陣列&lt;/h2&gt;&lt;br /&gt;
如果我們想要創造一個函數，專門查詢 list，則可以運用 JavaScript 中的 &lt;code&gt;.bind()&lt;/code&gt; ，來綁定 list 並創造出一個新的函數來使用：&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;// 以舊的 getData 為基礎創造一個新的函數，綁定 caller 和第一個代入參數
const newGetData = getData.bind(this, list);

// 執行新的函數時只需要代入尚未被綁定的參數即可
let data = newGetData(&#39;helloId&#39;);&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
&lt;em&gt;註：有興趣者可以延伸搜尋 Functional Programming 的「柯里化（Currying）」相關資料。&lt;/em&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;h2 id=&quot;toc_5&quot;&gt;設計函數的快取機制&lt;/h2&gt;&lt;br /&gt;
我們可以自己設計一個閉包（Closure）來實現函數的快取機制，簡單來說就是利用 JavaScript 的 Object 來建立一個 key-value pair 的快取。而 key 就使用函數的「輸入（Input）」，value 則是函數的執行「輸出（Output）」。&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const memoize = (fn) =&amp;gt; {
    // 將快取資料存放在閉包裡的記憶體
    const cache = {};

    return (arg) =&amp;gt; {
        // 先檢查是否有快取，如果沒有就執行原始函數並快取其結果
        if (cache[arg] === undefined)
            cache[arg] = fn(arg);

        // 回傳執行結果
        return cache[arg];
    };
};

// 對函數套用 Memoization 機制，會得到一個新的函數
const improvedGetData = memoize(newGetData);

// 這個新的函數使用方法和舊的一樣
let data1 = improvedGetData(&#39;helloId&#39;);

// 第二次執行同樣函數，並帶同樣參數時，就是直接從快取記憶體中取得結果
let data2 = improvedGetData(&#39;helloId&#39;);&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
被我們設計出來的 &lt;code&gt;memoize()&lt;/code&gt; 是通用的方法，可以應用在其他函數中，只要其他函數也有這樣的需求，又符合純函數的條件，是可以直接以 &lt;code&gt;memoize()&lt;/code&gt; 包裝，受惠於快取機制。&lt;br /&gt;
&lt;br /&gt;
&lt;h2 id=&quot;toc_6&quot;&gt;你應該要注意的問題&lt;/h2&gt;&lt;br /&gt;
Memoization 這樣的快取機制，雖對執行效能上有所有好處，但最直接的問題就是會佔用記憶體，如果我們的程式處理的資料量很大，就可能會佔用非常大量的記憶體而不會釋放。&lt;br /&gt;
&lt;br /&gt;
所以在某些函式程式語言中， Memoization 甚至支援快取資料數量的限制，如果你有需求，可以自己試著增加這樣的設計。&lt;br /&gt;
&lt;br /&gt;
&lt;h2 id=&quot;toc_7&quot;&gt;效能評估？&lt;/h2&gt;&lt;br /&gt;
至於增加快取後的效能如何呢？這邊準備了一個簡單的測試「&lt;a href=&quot;https://jsperf.com/memoization-testing/1&quot;&gt;Memoization Testing&lt;/a&gt;」，分別為原始函數執行的效能，以及引入 Memoization 機制後的效能，你可以試著跑跑看。&lt;br /&gt;
&lt;br /&gt;
第一次的執行上效能差不多，但若「同個函數+同樣輸入值」執行兩次後，原始函數的執行效能就會開始明顯與有快取機制的函數有所差距。&lt;br /&gt;
&lt;br /&gt;
&lt;h2 id=&quot;toc_8&quot;&gt;後記&lt;/h2&gt;&lt;br /&gt;
物件導向大家都很會玩了，可以開始玩玩不同維度的優化和技巧，其實相當有趣。:-P</description><link>https://fred-zone.blogspot.com/2017/04/javascript-memorization.html</link><author>noreply@blogger.com (Fred Chien)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiNsDm5Nu14_DXJm6HrTrOrH0v5dHsVjBLQ5KKPSr44Z4lzAIEI41nwFtR9URIrBQfF7qafZ4pcPyeiGKLCCwbgnqt0XUjiuUEEnAIQykFINS9AcmS0RgOdX9-5kHK92ZJXIPMPNQHiRkk/s72-c/JS-memorization.png" height="72" width="72"/><thr:total>1</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-558368138719641906</guid><pubDate>Sat, 25 Feb 2017 19:33:00 +0000</pubDate><atom:updated>2017-02-26T15:21:51.386+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">Koa</category><category domain="http://www.blogger.com/atom/ns#">Web Framework</category><category domain="http://www.blogger.com/atom/ns#">Web 相關技術</category><category domain="http://www.blogger.com/atom/ns#">程式心得筆記</category><category domain="http://www.blogger.com/atom/ns#">自由軟體</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><title>Koa 2 起手式！</title><description>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhopE5DgXVNpEm-TGej4cKycrd3jboVFYnjf614YNDQe-_nINaoDFLgUO8c0zoOwlaT1TLEX6ocF8V3Rnxwp56LSnqJsrOYuv4vTrMzmQdefECUe9fiVE3LTxJFdr332lN_O3RjHBzL24Q/s1600/koa2.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhopE5DgXVNpEm-TGej4cKycrd3jboVFYnjf614YNDQe-_nINaoDFLgUO8c0zoOwlaT1TLEX6ocF8V3Rnxwp56LSnqJsrOYuv4vTrMzmQdefECUe9fiVE3LTxJFdr332lN_O3RjHBzL24Q/s1600/koa2.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
在 Node.js 的世界裡，說到今天最潮的 Web Framework，肯定就是 Koa！其用了最新的 JavaScript 語法和特性，來改善 Web Framework 的設計。只不過，Koa 雖然相對於其他舊的 Web Framework 來說有相當多的進步，但很多人卻相當討厭 Koa 的 Generator 設計，尤其是那些「*」符號，那不知所謂的 yield 也讓很多人不舒服。所以至今仍然有許多人在使用 express 來當作自己的 Web Framework，寧可繼續使用那老派的 callback 設計，而不肯嘗試 Koa。&lt;br /&gt;
&lt;br /&gt;
隨著 ECMAScript 標準的進步，Koa 才剛被開發出來沒多久，原本的開發團隊就立即著手打造 Koa 2 ，開始更進一步採用更新的 JavaScript 特性，以 async/await 語法重新打造了全新且更簡潔的框架。可惜的是，由於 async/await 語法一直遲遲沒有被 JavaScript 引擎原生支援，因此總需要靠 babel 編譯打包程式碼後，才能正常跑在 Node.js 之上。這讓 Koa 2 一直無限期處於非穩定版，讓原開發者從開發的一開始，就打算等到 V8 和 Node.js 開始原生支援 async/await 後，才會被以穩定版（stable）的姿態釋出。&lt;br /&gt;
&lt;br /&gt;
所以，即使 Koa 2 到了今天已經相當穩定，也開始有不少人使用在自己的線上服務，卻一直無限期處於非穩定版的狀態。&lt;br /&gt;
&lt;br /&gt;
另外，由於 Koa 2 大量使用 Async/Await，如果你還對 Async/Await 的使用還不熟悉，建議在閱讀本文之前，先閱讀舊文「&lt;a href=&quot;http://fred-zone.blogspot.tw/2016/07/javascript-async.html&quot;&gt;JavaScript 好用的 async 異步函數！&lt;/a&gt;」來學習如何使用。&lt;br /&gt;
&lt;br /&gt;
&lt;h2 id=&quot;toc_1&quot;&gt;
學習 Koa 的好時機來囉&lt;/h2&gt;
&lt;br /&gt;
總算，日前 Node.js v7.6.0 釋出後已經正式宣布原生支援了 async/await 語法，而且不需要額外的參數選項。伴隨著這個消息，Koa 2.0 也隨即正式釋出了！&lt;br /&gt;
&lt;br /&gt;
Node.js 內建支援 ES7 的 async/await 真的是非常棒的消息！過去我們使用 async/await，都還需要 babel 的協助才能正常跑在舊版的 Node.js，不但開發上相當麻煩，非原生的各種 ES7 特性也浪費不少額外的記憶體和效能，這樣的問題在斤斤計較效能的 Server 環境下，更是讓人頭痛。&lt;br /&gt;
&lt;br /&gt;
如今 Node.js 的原生支援，讓我們已經不需要再擔心種種問題，讓我們可以得到簡潔的程式碼和兼顧效能，現在就是準備轉換到 Koa 2 的最好時機！:-)&lt;br /&gt;
&lt;br /&gt;
&lt;h2 id=&quot;toc_2&quot;&gt;
安裝 Koa 2&lt;/h2&gt;
&lt;br /&gt;
現在，我們終於可以直接使用 NPM 命令安裝 Koa 2：&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npm install koa&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;h2 id=&quot;toc_3&quot;&gt;
開發第一個應用程式&lt;/h2&gt;
&lt;br /&gt;
如果你有開發過 Koa 或 Express 的網站應用程式，Koa 2 的寫法其實相當雷同，差別是 Express 使用的是普通函數當 callback、Koa 是使用 Generator Function，而 Koa 2 是使用 Async Function。&lt;br /&gt;
&lt;br /&gt;
一個簡單的範例如下：&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const Koa = require(&#39;koa&#39;);

const app = new Koa();

app.use(async function(ctx) {
    ctx.body = &#39;Hello World&#39;;
});

app.listen(3001);&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
當 ctx.body 被設定一個內容後，連線就會回傳該內容回瀏覽器。在這範例中，無論發什麼要求給 Server ，都會得到「Hello World」的回傳。&lt;br /&gt;
&lt;br /&gt;
註：如果你使用過 Koa，會發現 Koa 2 已經不再使用 this 關鍵字，而是改成一個 context 物件代入到函數之中。&lt;br /&gt;
&lt;br /&gt;
&lt;h2 id=&quot;toc_4&quot;&gt;
使用異步函數打造的 Middleware&lt;/h2&gt;
&lt;br /&gt;
koa.use() 將用來載入 Middleware，所有連線工作都會經過 Middleware 處理。這也是為什麼，前一個例子裡，我們使用 koa.use() 設定了一個處理函數後，所有連線透會通過該函數進行處理並回傳同樣的值。&lt;br /&gt;
&lt;br /&gt;
要注意的是，該函數是一個「異步函數（Async Function）」，要用到 async 關鍵字來宣告:&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;app.use(async function() {
    // ...
});&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
註：如果你有過 express 開發經驗，對於 koa.use() 會相當熟悉，Koa 同樣支援了 Middleware 的架構，你可以將過去的程式輕易移植到這新的框架上。&lt;br /&gt;
&lt;br /&gt;
&lt;h2 id=&quot;toc_5&quot;&gt;
自訂 Router 和路徑管理&lt;/h2&gt;
&lt;br /&gt;
之前的範例直接使用 koa.use()，會將所有的連線都導入同一個處理函數，輸出同一個結果。若我們想要自訂不同的路徑，讓不同路徑用不同的處理函數，將需要額外安裝「koa-router」模組：&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npm install koa-router&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
然後可以用 koa-router 來管理對外的連線路徑：&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const Koa = require(&#39;koa&#39;);
const Router = require(&#39;koa-router&#39;);

const app = new Koa();
const router = Router();

// 設定根路徑的處理函數
router.get(&#39;/&#39;, async function(ctx) {
    ctx.body = &#39;Hello World&#39;;
});

app.use(router.routes());

app.listen(3001);&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;h2 id=&quot;toc_6&quot;&gt;
接收 QueryString 資料&lt;/h2&gt;
&lt;br /&gt;
QueryString 可說是歷史悠久且非常常見的傳值方法，藉由一個網址後面加上一個「?」字元後，就可以使用鍵值（Key/Value）來進行資料傳遞，並用「&amp;amp;」區隔多組資料。一個簡單的實際應用如下：&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;
&lt;pre&gt;&lt;code class=&quot;language-none&quot;&gt;http://my_server/send?name=fred&amp;amp;msg=Hello&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
取得資料的方法如下：&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;ctx.query.name
ctx.query.msg&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;h2 id=&quot;toc_7&quot;&gt;
接收 body 資料&lt;/h2&gt;
&lt;br /&gt;
當我們使用「POST」或「PUT」方法，我們就可以利用 body 傳送一些資料到伺服器，像是網頁表單時常使用這樣的傳值方法。若想要取得 body 的資料，必須先安裝「koa-bodyparser」模組。&lt;br /&gt;
&lt;br /&gt;
截至本文釋出為止，該模組還沒有隨著 Koa 2 推出正式支援的版本，所以預設下載回來的版本還是支援舊的 Koa，所以必須指定版本號「next」：&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;
&lt;pre&gt;&lt;code class=&quot;language-none&quot;&gt;npm install koa-bodyparser@next&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
當然，你也可以用「koa-convert」模組將舊的 Koa Middleare 直接轉換給 Koa 2 使用：&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;
&lt;pre&gt;&lt;code class=&quot;language-none&quot;&gt;npm install koa-convert&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
使用 koa.use() 載入 koa-bodyparser，koa 就會自動在處理連線時使用它解析 body：&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;var bodyParser = require(&#39;koa-bodyparser&#39;);

// 若想使用 koa-convert 進行轉換，要先載入模組：
// const convert = require(&#39;koa-convert&#39;);
// 再以 convert(bodyParser()) 包裝
app.use(bodyParser());&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
然後可以在路徑處理函數中，正常取得 body 內的資訊：&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;ctx.request.body.name
ctx.request.body.msg&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;h2 id=&quot;toc_8&quot;&gt;
錯誤處理&lt;/h2&gt;
&lt;br /&gt;
在 Koa 2 中，可以透過 ctx.throw() 進行錯誤處理，並回傳狀態值和內容給客戶端，他會中斷目前的處理函數，實際使用情境如下：&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;
router.get(&#39;/api/v1/user&#39;, async function(ctx) {

    // 檢查 Token，若有問題回傳 400 HTTP StatusCode
    if (ctx.query.token == &#39;123&#39;)
        ctx.throw(400);

    // 若已經拋出 400 的狀態，接下來的程式不會被執行
    ctx.body = &#39;Hello World&#39;;
});
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;h2 id=&quot;toc_9&quot;&gt;
加入多個 Middleware&lt;/h2&gt;
&lt;br /&gt;
所有的連線要求可以透過一系列、不只一個 Middleware 來進行處理，我們可以利用多次 koa.use() 來加入多個 Middleware，多個 Middleware 可以用來做到很多功能，例如記錄和顯示每個連線的狀態。&lt;br /&gt;
&lt;br /&gt;
加入多個 Middleware 的範例如下：&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const Koa = require(&#39;koa&#39;);

const app = new Koa();

app.use(async function(ctx, next) {
    // 略過這個 Middleware，讓下一個 Middleware 來接著處理
    await next();
});

app.use(async function(ctx) {
    ctx.body = &#39;Hello World&#39;;
});

app.listen(3001);
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;h2 id=&quot;toc_10&quot;&gt;
加入 Logger 來記錄連線要求&lt;/h2&gt;
&lt;br /&gt;
koa-logger 是一個能顯示連線要求狀態的第三方 Middleware，可以先透過 NPM 安裝它：&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npm install koa-logger&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
然後可以直接以 app.use() 引用：&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;const Koa = require(&#39;koa&#39;);
const logger = require(&#39;koa-logger&#39;);

const app = new Koa();

// 加入 logger 在其他的 Middleware 之前
app.use(logger());

app.use(async function(ctx) {
    ctx.body = &#39;Hello World&#39;;
});

app.listen(3001);&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
然後，你的應用程式就會輸出漂亮的連線要求訊息：&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;
&lt;pre&gt;&lt;code class=&quot;language-none&quot;&gt;  &amp;lt;-- GET /api/v1/hello
  --&amp;gt; GET /api/v1/hello 200 8,257ms 2b&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;h2 id=&quot;toc_11&quot;&gt;
在 Koa 2 裡使用 Mongoose&lt;/h2&gt;
&lt;br /&gt;
異步函數使用 await 關鍵字對 Promise、Thunk 進行等待，使開發者不再需要用到大量的 callback function，讓程式碼比較不會「橫著長大」。所以，只要 Mongoose 可以在做任何工作時，回傳一個 Promise 物件，我們就可以在 Koa 2 中使用 await 等它完成。&lt;br /&gt;
&lt;br /&gt;
還好，Mongoose 有支援這個功能，但我們得使用 .exec() 這個方法來取得 Promise：&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;router.get(&#39;/api/v1/user&#39;, async function(ctx) {

    // 利用 exec() 取得 Promise，然後以 await 等待完成
    ctx.body = await Users.find({}).exec();
});&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;h2 id=&quot;toc_12&quot;&gt;
函數宣告的習慣改變&lt;/h2&gt;
&lt;br /&gt;
在本文的範例中，仍然還是使用「function()」這樣的函數宣告方式，但很多開發者為了減少程式碼，大量改用 Arrow Function（箭頭函數）來宣告函數，所以你會大量看到這樣的情況：&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;router.get(&#39;/api/v1/user&#39;, async (ctx) =&amp;gt; {
    // ...
});&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
因為多數情況下，改用 Arrow Function 來宣告是沒有問題的，所以很多懶惰的開發者都會這樣使用。但建議你，如果有空時，還是要了解這種函數與普通函數宣告的差別。&lt;br /&gt;
&lt;br /&gt;
&lt;h2 id=&quot;toc_13&quot;&gt;
後記&lt;/h2&gt;
&lt;br /&gt;
這篇文章其實卡很久了，一直遲疑著要不要在正式穩定版之前公開，剛好趁著這幾天 Node.js v7.6.0 和 Koa 2 正式版釋出，所有的顧慮就沒有啦。:-D</description><link>https://fred-zone.blogspot.com/2017/02/koa-2.html</link><author>noreply@blogger.com (Fred Chien)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhopE5DgXVNpEm-TGej4cKycrd3jboVFYnjf614YNDQe-_nINaoDFLgUO8c0zoOwlaT1TLEX6ocF8V3Rnxwp56LSnqJsrOYuv4vTrMzmQdefECUe9fiVE3LTxJFdr332lN_O3RjHBzL24Q/s72-c/koa2.png" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-5839843152928327943</guid><pubDate>Fri, 03 Feb 2017 06:36:00 +0000</pubDate><atom:updated>2017-02-03T14:36:06.181+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">MessageQueue</category><category domain="http://www.blogger.com/atom/ns#">Node.js</category><category domain="http://www.blogger.com/atom/ns#">技術新知</category><category domain="http://www.blogger.com/atom/ns#">物聯網</category><category domain="http://www.blogger.com/atom/ns#">程式心得筆記</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><title>Node.js 也可以使用 Protocol Buffers！</title><description>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgu8N2eEIW2RGPiTHaANo0q-hBVisqcqCwW3is2x0twJwgUUsDI_sJpN-lYvEM3B-OoUXep3asG5kDgzUIQPw_IDwftbLRXc2OiSH0siyr252yg9CumTxGAMjC1nrvep2FnEklOmSYxJqQ/s1600/protobuf.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgu8N2eEIW2RGPiTHaANo0q-hBVisqcqCwW3is2x0twJwgUUsDI_sJpN-lYvEM3B-OoUXep3asG5kDgzUIQPw_IDwftbLRXc2OiSH0siyr252yg9CumTxGAMjC1nrvep2FnEklOmSYxJqQ/s1600/protobuf.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
「&lt;a href=&quot;https://developers.google.com/protocol-buffers/docs/overview&quot;&gt;Protocol Buffers (protobuf)&lt;/a&gt;」是一套 Google 所提出的結構化資料的包裝技術，讓資料便於網路傳輸或交換，如同常見的 JSON 和 XML 等技術一般。但相對於其他常見技術，protobuf 設計上更易於用來包裝二進位資料，應用在串流（Streaming）技術上，在資料包裝上也更為節省空間，在包裝或解析上也更有效率。&lt;br /&gt;
&lt;br /&gt;
&lt;em&gt;註一：若採用 JSON，由於原本的設計上並無法處理二進位資料，所以如果要包裝二進位資料，傳統做法會將資料轉換成 base64 的格式，再以字串（String）的格式儲存。因為這等於二次包裝資料，導致處理上非常沒有效率。&lt;/em&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;em&gt;註二：與 Google Protocol Buffers 類似的技術還有 MessagePack 及 Facebook 採用的 Apache Thrift，有興趣的人可以自行參考比較。&lt;/em&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;h2 id=&quot;toc_1&quot;&gt;
跨語言的優點&lt;/h2&gt;
&lt;br /&gt;
另外，Protocol Buffers 最大的優點，就是擁有跨程式語言的設計，提供了一個標準通用的 &lt;code&gt;.proto&lt;/code&gt; 定義方法，讓我們定義資料結構和格式。只需要載入這些我們事先準備好的資料定義，就可以輕易生成給不同語言（如：C++、C#、Go、Java、Objective-C 或 Python）用的資料解析器、包裝方法，讓我們可以在不同的語言之間，解析或包裝相同的結構資料。&lt;br /&gt;
&lt;br /&gt;
&lt;h2 id=&quot;toc_2&quot;&gt;
Protocol Buffers 的使用場景？&lt;/h2&gt;
&lt;br /&gt;
若在純粹的 Web 應用下，大多數情況，我們不需要處理二進位資料，或是需要非常精準的資料格式，也不會進行單筆高流量的資料交換，所以使用 JSON 或 XML 已經足以。但若你的應用有串流、二進位資料的需求，Protocol Buffers 就是你可以考慮的選擇。&lt;br /&gt;
&lt;br /&gt;
像是筆者在一些公司專案中，會運用 Message Queuing 進行各種訊息資料傳遞，以達成各種資料處理需求。但由於訊息資料內可能有大大小小等各種資料形式和資料型態需求，導致 JSON 包裝已經完全不敷使用，甚至有效能上的疑慮，這時就會採用 Prorocol Buffers 來打包資料。&lt;br /&gt;
&lt;br /&gt;
&lt;h2 id=&quot;toc_3&quot;&gt;
安裝 ProtoBuf.js&lt;/h2&gt;
&lt;br /&gt;
Google 官方其實並沒有實作 JavaScript 版本的 Protocol Buffers 支援，但還好廣大的社群已經有高手開發出 JavaScript 的模組「ProtoBuf.js」，除了在 Node.js 上可以使用以外，&lt;strong&gt;甚至可以在瀏覽器中使用&lt;/strong&gt;。&lt;br /&gt;
&lt;br /&gt;
所以，如果想在 Node.js 裡使用，可以直接透過 NPM 安裝模組：&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npm install protobufjs&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
補註：Protocol Buffers v3.0.0 beta 2 開始官方支援 JavaScript，未來有機會轉用官方的版本。&lt;br /&gt;
&lt;br /&gt;
&lt;h2 id=&quot;toc_4&quot;&gt;
使用 .proto 定義自己的資料格式&lt;/h2&gt;
&lt;br /&gt;
開始使用 Protocol Buffers 的第一個步驟，就是建立一個 &lt;code&gt;.proto&lt;/code&gt; 檔來描述定義一個自己的資料格式相當簡單，一個簡單的定義如下。&lt;br /&gt;
&lt;br /&gt;
&lt;strong&gt;Product.proto&lt;/strong&gt; 內容：&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;
&lt;pre&gt;&lt;code class=&quot;language-none&quot;&gt;package Ecommerce;

message Product {
    bool available = 1; // 是否上架（布林值）
    string name = 2;    // 產品名稱（字串）
    string desc = 3;    // 產品說明（字串）
    float price = 4;    // 價格（浮點數）
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
實際上 Protocol Buffers 支援了更多資料格式，有興趣的人可以自行參考官方所整理的表格：「&lt;a href=&quot;https://developers.google.com/protocol-buffers/docs/proto3#scalar&quot;&gt;Scalar Value Types&lt;/a&gt;」。&lt;br /&gt;
&lt;br /&gt;
&lt;h2 id=&quot;toc_5&quot;&gt;
使用我們定義的 .proto 來包裝資料&lt;/h2&gt;
&lt;br /&gt;
若要包裝資料，要先載入 .proto 檔案裡的資料定義，然後使用此定義去進行接下來的工作，而 ProtoBuf.js 提供了一個 encode 方法來進行資料包裝。&lt;br /&gt;
&lt;br /&gt;
由於經過 Protocol Buffers 包裝後的資料是二進位格式，所以 ProtoBuf.js 提供了 finish 方法輸出成 Node.js 的 Buffer 格式：&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;var ProtoBuf = require(&#39;protobufjs&#39;);

// 載入 Product.proto 檔案
ProtoBuf.load(&#39;Product.proto&#39;, function(err, root) {
    if (err)
        throw err;

    // 並取得 Product 資料定義
    var Product = root.lookup(&#39;Ecommerce.Product&#39;);
    
    // 準備包裝的資料
    var data = {
        available: true,
        name: &#39;ApplePen&#39;,
        desc: &#39;The combination of Apple and Pen&#39;,
        price: 100.0
    };
    
    // 包裝資料後回傳 Buffer 格式（二進位形態）
    var msgBuffer = Product.encode(data).finish();
});&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;h2 id=&quot;toc_6&quot;&gt;
解開已包裝的資料&lt;/h2&gt;
&lt;br /&gt;
若我們有一個已包裝過的資料（無論是從哪裡收到的資料），可以直接使用 decode 方法去解開它：&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;var ProtoBuf = require(&#39;protobufjs&#39;);

// 載入 Product.proto 檔案
ProtoBuf.load(&#39;Product.proto&#39;, function(err, root) {
    if (err)
        throw err;

    // 並取得 Product 資料定義
    var Product = root.lookup(&#39;Ecommerce.Product&#39;);
    
    // 解開
    var data = Product.decode(msgBuffer);
});
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;h2 id=&quot;toc_7&quot;&gt;
二進位資料形態的欄位&lt;/h2&gt;
&lt;br /&gt;
前面提到，Protocol Buffers 可以包裝二進位資料，若我們想要設定某個欄位為二進位的資料，可以將其資料型態設為「bytes」：&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;
&lt;pre&gt;&lt;code class=&quot;language-none&quot;&gt;package MyTest;

message Example {
    bytes binData = 1; 
}&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
然後，當我們在包裝資料時，該欄位應該是一個 Buffer 的物件：&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;var msgBuffer = Example
    .encode({
        binData: new Buffer(&#39;This is binary data&#39;)
    })
    .finish();&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
解開時，該欄位會是一個 Buffer 物件：&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;var data = Example.decode(msgBuffer);

// 將 Buffer 內容轉成字串形式輸出
console.log(data.binData.toString());&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;h2 id=&quot;toc_8&quot;&gt;
ProtoBuf.js 的效能表現&lt;/h2&gt;
&lt;br /&gt;
Protocol Buffers 這類的技術，不外乎就是把一個執行期的 JavaScript 物件，轉換包裝成二進位、字串等資料格式，使資料訊息便於透過網路和其他媒介傳送。實務上，與 JavaScript 物件轉成 JSON 字串是同樣的意思。&lt;br /&gt;
&lt;br /&gt;
所以若要評估這樣技術的效能，最實際的方式就是測試、比較他們的「轉換」的效率，ProtoBuf.js 官方提供了一些「&lt;a href=&quot;https://github.com/dcodeIO/ProtoBuf.js#performance&quot;&gt;效能測試&lt;/a&gt;」，方便我們在自己機器上進行 Protocol Buffers 與原生 JSON 處理的效能比較。&lt;br /&gt;
&lt;br /&gt;
從官方的測試結果來看，從資料包裝的速度，ProtoBuf.js 的效能快過於「JSON.stringify」將近一倍，如果是轉成二進位形式（to Buffer）更是快三倍左右；從解開包裝的速度來看，ProtoBuf.js 效能則是「JSON.parse」的三至四倍效能以上。&lt;br /&gt;
&lt;br /&gt;
&lt;strong&gt;整體比較起來，ProtoBuf.js 則是比純 JSON 的處理快上一倍以上。&lt;/strong&gt;&lt;br /&gt;
&lt;br /&gt;
節錄官方 Github 上的測試結果（機器：i7-2600K。Node.js 版本：6.9.1）：&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;
&lt;pre&gt;&lt;code class=&quot;language-none&quot;&gt;benchmarking encoding performance ...

Type.encode to buffer x 547,361 ops/sec ±0.27% (94 runs sampled)
JSON.stringify to string x 310,848 ops/sec ±0.73% (92 runs sampled)
JSON.stringify to buffer x 173,608 ops/sec ±1.51% (86 runs sampled)

      Type.encode to buffer was fastest
   JSON.stringify to string was 43.5% slower
   JSON.stringify to buffer was 68.7% slower

benchmarking decoding performance ...

Type.decode from buffer x 1,294,378 ops/sec ±0.86% (90 runs sampled)
JSON.parse from string x 291,944 ops/sec ±0.72% (92 runs sampled)
JSON.parse from buffer x 256,325 ops/sec ±1.50% (90 runs sampled)

    Type.decode from buffer was fastest
     JSON.parse from string was 77.4% slower
     JSON.parse from buffer was 80.3% slower

benchmarking combined performance ...

Type to/from buffer x 254,126 ops/sec ±1.13% (91 runs sampled)
JSON to/from string x 122,896 ops/sec ±1.29% (90 runs sampled)
JSON to/from buffer x 88,005 ops/sec ±0.87% (89 runs sampled)

        Type to/from buffer was fastest
        JSON to/from string was 51.7% slower
        JSON to/from buffer was 65.3% slower

benchmarking verifying performance ...

Type.verify x 6,246,765 ops/sec ±2.00% (87 runs sampled)

benchmarking message from object performance ...

Type.fromObject x 2,892,973 ops/sec ±0.70% (92 runs sampled)

benchmarking message to object performance ...

Type.toObject x 3,601,738 ops/sec ±0.72% (93 runs sampled)&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;h2 id=&quot;toc_9&quot;&gt;
其他使用場景？&lt;/h2&gt;
&lt;br /&gt;
只要你有需要跟其他系統、服務、外部程式進行資料交換，Protocol Buffers 就有他適用的地方。&lt;br /&gt;
&lt;br /&gt;
舉例來說，現在很多人開始採用 WebSocket 取代傳統的 Socket，使得 WebSocket 不再只是應用在瀏覽器之中，甚至可能是各種機器與機器之間的溝通。在這種情況下，其中交換、傳遞的資訊可能不是普通純文字這麼簡單，也很有可能是二進位類型、串流形式的資料，導致 JSON 可能因此不適合用於當作其中的資料交換格式。這時，就可以 Protocol Buffers 與 WebSocket 搭配使用。&lt;br /&gt;
&lt;br /&gt;
不只如此，在這 IoT 當道的年代，在這訊息技術滿天飛的年代 AMQP、MQTT 等各種通訊技術下，以及需要許多爆量資料收集分析的場景，Protocol Buffers 也很有發揮的空間。&lt;br /&gt;
&lt;br /&gt;
&lt;h2 id=&quot;toc_10&quot;&gt;
後記&lt;/h2&gt;
&lt;br /&gt;
要注意的是，Protocol Buffers 雖然是個好東西，但並非是個用來完全取代 JSON 的解決方案，JSON 仍有其可讀性高、易操作及通用性高等優點。在多數 API 設計的場景之下，JSON 仍然是最好的選擇。</description><link>https://fred-zone.blogspot.com/2017/02/nodejs-protocol-buffers.html</link><author>noreply@blogger.com (Fred Chien)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgu8N2eEIW2RGPiTHaANo0q-hBVisqcqCwW3is2x0twJwgUUsDI_sJpN-lYvEM3B-OoUXep3asG5kDgzUIQPw_IDwftbLRXc2OiSH0siyr252yg9CumTxGAMjC1nrvep2FnEklOmSYxJqQ/s72-c/protobuf.png" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-3538285099977947879</guid><pubDate>Wed, 25 Jan 2017 12:53:00 +0000</pubDate><atom:updated>2017-02-02T16:25:11.890+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">Web</category><category domain="http://www.blogger.com/atom/ns#">Web 相關技術</category><category domain="http://www.blogger.com/atom/ns#">程式心得筆記</category><title>上手使用 JavaScript 的 Map、Reduce 吧！</title><description>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhne1CvzfY8rLtOOZU50G4m6lXB1O8Y2Ys1vxpl6rp0HbTNpFAAVbnY3MNvqrF262EBCoiDyvxGkqXzP2F_WH0AzIikUzYIq5s7lBNYN998fTu9amD_xrh0wP7QfrAzAzQInTLAfRvxqv4/s1600/js-mapreduce.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhne1CvzfY8rLtOOZU50G4m6lXB1O8Y2Ys1vxpl6rp0HbTNpFAAVbnY3MNvqrF262EBCoiDyvxGkqXzP2F_WH0AzIikUzYIq5s7lBNYN998fTu9amD_xrh0wP7QfrAzAzQInTLAfRvxqv4/s1600/js-mapreduce.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;span id=&quot;goog_1061361673&quot;&gt;&lt;/span&gt;&lt;span id=&quot;goog_1061361674&quot;&gt;&lt;/span&gt;&lt;br /&gt;
雖然有些概念類似甚至可以相通，但這裡並不是指常聽到的「MapReduce」，本文目的不是要討論如何運用 MapReduce 這樣的架構去處理大資料庫。這裡真正要討論的是，如何使用 JavaScript 裡陣列（Array）中的 .map() 和 .reduce() 方法，並把一些常見的使用方法和情境描述出來大家進行參考。&lt;br /&gt;
&lt;br /&gt;
很多人對這兩個方法不習慣，原因不外乎是這兩種方法本來就不是一個非常直覺的東西，在大多數 JavaScript 語言的開發情境中，其實也沒有非得使用的理由。但不得不說，習慣了這兩個對陣列操作的方法，程式碼會變得簡潔，也更容易能處理一整批的資料。有時也能順便學習到一些「Functional Programming」會用到的概念，無論是在改善程式品質，還是投資自己的角度上，都有相當好處。&lt;br /&gt;
&lt;br /&gt;
&lt;h2 id=&quot;toc_1&quot;&gt;從最簡單的遍歷陣列開始&lt;/h2&gt;&lt;br /&gt;
面對一個陣列裡的一堆資料，我們一定是從遍歷開始，一一處理裡面的每一筆資料。你也許已經非常熟悉如何遍歷陣列，最常見的不外乎就是兩種做法。&lt;br /&gt;
&lt;br /&gt;
使用 for-loop：&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;var myArr = [ 1, 2, 3 ];

for (var index in myArr) {
    console.log(myArr[index]);
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
使用陣列內建的 forEach 方法：&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;var myArr = [ 1, 2, 3 ];

myArr.forEach(function(element) {
    console.log(element);
});&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
&lt;h2 id=&quot;toc_2&quot;&gt;使用 .map() 對每個陣列元素加工&lt;/h2&gt;&lt;br /&gt;
有些時候，我們想對每個陣列元素（Element）進行加工處理，於是最土法煉鋼的方法大概就是這樣：&lt;br /&gt;
&lt;br /&gt;
幫每個元素加一：&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;var myArr = [ 1, 2, 3 ];

for (var index in myArr) {
    myArr[index] = myArr[index] + 1;
}

// [ 2, 3, 4 ]
console.log(myArr);&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
這時你可以使用 .map() 方法來達成同樣目的：&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;var myArr = [ 1, 2, 3 ];

var newArr = myArr.map(function(element) {
    return element + 1;
});

// [ 2, 3, 4 ]
console.log(newArr);&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
.map() 會將每一個元素代入處理函數，而處理函數回傳的值，會被收集組成一個新的陣列，這個新的陣列元素數量會和原本陣列的一樣。換句話說，同樣是對陣列加工後得到結果，它會回傳一個新的、加工過後的陣列，而不會修改原本的陣列內容。&lt;br /&gt;
&lt;br /&gt;
&lt;h2 id=&quot;toc_3&quot;&gt;使用 .map() 進行資料校正處理&lt;/h2&gt;&lt;br /&gt;
當我們了解 .map() 的運作原理後，可以使用它做到更多資料處理的事，例如資料的校正或過濾。&lt;br /&gt;
&lt;br /&gt;
舉例來說，若是我們得到一個包含許多數值的陣列，而我們想限定這些數值不得超過我們設定的上限值，這時我們可以這樣處理，來得到一個經過檢查校正過後的資料結果：&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;var myArr = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];

var newArr = myArr.map(function(element) {
    // 數值大於五的數值視為五
    if (element &amp;gt; 5)
        return 5;
        
    return element;
});

// [ 1, 2, 3, 4, 5, 5, 5, 5, 5, 5 ]
console.log(newArr);&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
&lt;h2 id=&quot;toc_4&quot;&gt;使用 .reduce() 進行數值加總&lt;/h2&gt;&lt;br /&gt;
處理陣列資料的工作中，其中一項最常見的就是數值加總，或是進行統計運算。同樣的，若你使用土法煉鋼的做法，大致上如下：&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;var myArr = [ 1, 2, 3 ];
var result = 0;

for (var index in myArr) {
    result += myArr[index];
}

// 6
console.log(result);&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
若使用 .reduce()，可以這樣寫：&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;var myArr = [ 1, 2, 3 ];

// 處理每個元素後等待回傳結果，第一次處理時代入初始值 0
var result = myArr.reduce(function(prev, element) {
    // 與之前的數值加總，回傳後代入下一輪的處理
    return prev + element;
}, 0);

// 6
console.log(result);&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
我們可以看到，改用 .reduce() 之後，陣列元素的加總計算，不會再一直存取到外部的 result 變數，而是算完結果後才將結果統計結果回傳。這樣做的好處，是不會再跨 Scope 去存取外部的變數，這對 JavaScript 這種有複雜 Scope 設計的語言來說，程式碼不會到處去污染。&lt;br /&gt;
&lt;br /&gt;
&lt;h2 id=&quot;toc_5&quot;&gt;把 .map() 和 .reduce() 串接起來吧！&lt;/h2&gt;&lt;br /&gt;
這兩種方法都是用來處理陣列，所以我們可以輕易地串接兩者，以前面的例子來說，可以先對陣列資料進行校正和加工，然後對資料進行收斂和加總：&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;var myArr = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];

var result = myArr
    .map(function(element) {
        // 數值大於五的數值視為五
        if (element &amp;gt; 5)
            return 5;
            
        return element;
    })
    .reduce(function(prev, element) {
        // 與之前的數值加總，回傳後代入下一輪的處理
        return prev + element;
    }, 0);

// 40
console.log(result);&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
&lt;h2 id=&quot;toc_6&quot;&gt;利用 .reduce() 進行陣列扁平化&lt;/h2&gt;&lt;br /&gt;
如果你開始查 .reduce() 的資料，應該會看到一些 MDN 文件，會提到一些相當實用的功能，其中一個就是扁平化陣列的應用。簡單來說，就是將一個複雜的陣列，扁平化成一維，這在很多資料處理或數值計算上相當有用。&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;var myArr = [
    [ 1, 2 ],
    [ 3, 4, 5 ],
    [ 6, 7, 8 ]
];

// 將所有元素都與之前代入的陣列相接起來，第一次處理時代入初始值空陣列
var newArr = myArr.reduce(function(arr, element) {
    // ex: [ 1, 2 ].concat([ 3, 4, 5 ])
    return arr.concat(element);
}, []);

// [ 1, 2, 3, 4, 5, 6, 7, 8 ]
console.log(newArr);&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
所以這個處理函數將會被執行三次：&lt;br /&gt;
&lt;br /&gt;
&lt;ol&gt;&lt;li&gt;將空陣列與 [ 1, 2 ] 相接起來後回傳&lt;/li&gt;
&lt;li&gt;將被代入的 [ 1, 2 ] 與 [ 3, 4, 5 ] 相接起來後回傳&lt;/li&gt;
&lt;li&gt;將被代入的 [ 1, 2, 3, 4, 5 ] 與 [ 6, 7, 8 ] 相接起來後回傳&lt;/li&gt;
&lt;/ol&gt;&lt;br /&gt;
&lt;h2 id=&quot;toc_7&quot;&gt;利用 .reduce() 進行資料歸納和統計吧！&lt;/h2&gt;&lt;br /&gt;
我們也可以利用 .reduce() 配合上物件操作，對陣列的內容進行統計工作：&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;var myArr = [
    &#39;C/C++&#39;,
    &#39;JavaScript&#39;,
    &#39;Ruby&#39;,
    &#39;Java&#39;,
    &#39;Objective-C&#39;,
    &#39;JavaScript&#39;,
    &#39;PHP&#39;
];

// 計算出每種語言出現過幾次
var langStatistics = myArr.reduce(function(langs, langName) {
    if (langs.hasOwnProperty(langName)) {
        langs[langName]++
    } else {
        langs[langName] = 1;
    }
    
    return langs;
}, {});

// { &#39;C/C++&#39;: 1, &#39;JavaScript&#39;: 2, &#39;Ruby&#39;: 1, &#39;Java&#39;: 1, &#39;Objective-C&#39;: 1, &#39;PHP&#39;: 1 }
console.log(langStatistics);&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
&lt;h2 id=&quot;toc_8&quot;&gt;如果想要處理的資料是 Object 的形式怎麼辦？&lt;/h2&gt;&lt;br /&gt;
運用 Object.keys() 這樣的技巧，我們可以把 .map() 或 .reduce() 結合使用到 Object 的資料上使用，這樣就可以對 Object 資料進行相同的統計運算或數值計算。&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;var data = {
    &#39;Fred&#39;: 1,
    &#39;Leon&#39;: 2,
    &#39;Wesley&#39;: 3,
    &#39;Chuck&#39;: 4,
    &#39;Denny&#39;: 5
};

// 使用 Object.keys() 取得包含所有 key 的陣列
var result = Object.keys(data).reduce(function(prev, name) {
    // 利用 key 取得原始物件中的值，然後加總
    return data[name] + prev;
}, 0);

// 15
console.log(result);&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
&lt;h2 id=&quot;toc_9&quot;&gt;你在寫啥？結合 ECMAScript 6 後，世界都不一樣了。&lt;/h2&gt;&lt;br /&gt;
ES6 已經上了實際的戰場，當 .map()/.reduce() 方法加上箭頭函數（Arrow Function&lt;br /&gt;
），然後又配合上 JavaScript 語言的特性，整個程式碼將變得更為簡短乾淨。&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;let newArr = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ].map((value) =&amp;gt; value + 1);&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
當箭頭函數只有一個參數時，可以省去括號「()」：&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;let newArr = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ].map(value =&amp;gt; value + 1);&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
註：不過，對於不習慣的人來說，更難閱讀了。但在開放原始碼和社群的圈子裡，因為已經被大量使用，所以最好趕快習慣它，會方便你更容易看懂坊間的各種「新」程式碼。&lt;br /&gt;
&lt;br /&gt;
&lt;h2 id=&quot;toc_10&quot;&gt;後記&lt;/h2&gt;&lt;br /&gt;
當然，濫用 map/reduce 也可能會造成程式碼難以閱讀，無論是哪一種程式的技巧，這肯定都是一個問題。但至於什麼時候該用，什麼時候不該用，並不在本文範疇，個人認為，我們得先熟練使用這兩種方法，用熟了，再接著探討「好的使用情境」才有意義。因為很多人不熟悉，又不敢亂用，就更沒有機會習慣它了。&lt;br /&gt;
&lt;br /&gt;
所以，先別想太多，嘗試習慣使用它們吧！</description><link>https://fred-zone.blogspot.com/2017/01/javascript-mapreduce.html</link><author>noreply@blogger.com (Fred Chien)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhne1CvzfY8rLtOOZU50G4m6lXB1O8Y2Ys1vxpl6rp0HbTNpFAAVbnY3MNvqrF262EBCoiDyvxGkqXzP2F_WH0AzIikUzYIq5s7lBNYN998fTu9amD_xrh0wP7QfrAzAzQInTLAfRvxqv4/s72-c/js-mapreduce.png" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-3034369484532046771</guid><pubDate>Sat, 31 Dec 2016 09:55:00 +0000</pubDate><atom:updated>2016-12-31T18:01:08.236+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">Node.js</category><category domain="http://www.blogger.com/atom/ns#">程式心得筆記</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><title>Node.js 小密技：以 Readline 核心模組一行行讀取檔案內容</title><description>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgl5BnehSpoAOTzC8a6Ip1rKlEAEepGvq25f_p31r3_ksDOHPVJ3AWQ5uw2Q7z95F1ll8b80s9kO86Ja0mC_52CyeX7sjYaHHeqwDgqvH95c_P_8zGMFTRdk60V70lplrilsZkXZUHjxBw/s1600/bv_7qb5kxc8-jens-johnsson.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgl5BnehSpoAOTzC8a6Ip1rKlEAEepGvq25f_p31r3_ksDOHPVJ3AWQ5uw2Q7z95F1ll8b80s9kO86Ja0mC_52CyeX7sjYaHHeqwDgqvH95c_P_8zGMFTRdk60V70lplrilsZkXZUHjxBw/s1600/bv_7qb5kxc8-jens-johnsson.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
最近參與了一些關於資料處理的專案，處理了很多各式各樣的原始資料（Raw Data）或各種不同格式的資料，於是使用到了 Node.js 上的一些小技巧。像是一行行讀取檔案內容這件事，就隱藏了一些技巧。&lt;br /&gt;
&lt;br /&gt;
對很多人來說，處理的檔案內容都不大，如果用 Node.js 來一行行讀取檔案內容，不外乎就是將整個檔案讀出後再進行切割，做法大致上如下：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;var fs = require(&#39;fs&#39;);

fs.readFile(&#39;example.txt&#39;, function(err, data) {

    // 以換行字元作為切割點，將內容切成一個大陣列
    var lines = data.split(&#39;\n&#39;);

    lines.forEach(function(line) {
        // 一行行處理
    });
});&lt;/pre&gt;&lt;/div&gt;但有些時候，由於檔案並不小，若又牽涉到運算，不可能整個檔案都讀出到記憶體上才進行切割，這時就得用到 Stream（資料流）機制，將檔案一段段讀出來進行處理。然後，為了進行一行行的切割，我們會自己做這樣的機制，先將一段段讀取出來的檔案內容放到緩衝區（Buffer），然後找到換行字元進行切斷取出，然後再繼續讀取檔案，重複這樣的過程直到檔案結尾。&lt;br /&gt;
&lt;br /&gt;
的確，實做這樣的機制有點麻煩，所以其實能利用 Node.js 現成內建的核心模組 Readline 來做到切割資料流中一行字串的工作。因為常見的 Readline 用法都是拿來做終端機字元模式下的命令列操作，所以許多人沒有想到可以這樣使用 Readline。作法其實很簡單，就把 Readline 的 input 從標準輸入（Standard Input）換成我們的檔案讀取資料流就可以。&lt;br /&gt;
&lt;br /&gt;
完整做法如下：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;var fs = require(&#39;fs&#39;);
var readline = require(&#39;readline&#39;);

// 建立檔案讀取資料流
var inputStream = fs.createReadStream(&#39;example.txt&#39;);

// 將讀取資料流導入 Readline 進行處理 
var lineReader = readline.createInterface({ input: inputStream });
lineReader.on(&#39;line&#39;, function(line) {

    // 取得一行行結果
    console.log(&#39;NEW LINE&#39;, line);
});&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
&lt;h2&gt;後記&lt;/h2&gt;其實這樣的 Readline 用法，在 Node.js 官方 API 文件上可以看到，只不過是不久前才被加進去的，在文件的最後面。:-P&lt;br /&gt;
&lt;br /&gt;
參考連結：&lt;a href=&quot;https://nodejs.org/api/readline.html&quot;&gt;https://nodejs.org/api/readline.html&lt;/a&gt;</description><link>https://fred-zone.blogspot.com/2016/12/nodejs-readline.html</link><author>noreply@blogger.com (Fred Chien)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgl5BnehSpoAOTzC8a6Ip1rKlEAEepGvq25f_p31r3_ksDOHPVJ3AWQ5uw2Q7z95F1ll8b80s9kO86Ja0mC_52CyeX7sjYaHHeqwDgqvH95c_P_8zGMFTRdk60V70lplrilsZkXZUHjxBw/s72-c/bv_7qb5kxc8-jens-johnsson.jpg" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-3820519064200669413</guid><pubDate>Tue, 27 Dec 2016 16:29:00 +0000</pubDate><atom:updated>2016-12-28T00:30:06.687+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">C/C++</category><category domain="http://www.blogger.com/atom/ns#">Linux</category><category domain="http://www.blogger.com/atom/ns#">QML</category><category domain="http://www.blogger.com/atom/ns#">Qt</category><category domain="http://www.blogger.com/atom/ns#">Raspberry Pi</category><category domain="http://www.blogger.com/atom/ns#">User Interface</category><category domain="http://www.blogger.com/atom/ns#">程式心得筆記</category><category domain="http://www.blogger.com/atom/ns#">系統研發手札</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><title>產品開發玩技術很過癮！實作 QML 動畫背景！</title><description>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhveakbPHbty2Gmv99twqxyU28KsbZIgEyE7AUFfSETcz3OP87SE81WNXdPfDWxcT8rS3G8BswADw4rdtFgs8EDtTqCEuWGZ1Dorl1s36IXcpHI9u9OpJmcD5SA_G4kP3pYWGaZIS0C7lY/s1600/%25E8%259E%25A2%25E5%25B9%2595%25E5%25BF%25AB%25E7%2585%25A7+2016-12-27+%25E4%25B8%258B%25E5%258D%25889.29.39.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhveakbPHbty2Gmv99twqxyU28KsbZIgEyE7AUFfSETcz3OP87SE81WNXdPfDWxcT8rS3G8BswADw4rdtFgs8EDtTqCEuWGZ1Dorl1s36IXcpHI9u9OpJmcD5SA_G4kP3pYWGaZIS0C7lY/s1600/%25E8%259E%25A2%25E5%25B9%2595%25E5%25BF%25AB%25E7%2585%25A7+2016-12-27+%25E4%25B8%258B%25E5%258D%25889.29.39.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
由於最近在開發自己的產品，又開始重操舊業，開發起 Linux 系統的相關應用和嵌入式技術。為了這個產品，精心開發了一個使用者介面，除了動手把驅動程式搞定、圖形化介面搞定，也調教效能、改善系統架構。&lt;br /&gt;
&lt;br /&gt;
開發自己的產品很過癮，愛怎麼搞就怎麼搞！於是，看到死板的背景覺得很不舒服，就在思考是否可以跑個動畫背景呢？&lt;br /&gt;
&lt;br /&gt;
因為使用的是 QML 技術來開發 UI，最直接的想法，就是用 QtMultimedia 的 MediaPlayer 無限循環播放一個影片，當作動畫背景：&lt;br /&gt;
&lt;div&gt;
&lt;pre&gt;MediaPlayer {
    id: bg;
    source: &#39;bg.mov&#39;;
    loops: MediaPlayer.Infinite;
    autoPlay: true;
}

VideoOutput {
    anchors.fill: parent;
    source: bg;
}&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
當然，我們選擇的背景影片，是一個開頭跟結尾一樣的影片，如果正確循環播放，會無縫接軌的變成一個順暢的動畫背景。&lt;br /&gt;
&lt;br /&gt;
然而，結果不如預期，碰到了一個問題，那就是每當背景影片播放到最後時，會畫面變成全黑，然後才再一次重新開始播放，沒辦法「無縫接軌」。仔細暸解以後，發現 MediaPlayer 元件是 QMediaPlayer 的 QML Type 實作，所有秘密都藏在 QMediaPlayer 之中。因為 QMediaPlayer 預設所有的通知事件，都是固定以 1000ms（1秒）的頻率來觸發，這代表，當 QML 元件發現影片播完時，通常已經是播完以後的事了，所以畫面一定會因為影片結束而變黑，然後 QML 元件才發現影片結束，重新進行播放。&lt;br /&gt;
&lt;br /&gt;
知道緣由後，我們可以從事件更新頻率下手，讓 QML 元件發現影片播完的時間更接近實際影片結束的時間，但這必須動用到 C/C++ 的實作，因為 QMediaPlayer 的事件更新頻率無法以純 QML 的方法修改。&lt;br /&gt;
&lt;br /&gt;
C/C++ 完整應用程式的實作如下，我們把更新頻率調高為每 100ms 一次：&lt;br /&gt;
&lt;div&gt;
&lt;pre&gt;#include &amp;lt;QGuiApplication&amp;gt;
#include &amp;lt;QQmlApplicationEngine&amp;gt;
#include &amp;lt;QQuickWindow&amp;gt;
#include &amp;lt;QMediaObject&amp;gt;
#include &amp;lt;QMediaPlayer&amp;gt;

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);

    QQmlApplicationEngine engine;
    engine.load(QUrl(QStringLiteral(&quot;qrc:/resources/App.qml&quot;)));

    // Getting background component by using background objectName
    QObject *obj = static_cast&amp;lt;QObject *&amp;gt;(engine.rootObjects().first());
    QObject *background = obj-&amp;gt;findChild&amp;lt;QObject *&amp;gt;(&quot;background&quot;);

    // Set NotifyInterval to 100ms
    QMediaPlayer *player = qvariant_cast&amp;lt;QMediaPlayer *&amp;gt;(background-&amp;gt;property(&quot;mediaObject&quot;));
    player-&amp;gt;setNotifyInterval(100);

    return app.exec();
}&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
除此之外，為了可以順利取得 QML 中的 MediaPlayer 元件，我們需要幫其設定一個「objectName」作為識別，讓 C/C++ 這的原生程式可以搜尋的到該元件：&lt;br /&gt;
&lt;div&gt;
&lt;pre&gt;MediaPlayer {
    id: bg;
    objectName: &#39;background&#39;;
    source: &#39;bg.mov&#39;;
    loops: MediaPlayer.Infinite;
    autoPlay: true;
}&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
雖然我們縮短了每次更新的間隔時間，調到了 100ms，已經非常接近了影片結束時間，但仍然可能會發生問題。所以保險起見，我們可以多做些檢查工作，在影片結束前 100ms 左右時，就讓他重頭開始播放一次影片。&lt;br /&gt;
&lt;div&gt;
&lt;pre&gt;MediaPlayer {
    id: bg;
    objectName: &#39;background&#39;;
    source: &#39;bg.mov&#39;;
    loops: MediaPlayer.Infinite;
    autoPlay: true;

    onPositionChanged: {
        if (position &amp;gt;= duration - 100) {
            bg.seek(0);
        }
    }
}&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
理論上，這樣做會損失不到 100ms 長度的動畫，但人通常感覺不出來這麼短的損失，而且是前方還有 UI 介面的情況之下。但如果你仍然有感，可以考慮把頻率改為 50ms 或更少。&lt;br /&gt;
&lt;br /&gt;
最後就可以享受會動的背景啦！</description><link>https://fred-zone.blogspot.com/2016/12/qml.html</link><author>noreply@blogger.com (Fred Chien)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhveakbPHbty2Gmv99twqxyU28KsbZIgEyE7AUFfSETcz3OP87SE81WNXdPfDWxcT8rS3G8BswADw4rdtFgs8EDtTqCEuWGZ1Dorl1s36IXcpHI9u9OpJmcD5SA_G4kP3pYWGaZIS0C7lY/s72-c/%25E8%259E%25A2%25E5%25B9%2595%25E5%25BF%25AB%25E7%2585%25A7+2016-12-27+%25E4%25B8%258B%25E5%258D%25889.29.39.png" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-3726209343173394802</guid><pubDate>Wed, 27 Jul 2016 04:05:00 +0000</pubDate><atom:updated>2016-07-27T12:05:22.452+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">ECMAScript</category><category domain="http://www.blogger.com/atom/ns#">ECMAScript 7</category><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">Web 相關技術</category><category domain="http://www.blogger.com/atom/ns#">技術新知</category><title>JavaScript 好用的 async 異步函數！</title><description>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_5_iIalF5fT2E4ZwGJgmc2nTod4Pz6GzWb9A64r55Q7QD3f0-xZDs8cuhlafAdX6ksNg-bwh_tXop1hdl5dpfLC2KQKqa79Si-EOY-HGaYB1xggAvVirtNAURB6hn_IZ6XLGfWFrfbLI/s1600/ES7-async.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_5_iIalF5fT2E4ZwGJgmc2nTod4Pz6GzWb9A64r55Q7QD3f0-xZDs8cuhlafAdX6ksNg-bwh_tXop1hdl5dpfLC2KQKqa79Si-EOY-HGaYB1xggAvVirtNAURB6hn_IZ6XLGfWFrfbLI/s1600/ES7-async.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
先聲明，async 異步函數是 ECMAScript 第七版（ES7）才被支援的語法和特性，目前 ES7 還沒有被大多數的 JavaScript Engine 所實作，如果你要使用，需要用到 babel 這類工具，先把此程式編譯轉換，讓其可在舊版本 JavaScript Engine 上執行。&lt;br /&gt;
&lt;br /&gt;
如果你覺得以 co 模組來操作 Generator 很好用，你可以想像 async 異步函數就是原生的 co，幾乎是同樣的使用方式，同樣的使用概念，只不過不再需要使用 generator 和 yield 這類語法。如果你是個過不了在函數上有個醜陋「*」符號這一關的人，async 異步函數的使用方式應該會讓你感覺到舒服許多。&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;什麼是 async 異步函數（async functions）？&lt;/h2&gt;&lt;br /&gt;
異步函數使用方式其實和一般的函數一樣，只不過在這函數之內的程式，可以用 await 的語法去執行並等待異步工作（如：Promise）而不需要使用到骯髒的 callback function。宣告並使用一個 async 異步函數，就是在定義函數時加上「async」，然後直接執行這個函數即可，簡單的範例如下：&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;async function myAsyncFunc() {
    console.log(&#39;Hello async functions!&#39;);
}

myAsyncFunc();
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
&lt;h2&gt;搭配 Promise 的使用&lt;/h2&gt;&lt;br /&gt;
Promise 通常被大量用來管理非同步的工作，並讓開發者容易管理錯誤拋出等機制，一個典型的 Promise 使用如下：&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;var task = new Promise(function(resolve, reject) {

    // 執行一個非同步的工作，完成後呼叫帶入的 callback
    doAsyncTask(function(err) {

        // 有問題呼叫 reject，並帶入錯誤值
        if (err)
            return reject(err);

        // 成功呼叫 resolve 並帶入回傳值
        resolve(&#39;VALUE&#39;);
    });
});

// 使用 then 去執行並等待工作完成，成功會呼叫 callback，失敗則用 catch 去接收。
task
    .then(function(val) {
        console.log(val);
    })
    .catch(function(err) {
        console.log(err);
    });
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
如果在「異步函數」中呼叫以 Promise 包裝的工作，可以直接使用 await 語法：&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;async function myAsyncFunc() {
    var val = await task;
    console.log(val);
}

myAsyncFunc();
&lt;/pre&gt;&lt;/div&gt;你會看到在異步函數中，程式邏輯會以「像是阻塞的方式進行」，await 會等到工作完成後，將回傳值回傳，然後才繼續下一行工作。要注意的是，因為看起來像程式會阻塞，熟悉 JavaScript 的人會不自覺開始害怕事件引擎被鎖死，但實際上 await 是以非同步的方式在進行，並不會卡住或影響事件引擎的運作。&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;搭配 Thunk 的使用&lt;/h2&gt;&lt;br /&gt;
什麼是 Thunk？簡單來說就是一個處理函數，完成時會呼叫 callback 函數表示完成，實務上最常的用法會在外面包一層函數，創造一個 Closure，一個簡單的 Thunk 如下：&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;function myThunkFunc(thing) {

    return function(done) {

        setTimeout(function() {
            console.log(thing);
            done(null, &#39;World&#39;);
        }, 1000);
    };
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
異步函數裡面，我們可以這樣使用它：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;async function myAsyncFunc() {
    var val = await myThunkFunc(&#39;Hello&#39;);
    console.log(val);
}

myAsyncFunc();
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
&lt;h2&gt;等待其他異步函數完成工作&lt;/h2&gt;&lt;br /&gt;
當然，await 除了可以吃 Thunk 和 Promise 之外，也可以處理並等待其他的「異步函數」，如下：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;async function anotherAsyncFunc(thing) {
    var val = await myThunkFunc(thing);
    return val;
}

async function myAsyncFunc() {
    var val = await anotherAsyncFunc(&#39;Hello&#39;);
    console.log(val);
}

myAsyncFunc();
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
&lt;h2&gt;錯誤處理&lt;/h2&gt;&lt;br /&gt;
當 Promise 的 reject() 被呼叫，或是 Thunk 的 callback 函數被呼叫時，第一個參數不是 null，就代表這個異步工作是有錯誤發生的，如果要從 await 偵測這些錯誤訊息，需要使用 try-catch 去接這些錯誤訊息。&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;async function myAsyncFunc() {
    try {
        var val = await myThunkFunc(&#39;Hello&#39;);
    } catch(e) {
        console.log(e);
    }
}

myAsyncFunc();
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
&lt;h2&gt;舒服！到處使用異步函數&lt;/h2&gt;&lt;br /&gt;
一旦你熟悉如何使用異步函數，你可以到處使用。其實他就像一般的函數一樣，他可以被當成一個 callback 來使用，像是下面這個例子，就把它當成 Promise 的處理函數：&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;var task = new Promise(async function(resolve, reject) {
    try {
        await doAsyncTask();
    } catch(e) {
        return reject(e);
    }

    resolve();
});
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
&lt;h2&gt;後記&lt;/h2&gt;&lt;br /&gt;
如果你是原本就在使用 co 模組的人，應該會發現 async/await 根本就是一樣的東西，對你來說根本無痛，唯一有點麻煩的是，目前 JavaScript 仍然還沒有原生支援，需要 babel 一類的編譯器才能使用。但有不少人看重程式碼的簡潔和漂亮，已經大量使用了。&lt;br /&gt;
&lt;br /&gt;
另外提到，Koa 2.0 因為完全採用 async/await 的方式，無限期處於不穩定版本。等到 async/await 被原生支援那一天， Koa 2.0 穩定版就會推出了，相信這一天就快要到來。</description><link>https://fred-zone.blogspot.com/2016/07/javascript-async.html</link><author>noreply@blogger.com (Fred Chien)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_5_iIalF5fT2E4ZwGJgmc2nTod4Pz6GzWb9A64r55Q7QD3f0-xZDs8cuhlafAdX6ksNg-bwh_tXop1hdl5dpfLC2KQKqa79Si-EOY-HGaYB1xggAvVirtNAURB6hn_IZ6XLGfWFrfbLI/s72-c/ES7-async.png" height="72" width="72"/><thr:total>1</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-2622979190106853465</guid><pubDate>Mon, 06 Jun 2016 03:03:00 +0000</pubDate><atom:updated>2016-06-06T16:33:42.395+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Koa</category><category domain="http://www.blogger.com/atom/ns#">Node.js</category><category domain="http://www.blogger.com/atom/ns#">Web</category><category domain="http://www.blogger.com/atom/ns#">Web 相關技術</category><category domain="http://www.blogger.com/atom/ns#">技術新知</category><category domain="http://www.blogger.com/atom/ns#">程式心得筆記</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><title>下一代的框架：Koa 1.0 起手式</title><description>&lt;a href=&quot;https://camo.githubusercontent.com/674563115c4e0d4e5d99440b916952ad795c498e/68747470733a2f2f646c2e64726f70626f7875736572636f6e74656e742e636f6d2f752f363339363931332f6b6f612f6c6f676f2e706e67&quot; imageanchor=&quot;1&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://camo.githubusercontent.com/674563115c4e0d4e5d99440b916952ad795c498e/68747470733a2f2f646c2e64726f70626f7875736572636f6e74656e742e636f6d2f752f363339363931332f6b6f612f6c6f676f2e706e67&quot; /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
身為 Node.js 使用者的你，還在使用 Express 嗎？快來使用下一代的 Web Framework 吧！Koa 是由 Express 的開發者們出來所開發的新網站框架，嘗試採用了最新的 ECMAScript 6 語法，讓開發者可以用更簡約的方式，開發網站應用程式，讓程式碼更好維護之外，也能受益於最新的語言特性。&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;
穩定版與不穩定版&lt;/h2&gt;
&lt;br /&gt;
現在的 Koa 分為 1.0 和 2.0 兩個版本，1.0 使用 ES6 的 Generator 特性，也是目前的 stable 版本，而 2.0 採用 ES7+ 的 async/await，據 Koa 官方說法，2.0 穩定度可以用於實際產品，只是在 ECMAScript 7 規格正式敲定，且 JavaScript V8 Engine 推出原生的實作之前，將無限期處於 unstable 的狀態。也就是說，若你想要在你自己的專案上使用 2.0，你必須使用 babel 這一類的編譯器，因為裡面用到了 ES7 的語法。&lt;br /&gt;
&lt;br /&gt;
本篇文章的重點將放在 Koa 1.0 之上，畢竟在 ECMAScript 7 還處於草案階段的現在，很難說未來會不會有什麼改變。&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;
安裝 Koa&lt;/h2&gt;
&lt;br /&gt;
由於 Koa 需要用到 ECMAScript 6 的語言特性，請先檢查你的 Node.js 版本，最少為 0.12 以上，如果你已經使用了 Node.js 4.0 或更高版本，請不用擔心這個問題。&lt;br /&gt;
&lt;br /&gt;
然後透過 NPM 即可安裝模組：&lt;br /&gt;
&lt;pre&gt;npm install koa&lt;/pre&gt;
&lt;br /&gt;
&lt;h2&gt;
開發第一個應用程式&lt;/h2&gt;
&lt;br /&gt;
開發 Koa 應用程式非常容易，下面是程式碼範例：&lt;br /&gt;
&lt;pre&gt;var koa = require(&#39;koa&#39;);

var app = koa();

app.use(function *() {
    this.body = &#39;Hello World&#39;;
});

app.listen(3001);
&lt;/pre&gt;
跑起來後，用瀏覽器連入 3001 埠即可看到「Hello World」的字樣，因為 this.body 的內容，將會被輸出到前端瀏覽器上。&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;
使用 Generator 打造的 Middleware&lt;/h2&gt;
&lt;br /&gt;
koa.use() 將用來載入 Middleware，所有連線工作都會經過 Middleware 處理。所以前一個例子裡，我們使用 koa.use() 設定了一個處理函數，該函數會用來處理所有連線工作。&lt;br /&gt;
&lt;br /&gt;
要注意的是，在 function 後面有一個「*」的符號，這代表這個函數是一個 Generator 函數，所以這函數裡面的程式將可以使用 Generator 的語言特性。若您不知道 Generator 是什麼，可以參考過去的舊文「&lt;a href=&quot;http://fred-zone.blogspot.tw/2015/07/es6-generator-co.html&quot;&gt;快樂玩 ES6 Generator，從 co 起手式開始&lt;/a&gt;」。&lt;br /&gt;
&lt;br /&gt;
【註一】如果你有過 express 開發經驗，對於 koa.use() 會相當熟悉，Koa 同樣支援了 Middleware 的架構，你可以將過去的程式輕易移植到這新的框架上。&lt;br /&gt;
【註二】Koa 底層使用 co 來操作 Generator，若你覺得 Generator 太過艱澀，只需要了解 co 的使用即可。&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;
加入多個 Middleware&lt;/h2&gt;
&lt;br /&gt;
所有的連線要求可以透過一系列、不只一個 Middleware 來處理，我們可以利用多次 koa.use() 來使用它，範例如下：&lt;br /&gt;
&lt;pre&gt;var koa = require(&#39;koa&#39;);

var app = koa();

app.use(function *(next) {
    yield next;
});

app.use(function *() {
    this.body = &#39;Hello World&#39;;
});

app.listen(3001);
&lt;/pre&gt;
&lt;br /&gt;
一個 Middleware 可以透過 yield 傳入 next 參數，讓連線要求進入到下一個 Middleware 被處理。&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;
自訂 Router 和路徑管理&lt;/h2&gt;
&lt;br /&gt;
之前的範例直接使用 koa.use()，會將所有的連線都導入同一個處理函數，輸出同一個結果。若我們想要自訂不同的路徑，讓不同路徑用不同的處理函數，將需要額外安裝「koa-router」模組：&lt;br /&gt;
&lt;pre&gt;npm install koa-router&lt;/pre&gt;
&lt;br /&gt;
然後可以直接修改我們的程式碼如下：&lt;br /&gt;
&lt;pre&gt;var koa = require(&#39;koa&#39;);
var Router = require(&#39;koa-router&#39;);

var app = koa();
var router = new Router();

// 針對不同路徑套用不同處理函數
router.get(&#39;/&#39;, function *() {
    this.body = &#39;HOME&#39;;
});

router.get(&#39;/myapi&#39;, function *() {
    this.body = &#39;API&#39;;
});

// 載入自訂的 router
app.use(router.middleware());
app.listen(3001);
&lt;/pre&gt;
範例中只有使用到「GET」方法，如果要用來開發 Restful API 或是處理一些表單上傳的工作，可以依樣畫葫蘆使用 router.post、router.put 或 router.del 方法。&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;
接收 QueryString 的資料&lt;/h2&gt;
&lt;br /&gt;
QueryString 可說是歷史悠久且非常常見的傳值方法，藉由一個網址後面加上一個「?」字元後，就可以使用鍵值（Key/Value）來進行資料傳遞，並用「&amp;amp;」區隔多組資料。一個簡單的實際應用如下：&lt;br /&gt;
&lt;pre&gt;http://my_server/send?name=fred&amp;amp;msg=Hello
&lt;/pre&gt;
&lt;br /&gt;
取得資料的方法如下：&lt;br /&gt;
&lt;pre&gt;console.log(this.request.query.name);
console.log(this.request.query.msg);
&lt;/pre&gt;
&lt;br /&gt;
&lt;h2&gt;
接收 body 的資料&lt;/h2&gt;
&lt;br /&gt;
當我們使用「POST」或「PUT」方法，我們就可以利用 body 傳送一些資料到伺服器，像是網頁表單時常使用這樣的傳值方法。若想要取得 body 的資料，必須先安裝一個「koa-bodyparser」模組：&lt;br /&gt;
&lt;pre&gt;npm install koa-bodyparser&lt;/pre&gt;
&lt;br /&gt;
使用 koa.use() 載入 koa-bodyparser，koa 就會自動在處理連線時使用它解析 body：&lt;br /&gt;
&lt;pre&gt;var bodyParser = require(&#39;koa-bodyparser&#39;);

app.use(bodyParser());
&lt;/pre&gt;
&lt;br /&gt;
然後可以在路徑處理函數中，正常取得 body 內的資訊：&lt;br /&gt;
&lt;pre&gt;console.log(this.request.body.name);
console.log(this.request.body.msg);
&lt;/pre&gt;
&lt;br /&gt;
&lt;h2&gt;
靜態文件支援&lt;/h2&gt;
&lt;br /&gt;
除了一般動態網頁外，我們也會在網頁中嵌入 CSS、前端的 JavaScript 和圖片等靜態檔案，這些檔案在瀏覽器載入頁面時，同時間也要提供瀏覽器能取得。為了達成這功能，可以使用「koa-static」來達成：&lt;br /&gt;
&lt;pre&gt;npm install koa-static&lt;/pre&gt;
&lt;br /&gt;
然後可以直接加入這個 middleware：&lt;br /&gt;
&lt;pre&gt;var serve = require(&#39;koa-static&#39;);

app.use(serve(__dirname + &#39;/public&#39;));
&lt;/pre&gt;
&lt;br /&gt;
其中要帶入路徑參數，告訴 koa-static 去哪個目錄尋找對應的靜態檔案，範例中是設定為此程式同一個目錄下的 public 目錄。&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;
Session 支援&lt;/h2&gt;
&lt;br /&gt;
要使用 Session 要先安裝 koa-session：&lt;br /&gt;
&lt;pre&gt;npm install koa-session&lt;/pre&gt;
&lt;br /&gt;
然後就可以在處理函數中使用 this.session 這個物件來存放資料：&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;var koa = require(&#39;koa&#39;);
var Router = require(&#39;koa-router&#39;);
var session = require(&#39;koa-session&#39;);

var app = koa();
var router = new Router();

// 設定一組金鑰，用來加密 session
app.keys = [ &#39;$*&amp;amp;!@#$^)*(DSIJCH(*&amp;amp;@#&#39; ];

// 載入 session middleware
app.use(session(app));

// 每次連線就將計數器加一
app.use(function *(next) {
    if (this.session.counter)
        this.session.counter = 0;

    this.session.counter++;

    yield next;
});

router.get(&#39;/&#39;, function *() {
    // 回傳顯示計數器的值
    this.body = this.session.counter;
});

// 載入自訂的 router
app.use(router.middleware());
app.listen(3001);
&lt;/pre&gt;
&lt;br /&gt;
這範例會在瀏覽器每次連線時，把 session 內的計數器加一，所以若是我們重複整理這個頁面，會看到數字不斷增長。&lt;br /&gt;
&lt;br /&gt;
要注意的是，使用 session 前，我們要為 app.keys 設一組金鑰（Key）， koa-session 會使用這組 Key 加密我們的 session 資料。&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;
後記&lt;/h2&gt;
&lt;br /&gt;
還在使用 express 嗎？別老土了。（笑）</description><link>https://fred-zone.blogspot.com/2016/06/koa-10.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-3507045017222800385</guid><pubDate>Wed, 11 May 2016 15:02:00 +0000</pubDate><atom:updated>2016-05-11T23:04:11.313+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Container</category><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">Linux</category><category domain="http://www.blogger.com/atom/ns#">Maker</category><category domain="http://www.blogger.com/atom/ns#">MakerCup</category><category domain="http://www.blogger.com/atom/ns#">MT7688</category><category domain="http://www.blogger.com/atom/ns#">QEMU</category><category domain="http://www.blogger.com/atom/ns#">VM</category><category domain="http://www.blogger.com/atom/ns#">心得分享、講座</category><category domain="http://www.blogger.com/atom/ns#">物聯網</category><category domain="http://www.blogger.com/atom/ns#">自由軟體</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><title>MakerBoard: 自幹 MT7688 模擬器！簡報釋出！</title><description>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgClw8bosTOAUupms_bX0ixle6ah42RzaEA0wghH4-Zdic4AKcXt47ZDO8znLt9lISf_hyphenhyphen4PJlU4D5vo-VmXy4zW5b0J8hyphenhyphendMM7NL99xF1x4GXHYxaPoXezmFR7Guc5xNP-bvjN0SJcL-k/s1600/1*AeTTPrAvjUbvlu5NeR6nwg.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;480&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgClw8bosTOAUupms_bX0ixle6ah42RzaEA0wghH4-Zdic4AKcXt47ZDO8znLt9lISf_hyphenhyphen4PJlU4D5vo-VmXy4zW5b0J8hyphenhyphendMM7NL99xF1x4GXHYxaPoXezmFR7Guc5xNP-bvjN0SJcL-k/s640/1*AeTTPrAvjUbvlu5NeR6nwg.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
使用 MTK LinkIt Smart 7688 這類開發板時，總是很痛苦，由於儲存空間不大，記憶體也不大，常常在開發的過程中飽受折磨。於是我們開始思考如何可以在自己的電腦上，模擬一個 MT7688 的環境，在有充沛資源的機器上進行開發。就這樣，前陣子開發了一個小小的 Open Source 工具專案「MakerBoard」，並在 MakerCup 的共筆網站發表「&lt;a href=&quot;https://medium.com/maker-cup/%E6%B2%92%E6%9C%89%E6%9D%BF%E5%AD%90%E4%B9%9F%E5%8F%AF%E4%BB%A5%E7%8E%A9-7688-%E6%A8%A1%E6%93%AC%E5%99%A8-25cb7d082dd5&quot;&gt;沒有板子也可以玩的 7688 模擬器！&lt;/a&gt;」。&lt;br /&gt;
&lt;br /&gt;
雖然這樣一個小小的模擬器運用了 VM 和 Container 相關技術。但其實主要概念並不難，這次 5/10 在台大的開放原始碼課程中，就簡單從 MakerBoard 這專案出發，然後說明了一下怎麼樣自己打造一個簡單的 Container，並利用 QEMU 來進行 Binary Translation 的工作。&lt;br /&gt;
&lt;br /&gt;
簡報釋出，請自行服用：&lt;br /&gt;
&lt;br /&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;
&lt;iframe allowfullscreen=&quot;&quot; frameborder=&quot;0&quot; height=&quot;485&quot; marginheight=&quot;0&quot; marginwidth=&quot;0&quot; scrolling=&quot;no&quot; src=&quot;//www.slideshare.net/slideshow/embed_code/key/r4lBoh76FRmM6d&quot; style=&quot;border-width: 1px; border: 1px solid #ccc; margin-bottom: 5px; max-width: 100%;&quot; width=&quot;595&quot;&gt; &lt;/iframe&gt;&lt;/div&gt;
</description><link>https://fred-zone.blogspot.com/2016/05/makerboard-mt7688.html</link><author>noreply@blogger.com (Fred Chien)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgClw8bosTOAUupms_bX0ixle6ah42RzaEA0wghH4-Zdic4AKcXt47ZDO8znLt9lISf_hyphenhyphen4PJlU4D5vo-VmXy4zW5b0J8hyphenhyphendMM7NL99xF1x4GXHYxaPoXezmFR7Guc5xNP-bvjN0SJcL-k/s72-c/1*AeTTPrAvjUbvlu5NeR6nwg.png" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-4573380297488507253</guid><pubDate>Sat, 12 Dec 2015 16:10:00 +0000</pubDate><atom:updated>2015-12-13T00:10:43.733+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Hackathon Taiwan</category><category domain="http://www.blogger.com/atom/ns#">HanGee 國民機運動</category><category domain="http://www.blogger.com/atom/ns#">Maker</category><category domain="http://www.blogger.com/atom/ns#">MakerCup</category><category domain="http://www.blogger.com/atom/ns#">有趣新構思</category><title>從 Maker 出發並反思：於是我們成立了 MakerCup！</title><description>&lt;div style=&quot;text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjHNk7ZCmOFyyU6mPfSya7IVyQ_UFHoueggb3XMs51mgM8Asn5_pq5_t5-UoDPk9cEZdCp3MYYC33sbTzmQal6MAjTQ41ljv2FBpfc_7AhDwyZuUCpJ-rvO4PCBx-bqChBQ7vvkxmRxWEk/s1600/12345465_1013862918633878_7821304416010543559_n.jpg&quot; imageanchor=&quot;1&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;480&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjHNk7ZCmOFyyU6mPfSya7IVyQ_UFHoueggb3XMs51mgM8Asn5_pq5_t5-UoDPk9cEZdCp3MYYC33sbTzmQal6MAjTQ41ljv2FBpfc_7AhDwyZuUCpJ-rvO4PCBx-bqChBQ7vvkxmRxWEk/s640/12345465_1013862918633878_7821304416010543559_n.jpg&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
Maker 一詞近年來翻紅，有人稱「自造者」，有人稱「創客」，以代工起家的國內產業，覺得 Maker 風潮是一個維持舊有工業地位的方法和機會，更將其引伸成軟硬整合、創業模式，無一不紛紛出來插手，想佔一塊地，分一杯羹。有些媒體將 Maker 塑造成有專業技術能力的人們，彷彿與一般人有很大的鴻溝。種種因素，自然越來越多人不了解 Maker 是什麼了。&lt;br /&gt;
&lt;br /&gt;
但我們認為真正的 Maker 並不是擁有厲害能力的人，而是願意動手落實的人。&lt;br /&gt;
&lt;h2&gt;
為什麼我們要成立 MakerCup？&lt;/h2&gt;
我們想聚集純粹想動手、交流的朋友，並讓更多人參與並體驗 Maker 的世界。&lt;br /&gt;
&lt;br /&gt;
事實上，Maker 的定義很簡單，凡是能打造、做東西的人，都能稱為 Maker。做菜的廚師，是個 Maker；編織衣服的人，是個 Maker；畫家，也是個 Maker。當然，寫軟體、做電子電路的人，以及各種設計師，通通都算是 Maker。無論在什麼領域，Maker 精神強調的是動手去實現、完成，去參與過程、瞭解過程，進而讓自己更有能力去打造出更多創意十足的東西。更重要的是，在這種不設限的旅程，能讓我們都具備著跨領域思考的能力。&lt;br /&gt;
&lt;br /&gt;
既然過程才是最重要的，我們便開始思考怎麼樣讓更多人交流，交流技術、能力，共同發展和探討更多的知識。我們不應該只是追求一時且短暫的成果，滿足政府或代工產業想要立即成果的 KPI，更或是不應該鑽牛角尖盲目追求頂尖技能，而是讓更多人參與、動手，普遍瞭解更多不同的事物和技能。&lt;br /&gt;
&lt;br /&gt;
於是， MakerCup 這個社群出現了，每週四都會舉辦一場分享交流活動或是小聚會，讓 Maker 平日下班或閒暇時，可以來走走坐坐，輕鬆喝點小飲料，或是現場做點東西：&lt;br /&gt;
&lt;a href=&quot;https://www.facebook.com/groups/MakerCup/&quot;&gt;https://www.facebook.com/groups/MakerCup/&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
我們希望，這個社群將如一碗太古時代的生命濃湯一般，熬煮出真正的 Maker 生命。&lt;br /&gt;
&lt;h2&gt;
延續黑客松台灣的精神&lt;/h2&gt;
還記得這一年，我們籌辦了整個年度的「&lt;a href=&quot;https://hackathon.tw/&quot;&gt;黑客松台灣（Hackathon Taiwan）&lt;/a&gt;」，每個月都有 300 至 500 人的大型創作活動，讓不敢踏出來的年輕學子、上班感到無聊的人、及很少離開自己專業領域的朋友，走出來到活動上以「能力會友」。這一年的過程，讓大家的成果，從簡陋成長到真正的創意或產品，從簡單的技術到複雜的應用，從小設計到跨領域的整合。&lt;br /&gt;
&lt;br /&gt;
想當初，很多人剛開始嘲笑我們的成果都像玩具，勸我們不要再辦下去，請大家白吃白喝沒有意義。但一年以後的今天，事實證明我們是對的，黑客松台灣的參加者們，有最堅實的創意、能力和執行力，能解決各式各樣的問題，就算去號稱 Maker 聖地的中國深圳，也絲毫不遜色。&lt;br /&gt;
&lt;br /&gt;
更重要的是所有人都樂在其中，並把這份能力和喜悅，帶回原本的工作崗位上。&lt;br /&gt;
&lt;br /&gt;
同樣的精神，同樣的想法，我們一樣將在 MakerCup 落實。我們希望讓更多人來交流，讓更多人來學習動手，共同成長，而不將只是各式各樣的發表會而已。&lt;br /&gt;
&lt;h2&gt;
我們所見、期待的未來？&lt;/h2&gt;
&lt;div&gt;
從商業角度，許多傳統代工廠，在初面對 Maker 時，都誤將 Maker 當作了新的客人，期望 Maker 能產出點子、找到客人，然後下單。事實上，Maker 並不應該是代工廠下的消費者，而是橫向整合者，將不同領域、需求及客群，重新整頓和安排設計，然後創造出各種新的產業型態。而對於代工廠，精緻化並不再是唯一選擇，成為各行各業的技術供應者亦是一種出路。&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
所以我們相信，新的世代和市場潮流，不是築一道高牆，將 Maker 拒於專業的工廠門外，而是讓 Maker 視野做廣、扎根，讓大家愛上來台灣當一個 Maker，做出許多前所未有的成果或產品。未來，肯定會有更多的企業投入、民間組織投入，技術上也會有更多模組化解決方案，或是各類的知識交流，甚至是文化交流，來支撐這樣的整合性變革。&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
不可否認，在 Maker 的年代，什麼產業都將會是科技業，也都會是混血兒產業，誰能迎合這樣多族群共榮，誰就能在這時代中發光發熱。&lt;br /&gt;
&lt;h2&gt;
歡迎加入我們！&lt;/h2&gt;
MakerCup 社群是由黑客松台灣（Hackathon Taiwan）的部分成員共同推動的，感謝背後有更多合作單位或是朋友的陸續協助和資助，如黑客松台灣講師發起的創作學校「&lt;a href=&quot;http://letschool.com/&quot;&gt;LetSchool&lt;/a&gt;」、「聯發科 MediaTek」、「Seeed Studio」、「緯創 Wistron」、「台灣品牌協會」、「台灣土地開發」及「卡市達創業加油站」。不久的將來，還有「Node.js Party」、「IoT Taiwan 社群」、「MakerBot」或是「品酒社群」在這一望無際的場地裡當鄰居。&lt;br /&gt;
&lt;br /&gt;
更多需要感謝的朋友們，不勝枚舉，也歡迎更多人共襄盛舉這樣具有台灣風味的「圓山社群觀光夜市」。&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
不多說了，先來一杯 Maker 吧！&lt;/div&gt;
&lt;/div&gt;
</description><link>https://fred-zone.blogspot.com/2015/12/maker-makercup.html</link><author>noreply@blogger.com (Fred Chien)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjHNk7ZCmOFyyU6mPfSya7IVyQ_UFHoueggb3XMs51mgM8Asn5_pq5_t5-UoDPk9cEZdCp3MYYC33sbTzmQal6MAjTQ41ljv2FBpfc_7AhDwyZuUCpJ-rvO4PCBx-bqChBQ7vvkxmRxWEk/s72-c/12345465_1013862918633878_7821304416010543559_n.jpg" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-4609944251253991122</guid><pubDate>Sat, 31 Oct 2015 23:03:00 +0000</pubDate><atom:updated>2015-11-01T10:00:28.976+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">ECMAScript 6</category><category domain="http://www.blogger.com/atom/ns#">Flux</category><category domain="http://www.blogger.com/atom/ns#">Isomorphic</category><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">Node.js</category><category domain="http://www.blogger.com/atom/ns#">React</category><category domain="http://www.blogger.com/atom/ns#">Web</category><category domain="http://www.blogger.com/atom/ns#">Web Framework</category><category domain="http://www.blogger.com/atom/ns#">Web 相關技術</category><category domain="http://www.blogger.com/atom/ns#">Webpack</category><category domain="http://www.blogger.com/atom/ns#">心得分享、講座</category><category domain="http://www.blogger.com/atom/ns#">技術新知</category><category domain="http://www.blogger.com/atom/ns#">程式心得筆記</category><category domain="http://www.blogger.com/atom/ns#">自由軟體</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><category domain="http://www.blogger.com/atom/ns#">雲端(Cloud)</category><title>Lantern 專案：快速打造屬於自己的 Isomorphic 網站服務</title><description>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh-KgrATwrCHmk3QdnVlV5Bs6X2dzGVbo-qN4baq0u8YHKqePol-SRm7kEJJqT64xLIT4jO2mfPl8YLgKFszyOeSVUU0Qtgu6zFQx-Ge6DbnA6fRWCZwsALfqhRs35d1aHIK_lJVqwNpzs/s1600/lamp-halloween-lantern-pumpkin.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh-KgrATwrCHmk3QdnVlV5Bs6X2dzGVbo-qN4baq0u8YHKqePol-SRm7kEJJqT64xLIT4jO2mfPl8YLgKFszyOeSVUU0Qtgu6zFQx-Ge6DbnA6fRWCZwsALfqhRs35d1aHIK_lJVqwNpzs/s1600/lamp-halloween-lantern-pumpkin.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
話說，Isomorphic 一直是 Node.js 開發者的夢想，期望同一套程式碼前後端都可以使用，大幅簡化程式碼和加速開發。此外，動態網頁的 SEO 問題也可以同時獲得解決，許多效能問題也可以得到改善。但是，要實現 Isomorphic 的架構，有很多的問題得先解決，會花大量時間在前期工作上，往往讓許多開發者頭痛。&lt;br /&gt;
&lt;br /&gt;
儘管頭痛，仍然阻止不了大家往 Isomorphic 的世界前進，我也因此建立了一個專案「&lt;a href=&quot;https://github.com/cfsghost/lantern&quot;&gt;Lantern&lt;/a&gt;」，希望能讓更多人能以 Isomorphic 架構，快速建構出自己的網站服務，省去許多前期工作的時間。該專案是一個網站服務的樣板，實作了會員系統、權限管理、第三方登入、多國語系和送信機制等功能，在使用者介面上也做了一個還算美觀的介面。基本上，開發者只要 clone 下來，然後修改設定檔或改改介面、增加點功能，就可以快速完成一個屬於自己的全新網站服務。&lt;br /&gt;
&lt;br /&gt;
最特別的是，「&lt;a href=&quot;https://github.com/cfsghost/lantern&quot;&gt;Lantern&lt;/a&gt;」整合了現今所有最新的技術和概念，包括了 Koa、React、FLUX、ES6/7+、Webpack 以及 Semantic UI，大量運用了 Generator、class 及 decorator 等最新 JavaScript 語言特性來簡化設計。所以，如果你想要接觸最新的技術，完全可以透過修改「&lt;a href=&quot;https://github.com/cfsghost/lantern&quot;&gt;Lantern&lt;/a&gt;」專案來學習和熟悉。&lt;br /&gt;
&lt;br /&gt;
目前「&lt;a href=&quot;https://github.com/cfsghost/lantern&quot;&gt;Lantern&lt;/a&gt;」支援 Facebook 剛發佈的最新 React v0.14+ 和 react-router 1.0.0+，也避免使用像 redux 這類反 FLUX 原始設計的框架，讓原本熟悉 React 和 FLUX 架構的開發者，可以快速上手。也提供一些常見的 Extension，方便開發者寫出前後端通用的程式碼，大多數情況下，開發者不需思考程式碼運行在前端還是後端。&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;快速安裝使用&lt;/h2&gt;若想要使用「Lantern」，方式很簡單，先從 Github 取得程式碼：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;git clone git@github.com:cfsghost/lantern.git&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
安裝必要之 NPM 模組：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;npm install&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
使用 webpack 編譯專案（若要正式上線，可加上 -p 選項來編譯）：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;webpack&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
運行網站服務：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;node app.js&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
最後可以使用瀏覽器開啟網址，確認是否成功：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;http://localhost:3001/&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
&lt;h2&gt;修改設定檔&lt;/h2&gt;一般情況，你無需做任何設定就可以把服務跑起來，但如果你需要修改網站名稱、使用自己的第三方登入設定以及電子郵件伺服器，可以修改 Lantern 的設定檔。設定檔是 JSON 的格式，相當容易修改。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;ol&gt;&lt;li&gt;只要進入到「configs」目錄&lt;/li&gt;
&lt;li&gt;把「general.json.default」複製一份並更名為「general.json」&lt;/li&gt;
&lt;li&gt;修改「general.json」內的設定&lt;/li&gt;
&lt;li&gt;重啟服務&lt;/li&gt;
&lt;/ol&gt;&lt;br /&gt;
&lt;h2&gt;目錄架構&lt;/h2&gt;如果你想要開始客製化網站服務，需要先簡單理解「Lantern」的目錄架構。&lt;br /&gt;
&lt;div&gt;&lt;ul&gt;&lt;li&gt;src - 主要程式&lt;/li&gt;
&lt;ul&gt;&lt;li&gt;js - 頁面部分的程式&lt;/li&gt;
&lt;li&gt;img - 存放圖片&lt;/li&gt;
&lt;li&gt;less - CSS 原始碼&lt;/li&gt;
&lt;li&gt;translations - 存放多國語言的對應表&lt;/li&gt;
&lt;/ul&gt;&lt;li&gt;routes - 主要為 Restful API&lt;/li&gt;
&lt;li&gt;lib - 後端的相關函式庫（資料庫、第三方認證、發送電子郵件等功能）&lt;/li&gt;
&lt;li&gt;models - 資料庫 Schema&lt;/li&gt;
&lt;/ul&gt;&lt;/div&gt;&lt;br /&gt;
&lt;h2&gt;快速上手開發&lt;/h2&gt;首先記得，只要你修改了「src」底下的任何檔案，你必須重新執行「webpack」來進行編譯。或是可以跑一個「webpack -w」在背景，讓 webpack 在檔案有變更的時候自動重新編譯程式碼：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;webpack -w
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
一般來說，我們會從頁面修改和增減開始進行客製化工作。由於「Lantern」是採用 React 來繪製頁面，所有的頁面程式都將放在「src/js/components」底下，只要看到副檔名為「.jsx」的檔案，就分別是各種畫面上的元件。&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;建立新的頁面&lt;/h2&gt;建立頁面需要修改「src/js/routes.js」，加入一個網址及對應的頁面元件（以 Chatroom.jsx 為例）：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;module.exports = [
    // 省略 ...
    {
        path: &#39;/chatroom&#39;,
        handler: require(&#39;./components/Chatroom.jsx&#39;)
    }
];
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
接著可以建立「src/js/components/Chatroom.jsx」檔案，開始設計你的頁面。如果需要使用 FLUX 的機制，可以載入並引入「Lantern」所提供之 decorator 到你的 React 元件上：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;import React from &#39;react&#39;;
import { flux } from &#39;Decorator&#39;;

@flux
class MyComponent extends React.Component {
    constructor() {
        super();

        this.state = {
            messages: []
        };
    }

    componentWillMount() {
        this.flux.on(&#39;state.Chatroom&#39;, this.flux.bindListener(this.onChange));
    }

    componentWillUnmount() {
        this.flux.off(&#39;state.Chatroom&#39;, this.onChange);
    }

    onChange = () =&amp;gt; {
        var store = this.flux.getState(&#39;Chatroom&#39;);

        this.setState({
            messages: store.messages
        });
    }

    render() {
        return &amp;lt;div&amp;gt;{this.state.messages}&amp;lt;/div&amp;gt;;
    }
}

export default MyComponent;
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
&lt;h2&gt;開發自己的 Actions 和 Stores&lt;/h2&gt;假設你已經很了解 FLUX 的開發模式，你可以直接開始設計 Action 和 Store。對「Lantern」而言，無論是 Action 和 Store 都是一樣的東西，只不過執行的順序不一樣。&lt;br /&gt;
&lt;br /&gt;
建立 Action（放在 src/js/actions/chatroom.js）：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;export default function *() {
    this.on(&#39;action.Chatroom.say&#39;, function *(name, message) {
        this.dispatch(&#39;store.Chatroom.addMessage&#39;, name + &#39;:&#39; + message);
    });
}; 
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
建立 Store（放在 src/js/stores/chatroom.js）：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;export default function *() {
    // 初始化一個 state 用來存放 store 的資料
    var store = this.getState(&#39;Chatroom&#39;, {
        messages: []
    });

    this.on(&#39;store.Chatroom.say&#39;, function *(msg) {

        // 加入新訊息到 store
        store.messages.push(msg);

        // State(Store) 已經更新，React 元件會被觸發更新
        this.dispatch(&#39;state.Chatroom&#39;);
    });
}; 
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
最後在「actions/index.js」和「stores/index.js」分別載入新建立的 Action 和 Store：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;export default {
    // ...省略
    chatroom: require(&#39;./chatroom&#39;)
}; 
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
&lt;h2&gt;存取 Restful API&lt;/h2&gt;「Lantern」提供了統一的方法呼叫 Restful API，無論前端還是後端都可以使用（在 Store 或 Action 中），此外，如果在後端使用呼叫，該方法會自動接續使用者的 Session （登入）狀態，進行 Restful API 存取。使某些使用者登入後才可存取的 API，更為容易被存取。&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;export default function *() {
    this.on(&#39;store.Chatroom.getMessages&#39;, function *() {
        var store = this.getState(&#39;Chatroom&#39;);

        try {
            var res = yield this.request
                .get(&#39;/apis/messages&#39;)
                .query();

            // 取得聊天室訊息，並更新到 store
            store.messages = res.body;

            // State(Store) 已經更新，React 元件會被觸發更新
            this.dispatch(&#39;state.Chatroom&#39;);
        } catch(e) {
            switch(e.status) {
            case 500:
            case 400:
                console.log(&#39;Something\&#39; wrong&#39;);
                break;
            }
        }
    });
};
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
&lt;h2&gt;在畫 React 元件前先預載資料&lt;/h2&gt;後端要把畫面送到瀏覽器前，有時需要先資料庫的資料載入，預先植入畫面之中，前端有時也需要預先載入一些資料，以便畫面宣染時有實質內容。我們可以透過載入「@preAction」這個 decorator 來達成這個需求。「@preAction」會在元件初始化前，先去執行一些工作。&lt;br /&gt;
&lt;br /&gt;
底下範例是利用「@preAction」去跑 FLUX 裡的 Action - 「Chatroom.fetchMessages」：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;import { preAction } from &#39;Decorator&#39;;

// 相當於 this.flux.dispatch(&#39;action.Chatroom.fetchMessages&#39;)
@preAction(&#39;Chatroom.fetchMessages&#39;)
class MyComponent extends React.Component {
    // ...
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
當然可能要預先做的工作不只一項，而且可能要帶入 React 元件的 props 或更多資訊到 Action 中。「@preAction」可以被帶入函數，作更複雜的設計：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;@preAction((handle) =&amp;gt; {
    handle.doAction(&#39;Chatroom.fetchMessages&#39;);
    handle.doAction(&#39;Chatroom.doSomething&#39;, handle.props.blah, 123);
})
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
因為 Store 會因為「@preAction」而被更新、有資料，這時就可以理所當然地在元件初始化時直接取用 State（Store）的內容。&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;class MyComponent extends React.Component {
    constructor(props, context) {
        super();

        this.state = {
            messages: context.flux.getState(&#39;Chatroom&#39;).messages;
        };
    }
    // 省略 ...
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
&lt;h2&gt;動態載入 JavaScript 或 CSS 檔案&lt;/h2&gt;&lt;br /&gt;
很多 JavaScript 或 CSS 檔案是隨著 React Component 的載入，才會被動態載入，有時甚至需要照順序載入。此外，通常這樣的機制比較多會被使用在前端瀏覽器的頁面上，同樣的載入程式碼工作，在後端 Rendering 時往往會壞掉而無法通用，這在 Isomorphic 的架構中往往需要特別處理，像是判斷執行期是在前端還是後端，相當麻煩。&lt;br /&gt;
&lt;br /&gt;
為此，「Lantern」提供了「@loader」這個 Decorator，使開發者可以容易引入動態載入的機制，而且不用思考前後端的問題，也可以控制載入順序，或是等待檔案載入完成。&lt;br /&gt;
&lt;br /&gt;
以下範例就是一個載入地圖 API 的範例，載入工作只會在前端執行，不會在後端執行：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;import { loader } from &#39;Decorator&#39;;

@loader
class MyMap extends React.Component {

    componentWillMount() {
        // Loader 在後端不會有任何作用
        this.loader.css(&#39;https://example.com/css/test.css&#39;);
    }

    // componentDidMount 只會在前端觸發
    componentDidMount() {
        this.loader.css(&#39;https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.5/leaflet.css&#39;);
        this.loader.css(&#39;https://api.tiles.mapbox.com/mapbox.js/plugins/leaflet-minimap/v1.0.0/Control.MiniMap.css&#39;);

        this.loader.script([
            &#39;https://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.5/leaflet.js&#39;,
            &#39;https://api.tiles.mapbox.com/mapbox.js/plugins/leaflet-minimap/v1.0.0/Control.MiniMap.js&#39;
        ], function() {
            // 初始化地圖 ...
        });
    }

    render() {
        return &lt;div&gt;&lt;/div&gt;;
    }
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
&lt;h2&gt;取得和監聽視窗資訊&lt;/h2&gt;&lt;br /&gt;
為了更方便前端排版，尤其是需要滿版的設計時，我們往往需要得知或監控瀏覽器視窗的大小，通常做法是存取瀏覽器中的「window」物件，並監聽事件來達成。但「window」物件只在瀏覽器上存在，在後端如果存取該物件，會失敗而且有錯誤發生，在以往 Isomorphic 架構中，每次都要特別處理，相當麻煩。因此「Lantern」預設提供了一個名為「Window」的 Store，將這類資訊包裝起來，使 React Component 能輕易存取又不會因在後端或前端而出現問題。&lt;br /&gt;
&lt;br /&gt;
下面範例就是存取 Window 的例子，以及監聽視窗大小改變的事件。&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;@flux
class MyPage extends React.Component {
    constructor(props, context) {
        super();

        var win = context.flux.getState(&#39;Window&#39;);
        this.state = {
            winWidth: win.width,
            winHeight: win.height
        };
    }

    componentWillMount() {
        this.flux.on(&#39;state.Window&#39;, this.flux.bindListener(this.updateDimensions));
    }

    componentWillUnmount() {
        this.flux.off(&#39;state.Window&#39;, this.updateDimensions);
    }

    updateDimensions = () =&gt; {
        var win = this.flux.getState(&#39;Window&#39;);
        this.setState({
            winWidth: win.width,
            winHeight: win.height
        });
    }

    render() {
        return &lt;div&gt;{this.state.winWidth}x{this.state.winHeight}&lt;/div&gt;;
    }
}
&lt;/pre&gt;&lt;/div&gt;&lt;h2&gt;看不懂很多 ES6 和 ES7 的東西？&lt;/h2&gt;這邊已經整理了一些常用的對應表「&lt;a href=&quot;https://github.com/cfsghost/lantern/wiki/ES6-and-ES7&quot;&gt;ES6 and ES7&lt;/a&gt;」，方便開發者理解其中的語法。&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;更多文件和說明&lt;/h2&gt;&lt;div&gt;更多資訊可以參考 Github 上的 Wiki：&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://github.com/cfsghost/lantern/wiki&quot;&gt;https://github.com/cfsghost/lantern/wiki&lt;/a&gt;&lt;/div&gt;&lt;h2&gt;後記&lt;/h2&gt;其實「Lantern」已經改版了幾次，因為之前在好幾個要上線的專案上，每次都發現有些許不足之處，所以就不斷翻新架構和改進，甚至是優化效能。到目前為止，大致已經算是穩定的狀態，未來的開發方向不外乎是繼續寫 Isomorphic 的 Extension，以及效能優化。&lt;br /&gt;
&lt;br /&gt;
如果你有興趣，歡迎加入並共同改善這個專案。:-)</description><link>https://fred-zone.blogspot.com/2015/11/lantern-isomorphic.html</link><author>noreply@blogger.com (Fred Chien)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh-KgrATwrCHmk3QdnVlV5Bs6X2dzGVbo-qN4baq0u8YHKqePol-SRm7kEJJqT64xLIT4jO2mfPl8YLgKFszyOeSVUU0Qtgu6zFQx-Ge6DbnA6fRWCZwsALfqhRs35d1aHIK_lJVqwNpzs/s72-c/lamp-halloween-lantern-pumpkin.jpg" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-5483277502510441021</guid><pubDate>Wed, 30 Sep 2015 03:54:00 +0000</pubDate><atom:updated>2015-09-30T11:54:07.781+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Git</category><category domain="http://www.blogger.com/atom/ns#">程式心得筆記</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><title>Git 大哉問：如何為 Fork 出來的專案，同步上游的更新？</title><description>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://git-scm.com/images/logos/downloads/Git-Logo-2Color.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;165&quot; src=&quot;https://git-scm.com/images/logos/downloads/Git-Logo-2Color.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
搭配使用 Git 進行開發工作，時常會碰到一個狀況，就是我們 fork 一個專案出來修改，但在我們在修改的同時上游有了更新，這時我們會想要把上游的更新同步下來。這是一個常見的問題，許多人不時會提出來詢問，事實上如果你去 Google ，多半能找到這樣一篇名為「&lt;a href=&quot;https://help.github.com/articles/syncing-a-fork/&quot;&gt;Syncing a fork&lt;/a&gt;」的 Github 文件。雖然這篇文章已經把程序詳細列出來了，但還是有人看不太懂，原因是要搭配「&lt;a href=&quot;https://help.github.com/articles/configuring-a-remote-for-a-fork/&quot;&gt;Configuring a remote for a fork&lt;/a&gt;」這一篇文件一起看才知道來龍去脈。&lt;br /&gt;
&lt;br /&gt;
簡單來說，我們要先把「上游（upstream）」的 repository 加入我們眼前正在修改的專案，然後把上游更新拉回來，最後再與我們現有程式碼合併。&lt;br /&gt;
&lt;br /&gt;
首先，加入上游的 repository 並命名為「upstream」：&lt;br /&gt;
&lt;div&gt;
&lt;pre&gt;git remote add upstream https://github.com/YOUR_USERNAME/YOUR_FORK.git&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
未來想要更新前，可以使用 fetch 去拉上游的更新回來：&lt;br /&gt;
&lt;div&gt;
&lt;pre&gt;git fetch upstream&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
最後再把 upstream 的內容，與現有的正在修改的進行「合併」：&lt;br /&gt;
&lt;div&gt;
&lt;pre&gt;git merge upstream/master&lt;/pre&gt;
&lt;/div&gt;
</description><link>https://fred-zone.blogspot.com/2015/09/git-fork.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-8829706643037777692</guid><pubDate>Sat, 19 Sep 2015 20:13:00 +0000</pubDate><atom:updated>2015-09-20T04:13:41.537+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Flux</category><category domain="http://www.blogger.com/atom/ns#">io.js</category><category domain="http://www.blogger.com/atom/ns#">Node.js</category><category domain="http://www.blogger.com/atom/ns#">Web Framework</category><title>Fluky - 打造 Isomorphic App 的副產品：一個基於事件驅動的 Flux 框架</title><description>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://images.unsplash.com/photo-1436874555419-bb64221c5c1d?q=80&amp;amp;fm=jpg&amp;amp;s=4593f78e972ee577c98775d55b66a344&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;480&quot; src=&quot;https://images.unsplash.com/photo-1436874555419-bb64221c5c1d?q=80&amp;amp;fm=jpg&amp;amp;s=4593f78e972ee577c98775d55b66a344&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
要講到 Fluky 這一個 Flux 框架，這要從我的一個新計畫說起。因為最近又重新興起了一波 Isomorphic App 的熱潮，許多人開始打造了自己的 Isomorphic 網站，自己也做了一個。而什麼是 Isomorphic 呢？簡單來說，就是寫一次程式，然後前後端都可以使用的機制，也是一個網站服務工程師的夢想。還記得過去自己曾實作了 frex.js 試圖達成 API 層面的 Isomorphic，現在 React 這樣的前端框架，更提供了一個打造前後端 Rendering 的 Isomorphic，使得原本在前端動態產生的畫面，可以在後端產生，更一舉解決了 SEO 的問題。&lt;br /&gt;
&lt;br /&gt;
話說，搭上了這波熱潮，最近開始土炮自己的 Isomorphic App，Github 上開啟了一個「&lt;a href=&quot;https://github.com/cfsghost/lantern&quot;&gt;Lantern 燈籠&lt;/a&gt;」專案，希望做一個標準的專案架構，讓自己以後開發新專案時，可以不需要重新再來一遍。痛苦的是，與很多人一樣，踩到了很多地雷，在專案架構設計上，也一直有很多許要調整的地方，這也難怪，畢竟這是一個興新的開發概念。&lt;br /&gt;
&lt;br /&gt;
於是從零到有的過程中，也有許多副產品，其中包括了一個新的 Flux 框架「Fluky」。很多人問我為什麼不採用當今紅遍半天邊的「redux」，原因其實很簡單，我不想脫離傳統 Flux 模式和 React 開發的概念太遠，然後同時想要用試著更精簡的方式描述這些流程。另外一點是，受到過去 X11 這世界最先進的網路圖形化視窗系統的設計所啟發，打算試著全面使用「事件」來管理資料流和程式上任何的溝通。&lt;br /&gt;
&lt;br /&gt;
如果你有興趣，可以直接以 NPM 來安裝這個 Flux 框架：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;npm install fluky&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
&lt;h2&gt;Fluky 的設計&lt;/h2&gt;&lt;br /&gt;
基本上， Fluky 本身的概念很簡單，幾乎所有的行為都是透過 Fluky.dispatch() 這個 API 來進行，包括呼叫 Action、Store，然後所有的行為都可以使用 Fluky.on() 所監聽。也就是說，只需要這兩個 API，幾乎就已經足夠。對於 View 的工作而言，永遠就是呼叫 Fluky.dispatch(&#39;action.*&#39;) 和監聽 Fluky.on(&#39;store.*&#39;) 。&lt;br /&gt;
&lt;br /&gt;
這樣設計有什麼好處呢？因為所有的訊息和命令傳遞，都有統一的事件機制和命名規則，理論上來說，事件可以很容易被提到前端或是放在遠端被處理，這就有點像 X11 的設計，可遠端也可本地端進行圖形繪製處理。不過對 Fluky 來說，這目前還算太遠了，目前 Fluky 還沒有真正處理太過複雜的狀況，純粹就是以完全的事件化來處理資料流。&lt;br /&gt;
&lt;br /&gt;
也因為一切都事件化，就可以良好支援 Isomorphic 的設計，例如很多的 Action、Store 工作，可以有個前後端統一的命名和呼叫方法，在 Server 預處理，便於 Server Rendering 的使用，甚至是一部份在前端做，一部份在後端做都有可能。最重要的是，在 Isomorphic 上會碰到的前後端 state 不一致的狀況，也可以很容易使用事件、或是在事件分配中的空擋，進行 state 同步而獲得解決。&lt;br /&gt;
&lt;br /&gt;
此外，為了嘗試新技術，Fluky 也在前端引入了 Generator ，所以如果你想要使用 Fluky，要確保前端瀏覽器能使用 ECMAScript 6+ 最新的標準，或是你必須安裝 babel 模組來打包並轉換你的程式碼為 ES5。&lt;br /&gt;
&lt;br /&gt;
講了這麼多，怎麼使用 Fluky 呢？下面將以實作一個簡單的 TODO 清單為例。&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;建立 Action&lt;/h2&gt;&lt;div&gt;&lt;pre&gt;import Fluky from &#39;fluky&#39;;

Fluky.on(&#39;action.Todo.toggle&#39;, function *(todo) {
  // 用不同的 store 方法處理
  if (todo.completed)
    Fluky.dispatch(&#39;store.Todo.unmark&#39;, todo.id);
  else
    Fluky.dispatch(&#39;store.Todo.mark&#39;, todo.id);
});&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
&lt;h2&gt;建立 Store&lt;/h2&gt;&lt;div&gt;&lt;pre&gt;var todoStore = Fluky.getState(&#39;Todo&#39;, {
  todos: [
    {
      id: 1,
      name: &#39;寫一篇文章&#39;,
      completed: false
    }
  ]
});

Fluky.on(&#39;store.Todo.unmark&#39;, function *(id) {

  // 找到指定的 TODO 項目
  for (var index in todoStore.todos) {
    var todo = todoStore.todos[index];

    if (todo.id == id) {
      // 改為未完成
      todo.completed = false;

      // 發出 store 已改變的事件
      Fluky.dispatch(&#39;store.Todo&#39;, &#39;change&#39;);
      break;
    }
  }
});

Fluky.on(&#39;store.Todo.mark&#39;, function *(id) {

  // 找到指定的 TODO 項目
  for (var index in todoStore.todos) {
    var todo = todoStore.todos[index];

    if (todo.id == id) {
      // 改為完成
      todo.completed = true;

      // 發出 store 已改變的事件
      Fluky.dispatch(&#39;store.Todo&#39;, &#39;change&#39;);
      break;
    }
  }
});&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
&lt;h2&gt;在 React 元件內的使用&lt;/h2&gt;&lt;div&gt;&lt;pre&gt;import React from &#39;react&#39;;
import Fluky from &#39;fluky&#39;;

class TodoList extends React.Component {

  constructor() {

    // 取得 Todo 的 Store，從 Fluky 的 state 資料暫存區
    this.state = {
        todos: Fluky.getState(&#39;Todo&#39;).todos;
    };
  }

  componentDidMount() {
    // 監聽 store 的改變事件
    Fluky.on(&#39;store.Todo&#39;, Fluky.bindListener(this.onChange));
  }

  componentWillUnmount() {
    // 停止監聽 store
    Fluky.off(&#39;store.Todo&#39;, this.onChange);
  }

  onChange = () =&amp;gt; {

    // 當 store 有改變時更新元件的 state
    this.setState({
      todos: Fluky.getState(&#39;Todo&#39;).todos;
    });
  }

  toggle = (todo) =&amp;gt; {
    // 呼叫 Action 去切換工作項目狀態
    Fluky.dispatch(&#39;action.Todo.toggle&#39;, todo);
  }

  render: function() {
    var todoList = [];

    // 印出所有的工作項目
    this.state.todos.forEach((todo) =&amp;gt; {
      todoList.push(&lt;div onclick=&quot;{this.toggle.bind(this,&quot; todo=&quot;&quot;&gt;{todo.text}&lt;/div&gt;);
    });

    return (
      &lt;div&gt;      {todoList}
      &lt;/div&gt;    );
  }
}&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
&lt;h2&gt;State 資料暫存區的設計&lt;/h2&gt;傳統的 Flux 做法，不外乎是載入所要的 Store 檔案，來取得 Store 資料，這樣做不但麻煩且囉唆。既然事件分配器（Event Dispatcher）是 Singleton（只存在一個實例，所有人共用），將 Store 的資料共同管理顯然是比較簡單的做法，然後只需要統一使用 Fluky.getState() 就可以取得所需要的 Store 資料。 &lt;br /&gt;
&lt;br /&gt;
如果從前述範例來看，Fluky.getState() 可以帶兩個參數，第一個是 State 的名稱，第二個是當 State 不存在時，其預設值。&lt;br /&gt;
&lt;br /&gt;
當然，這個暫存區是可以整個取出來的，也可以使用 Fluky.setInitialState() 或是藉由 window.Fluky.state 在第一時間載入時整個放回去，這可以應用在解決 Isomorphic App 的前後端 Store 不同步的問題。&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;後記&lt;/h2&gt;&lt;br /&gt;
新專案「&lt;a href=&quot;https://github.com/cfsghost/lantern&quot;&gt;Lantern 燈籠&lt;/a&gt;」目標就是嘗試開發一個 Isomorphic 的網站服務，並使用最新的技術，此外，也希望開發一些基本功能（如：會員系統、第三方登入、權限管理等），方便日後開發新網站服務時，可以避免早期的苦工和踩地雷。這是一個 Open Source 專案，如果你有興趣，可以一同開發。:-)</description><link>https://fred-zone.blogspot.com/2015/09/fluky-isomorphic-app-flux.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-2137746301104626427</guid><pubDate>Fri, 28 Aug 2015 04:15:00 +0000</pubDate><atom:updated>2015-08-28T12:20:59.851+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">io.js</category><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">Node.js</category><category domain="http://www.blogger.com/atom/ns#">Web 相關技術</category><category domain="http://www.blogger.com/atom/ns#">程式心得筆記</category><title>Node.js 的單執行緒（Single Thread）設計，到底有什麼優點？</title><description>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://images.unsplash.com/uploads/1412198485051133af17f/5049dacb?q=80&amp;amp;fm=jpg&amp;amp;s=b8d7cc2e2818b3fde9aad29dfff63f02&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;425&quot; src=&quot;https://images.unsplash.com/uploads/1412198485051133af17f/5049dacb?q=80&amp;amp;fm=jpg&amp;amp;s=b8d7cc2e2818b3fde9aad29dfff63f02&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
這是個總有人一問再問的問題，到底 Node.js 這樣單執行緒（Single Thread）的設計，到底有什麼優點？為什麼總是有人說，它比傳統多執行緒的設計來得有效率？以往，一旦開始討論這個問題，總是會有人開始提到 Context Switch、Asynchronous 等機制，越講越玄也越講越複雜化。&lt;br /&gt;
&lt;br /&gt;
其實我們可以用簡單的餐廳比喻，就能理解 Single Thread 加上事件驅動（Event-driven）的機制，如何與傳統設計不一樣。&lt;br /&gt;
&lt;h2&gt;
場景想像&lt;/h2&gt;
試想一個場景：一間餐廳有 100 個座位，然後來了100個客人。&lt;br /&gt;
&lt;h2&gt;
處理方法&lt;/h2&gt;
身為老闆的你，你會選擇哪種方式服務這些客人：&lt;br /&gt;
&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;請100個服務生一對一服務這些客人。&lt;/li&gt;
&lt;li&gt;請 25 個服務生，看當下狀況服務這些客人。&lt;/li&gt;
&lt;/ol&gt;
一般來說，傳統的多 Multi-thread 的設計就類似方法&amp;nbsp;1，而 Single-thread 且 Event-driven 的設計就是方法&amp;nbsp;2。 &lt;br /&gt;
&lt;br /&gt;
通常大多數情況我們會選擇方法 2，因為客人通常都是處於等待（看菜單、等上菜、吃自己）的情況，並不需要服務生貼身服務。所以即便請 100 個服務生，這些服務生大多數時間也只是等在那也佔地方，而服務生眾多也導致服務生之間的溝通和互動其實不易，更不容易交換資源，回報和協同工作難以進行。反而安排一個小型的外場班，讓裡面的人合作見機行事，會比派出 100 個各自獨立的人來得好。&lt;br /&gt;
&lt;h2&gt;
併發數高的原因&lt;/h2&gt;
併發數（concurrent request）指的是單位時間內可以處理的要求量，一般用來評估一個網路應用程式的效能。而通常在網路服務裡，併發數也相當於單一時間內能服務的連線數量。&lt;br /&gt;
&lt;br /&gt;
所以，以前面餐廳外場班的模型來說，如果你有 100 個服務生，就可以服務 400 個客人。換句話說，同樣的資源，能處理的併發數（concurrent requests）也就比較高。&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;
後記&lt;/h2&gt;
不過如果你開的是酒店或按摩店，那可能就要請一百位服務生了。:-)</description><link>https://fred-zone.blogspot.com/2015/08/nodejs-single-thread.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-2728867736742164797</guid><pubDate>Mon, 13 Jul 2015 04:12:00 +0000</pubDate><atom:updated>2015-07-13T12:12:54.111+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">心得分享、講座</category><category domain="http://www.blogger.com/atom/ns#">心情筆記</category><title>Geek？技客？是什麼？我不宅，我用動手代替說話！</title><description>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://download.unsplash.com/reserve/9Fx2auQ4yoHM8OpWA8qw__MG_5265.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;426&quot; src=&quot;https://download.unsplash.com/reserve/9Fx2auQ4yoHM8OpWA8qw__MG_5265.jpg&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
因為我對外都自稱一個 Geek，所以時常有人問我，Geek 是什麼？一直以來，「技客（Geek）」個名詞讓許多人感到陌生，甚至對這詞彙一知半解的人，都以為 Geek 與普通宅宅無異，更甚至是覺得這只是另一種宅宅的說法。的確，Geek 某些地方與普通宅宅很相近，指的是醉心於特定專業或領域的人，但這些人擁有絕佳才能，只是為了鑽研知識或研究新事物可以幾乎荒廢其他事，廢寢忘食對他們來說只是小意思，甚至可以犧牲人際關係等一般正常社會交際行為。由此可以看出 Geek 對於自有興趣的事物，將會多努力去鑽研。&lt;br /&gt;
&lt;br /&gt;
不可否認，「技客（Geek）」這個詞在早期帶有貶意，但在現今社會中，人們交流的手段和方法已經透過科技有很大的改變，Geek 可以透過網路或各種新方法，找到能分享交流的同好朋友。透過網路串連，他們不再只是群體中被傳統主流排除在外的人，反而搖身一變，變成強力促使世界進步的主要群體。如今，大家眼中的 Geek ，代表著才能與努力，更代表能「動手完成任務」的傑出人們。開玩笑的說，專心致志（宅宅）加上真正動手實做的能力，就是「技客（Geek）」。&lt;br /&gt;
&lt;br /&gt;
想要成為一個 Geek 嗎？只要你符合這樣的條件，無論你原本是個技術 Hacker、Maker 還是個設計師，更甚至是任何領域的人，都可以稱自己是一個 Geek，甚至可以以此為榮！也許，你也有自己沒發現，但是能堪稱 Geek 的一面也說不定！&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;
後記&lt;/h2&gt;
為了讓更多 Geek 能聚集在一起交流，共同迸出創新的火花，我們 &lt;a href=&quot;https://hackathon.tw/&quot;&gt;Hackathon Taiwan&lt;/a&gt; 開始了一個新計畫 GeekBar I/O，將嘗試透過各種活動和方式，讓 Geek 們共同發光發熱！歡迎大家共襄盛舉！&lt;br /&gt;
&lt;br /&gt;</description><link>https://fred-zone.blogspot.com/2015/07/geek.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-8347686903679489608</guid><pubDate>Wed, 08 Jul 2015 12:28:00 +0000</pubDate><atom:updated>2015-07-08T20:31:11.450+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">ECMAScript 6</category><category domain="http://www.blogger.com/atom/ns#">io.js</category><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">Node.js</category><category domain="http://www.blogger.com/atom/ns#">Web</category><category domain="http://www.blogger.com/atom/ns#">Web 相關技術</category><category domain="http://www.blogger.com/atom/ns#">技術新知</category><category domain="http://www.blogger.com/atom/ns#">程式心得筆記</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><title>快樂玩 ES6 Generator，從 co 起手式開始</title><description>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://c2.staticflickr.com/8/7306/16407404782_8b9c57eab3.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;400&quot; src=&quot;https://c2.staticflickr.com/8/7306/16407404782_8b9c57eab3.jpg&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
自從 Node.js 0.12 版和 io.js 之後，大量的開發者開始了各自的 ECMAScript 6 大冒險，許多人對 Generator 的使用仍跌跌撞撞，對於這種看似「同步（Synchronous）」的「異步（Asynchronous）」機制，有許多人腦袋遲遲無法轉過來。雖然在小弟的書（參閱連結：&lt;a href=&quot;http://fred-zone.blogspot.tw/2015/04/nodejs.html&quot;&gt;新書報到！Node.js 模組參考手冊！&lt;/a&gt;）已經有清楚的說明 Generator 使用方法，但就許多讀者回函來看，對於 JavaScript 越是熟悉的人，越無法直觀理解 Generator 的思維，甚至是老是抓不准使用的時機點。&lt;br /&gt;
&lt;br /&gt;
尤其是過去我們已經有 Promise、async、Q 和 bluebird 等處理非同步程式流程的模組和工具，很多人就是覺得沒有使用 Generator 的必要。不過，如果你會使用 co 模組，你會突然發現若是將過去的流程機制與 Generator 相搭配，程式開發將變得更為流暢。&lt;br /&gt;
&lt;br /&gt;
若想要安裝 co 模組，可以直接以 NPM 下載：&lt;br /&gt;
&lt;div&gt;
&lt;pre&gt;npm install co&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
本文接下來會說明一些 co 的基本使用，破除一些 Generator 難以使用的地方，讓開發者們更容易開始 Generator 的旅程。&lt;br /&gt;
&lt;h2&gt;
讓原生 Generator 更好用&lt;/h2&gt;
這是很多人不喜歡使用 Generator 的主因，以往為了使用 Generator，我們還要先建立一個 Generator 的函數，然後不時的去處理 Generator 所返回的資訊，一遍又一遍進入 Generator 之中。因此，無論 Generator 再好用，這些麻煩的動作也完全抵銷他的優勢，大多數人還不如回到舊的流程控制方法，以免徒增自己的麻煩。&lt;br /&gt;
&lt;br /&gt;
而如果使用 co，可以直接將 Generator 函數當作「立即函數使用」，其餘的部份我們可以不需要擔心：&lt;br /&gt;
&lt;div&gt;
&lt;pre&gt;var co = require(&#39;co&#39;);

co(function *() {
&amp;nbsp; &amp;nbsp; console.log(&#39;Inside&#39;); 
}) &lt;/pre&gt;
&lt;/div&gt;
&lt;h2&gt;
再也不用煩惱 yield！&lt;/h2&gt;
以前光是 Promise，就已經讓很多人詬病，覺得每次使用 Promise 都要花許多時間對函數進行包裝，而 Generator 也有類似的問題，若是要使用 yield，更是一件大工程。於是，co 幫開發者做了些設計，讓 Generator 可以直接利用 yield 去跑 Promise 類型的函數、陣列等物件，因為是 yield，其執行模式是「異步」，不會阻塞事件引擎。&lt;br /&gt;
&lt;br /&gt;
co 支援 Trunks，也就是回傳一個簡單函數進行異步工作，範例如下：&lt;br /&gt;
&lt;div&gt;
&lt;pre&gt;var co = require(&#39;co&#39;);

function updateInfo() {

&amp;nbsp; &amp;nbsp; // 返回一個將被執行的函數 
&amp;nbsp; &amp;nbsp; return function(done) { 

&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // 可以執行各種異步工作
&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; setTimeout(function() {

&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // 完成工作後執行 callback，參數一為錯誤訊息，參數二為回傳值
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; done(null, &#39;Done&#39;); 
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }, 1000);
&amp;nbsp;&amp;nbsp;&amp;nbsp; };
}

co(function *() {
&amp;nbsp; &amp;nbsp; // 執行並等待回傳值 
&amp;nbsp; &amp;nbsp; var ret = yield updateInfo();

&amp;nbsp;&amp;nbsp;&amp;nbsp; // 一秒後收到回傳值並印出來
&amp;nbsp; &amp;nbsp; console.log(ret); 
});&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;h2&gt;
支援 Promise 的 yield&lt;/h2&gt;
如果你熟悉 Promise，這是個好消息， co 讓 yield 可以直接吃 Promise：&lt;br /&gt;
&lt;div&gt;
&lt;pre&gt;co(function *() {
&amp;nbsp; &amp;nbsp; var ret = yield Promise.resolve(true);
});&lt;/pre&gt;
&lt;/div&gt;
&lt;h2&gt;
直接使用 yield 跑一整個陣列的工作&lt;/h2&gt;
前面提到 co 讓 yield 也支援陣列，所以我們可以準備一系列的工作，放在陣列中讓 yield 去處理：&lt;br /&gt;
&lt;div&gt;
&lt;pre&gt;co(function *() {
&amp;nbsp; &amp;nbsp; var a = Promise.resolve(true);
&amp;nbsp; &amp;nbsp; var b = Promise.resolve(true);
&amp;nbsp; &amp;nbsp; var c = Promise.resolve(true); 
&amp;nbsp; &amp;nbsp; var ret = yield [ a, b, c ];
});&lt;/pre&gt;
&lt;/div&gt;
&lt;h2&gt;
異步處理一個陣列裡的所有元素&lt;/h2&gt;
這大概是以往 JavaScript 最頭痛的工作，後來有了 async 這類模組後，才比較容易處理，不然光使用 forEach 或是 for 迴圈來做，碰到較大的陣列，往往使程式阻塞卡死。如果使用 co 前面的起手式，我們可以試著這樣做（如果不使用 Promise）：&lt;br /&gt;
&lt;div&gt;
&lt;pre&gt;var arr = [ 1, 2, 3, 4, 5 ];

function handle(el) {

&amp;nbsp; &amp;nbsp; return function() {
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // 處理傳入值 
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; console.log(el); 
&amp;nbsp;&amp;nbsp;&amp;nbsp; };
} 

co(function *() {

&amp;nbsp; &amp;nbsp; // 在 Generator 裡，使用 yield 不會使 JavaScript 事件引擎阻塞，
&amp;nbsp;&amp;nbsp;&amp;nbsp; // 但因為程式流程會等 yield 結束，for 迴圈也不會造成阻塞
&amp;nbsp; &amp;nbsp; for (var index in arr) {
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; var el = arr[index];
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp; yield handle(el);
&amp;nbsp;&amp;nbsp;&amp;nbsp; }
});&lt;/pre&gt;
&lt;/div&gt;
&lt;h2&gt;
後記&lt;/h2&gt;
經過 co 包裝後的 Generator 非常好用，無論你過去習慣用哪一種流程控制的方式，都可以很好的轉換並整合過來。試試看吧！:-)</description><link>https://fred-zone.blogspot.com/2015/07/es6-generator-co.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-2597579011306879931</guid><pubDate>Sun, 24 May 2015 15:59:00 +0000</pubDate><atom:updated>2015-05-25T01:27:58.634+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Hackathon Taiwan</category><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">Node.js</category><category domain="http://www.blogger.com/atom/ns#">NodeSchool</category><category domain="http://www.blogger.com/atom/ns#">Web</category><category domain="http://www.blogger.com/atom/ns#">心情筆記</category><category domain="http://www.blogger.com/atom/ns#">有趣新構思</category><title>黑客松式的學習活動：NodeSchool International Day 精彩紀錄</title><description>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhgS3QaSpqjKSIJfPsoM7HUee2ljyLfYmR620DD90SzEngUIDNfQ27twrK_7fBCR6nEhcxxoKgDnp8BeVQlr7Jp3GLtjWsPPTah7LI0KcRNTKY47NtNybT-DwxRjos-bQoK0q8xB_iH4ps/s1600/DSC_0006.JPG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;360&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhgS3QaSpqjKSIJfPsoM7HUee2ljyLfYmR620DD90SzEngUIDNfQ27twrK_7fBCR6nEhcxxoKgDnp8BeVQlr7Jp3GLtjWsPPTah7LI0KcRNTKY47NtNybT-DwxRjos-bQoK0q8xB_iH4ps/s640/DSC_0006.JPG&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&amp;nbsp;『是不是要很厲害才能參加黑客松呢？』這是一個永遠都會有人問的問題。&lt;br /&gt;
&lt;br /&gt;
事實上，黑客松從一開始的出發點，就是三五好友聚在一起進行研究及開發的活動，帶有很純粹的動機。參加這樣的活動，你不必真的很厲害到極點，而最重要的是動手和夥伴合作，過程中可以順便認識朋友與他人交流，讓投入的過程中更加豐富有趣。&lt;br /&gt;
&lt;br /&gt;
從這樣的角度來看，『黑客松式的學習』是不是有可能的呢？如今看來是肯定的，5/23 於台北剛結束的 NodeSchool 聚會就是一場黑客松式學習活動，吸引了近兩百位參加者，共同參與了這場具有黑客精神的學習活動！&lt;br /&gt;
&lt;br /&gt;
順帶一提，這此使用的場地，也是 &lt;a href=&quot;http://hackathon.tw/&quot;&gt;黑客松台灣（Hackathon Taiwan）&lt;/a&gt; 每個月辦活動的主場地，無論如何，也記得來報名下次 6/13 - 6/14 （六、日）的黑客松活動！&lt;br /&gt;
&lt;h2&gt;
什麼是 NodeSchool？&lt;/h2&gt;
NodeSchool 是一個線上學校，目標是運用線上工作坊課程，讓所有人都可以在這學到各種技術。而恰逢 NodeSchool International Day 國際日活動，全球各地都紛紛各自舉行一場 NodeSchool 盛會，因此身在台灣的我們也理所當然響應了這樣的活動。&amp;nbsp; &lt;br /&gt;
&lt;br /&gt;
這個活動的形式相當特別，沒有講師在台上講課，而是採用線上工作坊教材與題目的形式，讓參加者在自己的電腦上挑戰通關。NodeScool 擁有許多工作坊教材，從基礎課程到進階的選修課程都有，除了 JavaScript、Node.js 之外，也有 ES6、WebGL、Three.js、Functional Programing  和 React.js 等課程。詳細的課程資訊，可以參考 &lt;a href=&quot;http://nodeschool.io/&quot;&gt;NodeSchool 官方網站&lt;/a&gt;。&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://github.com/sethvincent/javascripting/raw/master/screenshot.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;272&quot; src=&quot;https://github.com/sethvincent/javascripting/raw/master/screenshot.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
由於每一份教材都是經過設計，而且每一個主題和關卡都擁有提示和說明，參加者在一題題解答的過程中，可以靠自己慢慢學會一門技術。如果真的遭遇到了自己難以突破的困難，可以跟左右鄰居討論研究，若真的也沒有辦法了，可以找在現場自願的指導員（Mentor）幫忙排解問題。&lt;br /&gt;
&lt;h2&gt;
黑客松式的學習活動 &lt;/h2&gt;
回想起過去從小到大，我們的學習模式，總是有個老師在台上教學，枯燥而乏味，而為了保證教學進度，不免伴隨著填鴨式的教學方法。這樣的模式我們早就習慣，雖不見得喜歡，但也無可奈何，逐漸的，我們的思考和學習，慢慢僵化了。&lt;br /&gt;
&lt;br /&gt;
具有一定寫程式經驗的人都知道，如果想要把程式技術練好，動手研究是不可缺少的工作，而持續鑽研的熱情更是不可以缺少，學習並習慣自己動手解決一個個問題更是必要的訓練。此外，比起學習到一門技術，我們如何在學習和訓練的過程，培養自己自我學習和與其他人間的溝通交流，更為重要。與他人交換想法，或是藉由分享知識將自己所學內化，更是一種提昇自己的方式。&lt;br /&gt;
&lt;br /&gt;
因此，NodeSchool 活動與過去的學習方法最大不同，就是提供一個讓大家共同參與的形式，目標是要大家能在此透過自己動手學習到技術，也參與交流討論，當大家在活動結束後走出會場時，能帶著貨真價值的學習成果回家。&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEibmZSrfh9AlFJc6wxEIsDEp5Rz-b5V_Lky7dph79t8v2vk0AIOq3h_66TkejrmOWNKHuUqaMOeyrBLVvMc6AktCDDGDoFbwkmyHumBsba2WJhIY4VcFQEaVbabLBgKGSJ97r7t8G7CRCQ/s1600/DSC_0001.JPG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;360&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEibmZSrfh9AlFJc6wxEIsDEp5Rz-b5V_Lky7dph79t8v2vk0AIOq3h_66TkejrmOWNKHuUqaMOeyrBLVvMc6AktCDDGDoFbwkmyHumBsba2WJhIY4VcFQEaVbabLBgKGSJ97r7t8G7CRCQ/s640/DSC_0001.JPG&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;h2&gt;
沒有大神，只有迫切突破自我的歡樂參加者&lt;/h2&gt;
別以為這樣標榜學習的活動只有初學者前來，其實會眾裡臥虎藏龍，不乏有大神級別的高手參與。只不過，在這樣的活動中，即便是高手，也正煩惱自己手上的問題和課程，也需要更多高手一同討論和解決問題。&lt;br /&gt;
&lt;br /&gt;
由於 NodeScool 工作坊的主題相當廣泛，無論是初學者還是專家，都可以前來，然後挑戰一些自己感興趣的技術，然後號招在現場的高手們，一起討論並學習。我們可以說，NodeSchool 的活動，是一個兼具動手、討論交流和突破自我的學習活動。&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjjxWJ8gJE5_aVla_GS2rvvszHgV7PhPMvlI0XqJ3izVZpVX-ut2RJBSD_pit0GD9bLTKZ0iplyv3Jz_k7s8UyaIGzdoq-9taOlpqfOK984Wa6C6s_3S7wa74Bnt7iGson7c-4SCvE8cZs/s1600/DSC_0012.JPG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;360&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjjxWJ8gJE5_aVla_GS2rvvszHgV7PhPMvlI0XqJ3izVZpVX-ut2RJBSD_pit0GD9bLTKZ0iplyv3Jz_k7s8UyaIGzdoq-9taOlpqfOK984Wa6C6s_3S7wa74Bnt7iGson7c-4SCvE8cZs/s640/DSC_0012.JPG&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;h2&gt;
後記&lt;/h2&gt;
如果有人完成了課程，日後可以在參加 NodeSchool 時，自願擔任該課程的指導員（Mentor），如果還沒完成的人，可以回家繼續完成，若碰到問題，可以至 &lt;a href=&quot;https://github.com/nodeschool/taiwan/issues&quot;&gt;Github Issues&lt;/a&gt; 直接發問，或到每週四的 Node.js Party 及讀書會討論。日後，社群應該也會不定期的舉辦中小型的 NodeSchool 活動，讓大家共同參與。&lt;br /&gt;
&lt;br /&gt;
另外有趣的是，這次活動也有碰到一些企業派人來參與或獵才，像我就碰到 Foxconn 等公司的主管級人物前來參加活動，也一起下去研究程式。誰說主管只會動一張嘴呢？至少會來黑客松的主管也會動手！:-D</description><link>https://fred-zone.blogspot.com/2015/05/nodeschool-international-day.html</link><author>noreply@blogger.com (Fred Chien)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhgS3QaSpqjKSIJfPsoM7HUee2ljyLfYmR620DD90SzEngUIDNfQ27twrK_7fBCR6nEhcxxoKgDnp8BeVQlr7Jp3GLtjWsPPTah7LI0KcRNTKY47NtNybT-DwxRjos-bQoK0q8xB_iH4ps/s72-c/DSC_0006.JPG" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-9195735519040580512</guid><pubDate>Thu, 21 May 2015 15:57:00 +0000</pubDate><atom:updated>2015-05-21T23:57:36.667+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Hackathon Taiwan</category><category domain="http://www.blogger.com/atom/ns#">QML</category><category domain="http://www.blogger.com/atom/ns#">User Interface</category><category domain="http://www.blogger.com/atom/ns#">心得分享、講座</category><title>簡報釋出！超酷炫科幻 UI：QML 入門</title><description>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://cloud.githubusercontent.com/assets/252072/7604018/44697ee8-f96f-11e4-9690-db826fccbc22.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;478&quot; src=&quot;https://cloud.githubusercontent.com/assets/252072/7604018/44697ee8-f96f-11e4-9690-db826fccbc22.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
這次五月的 &lt;a href=&quot;https://hackathon.tw/&quot;&gt;Hackathon Taiwan&lt;/a&gt;， 因為原本的講師沒空，所以我就來救援了一場 QML 工作坊，這堂課以活動的倒數計時器為開頭，討論怎麼使用 QML 進行超酷炫科幻 UI 的設計，是一堂初學入門課程。&lt;br /&gt;
&lt;br /&gt;
有鑑於過去聽到不少人反應，要使用 QML 必須與 C/C++ 打交道，門檻相當高，於是，這個工作坊的目標，就是讓學員可以在純 QML 的環境下，以 QML 來開發自己的 UI 界面。即便是不熟悉程式語言的人，都可以輕易使用 QML 做出絢麗的 UI。&lt;br /&gt;
&lt;h2&gt;
活動簡報&lt;/h2&gt;
&lt;br /&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;
&lt;iframe allowfullscreen=&quot;&quot; frameborder=&quot;0&quot; height=&quot;355&quot; marginheight=&quot;0&quot; marginwidth=&quot;0&quot; scrolling=&quot;no&quot; src=&quot;//www.slideshare.net/slideshow/embed_code/key/MttB7jBhJqxktH&quot; style=&quot;border-width: 1px; border: 1px solid #CCC; margin-bottom: 5px; max-width: 100%;&quot; width=&quot;425&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;
&lt;h2&gt;
後記&lt;/h2&gt;
這次 QML 工作坊事後反應還算不錯，甚至有人以 QML 來開發黑客松活動的作品，未來如果有機會可以再次開課。:-)</description><link>https://fred-zone.blogspot.com/2015/05/uiqml.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-6096490026753189134</guid><pubDate>Wed, 06 May 2015 22:56:00 +0000</pubDate><atom:updated>2015-05-07T06:56:45.092+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">ECMAScript</category><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">Web 相關技術</category><title>JavaScript 的各種迷霧</title><description>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;http://upload.wikimedia.org/wikipedia/commons/9/99/Unofficial_JavaScript_logo_2.svg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://upload.wikimedia.org/wikipedia/commons/9/99/Unofficial_JavaScript_logo_2.svg&quot; height=&quot;320&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&amp;nbsp;在 Facebook 看到很多人都在整理自己的程式筆記，我也整理關於 JavaScript 語言常被誤解部份的筆記，然後分享出來供各位同好參考。&lt;br /&gt;
&lt;br /&gt;
 講實話，從 1995 年開始的 JavaScript，直到前幾年，都讓我恨的牙癢癢，甚至覺得他是個垃圾，因為當時的他太沒有規範，效能極差。直到近年來才逐步改善，我也才慢慢擺脫對他的排斥及恨意。&lt;br /&gt;
&lt;br /&gt;

 但就算已經有標準規範，JavaScript 語言對很多人來說依然很頭痛，除了其 Asynchronous 
非同步的機制外，更要面對其極為動態（Dynamic）的特性，而從 OOP 的角度來看，其 classless 的設計更讓人不知所措（ES6 
之後開始支援 Class），與傳統的 OOP 相去甚遠。所以，JavaScript 
處處與其他語言完全不一樣的概念及思維，讓很多人在裡面處處誤解。&lt;br /&gt;
&lt;br /&gt;
 所以若是想學習 JavaScript 深入的概念，最好的方式就是放下其他語言的既有概念，直接了解 JavaScript 的核心機制，雖然會讓人手足無措，但卻是最好的手段。否則，很容易練就或死背很多怪招，寫出難以除錯或過於複雜的程式碼，當然，更嚴重的是誤導他人。&lt;br /&gt;
&lt;br /&gt;

 要深入或精通 
JavaScript，務必了解並思考其執行程式時發生什麼事，每一行程式碼當下被執行時的狀態，遠遠比程式碼排列來得重要。只要掌握每一行程式碼的『執
行當下』，所有 JavaScript 的疑難雜症，都能迎刃而解。關於這一點，有興趣閱讀硬梆梆定義文件的，可以參考 ECMA-626 的『10.3
 Execution Contexts』一節。（參考網址：&lt;a href=&quot;http://l.facebook.com/l.php?u=http%3A%2F%2Fwww.ecma-international.org%2Fecma-262%2F5.1%2F%23sec-10.3&amp;amp;h=BAQEnYbrT&amp;amp;enc=AZNJ46mcPYcDCR0JHcRg39QqswQfhiLc8urz6TERftiJEYIE0XHIEPLNwP_SVB9Qwg41bpxGku8dDg2iaHg4On_OBIozYA4R6U4RcrQS8UuQmzUxpOHUefuWcYAUVPbcDbik1RUPQMrTQrG5gjTYqFEl774rRm2sedfpmdlxWNpQ2w&amp;amp;s=1&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;http://www.ecma-international.org/ecma-262/5.1/#sec-10.3&lt;/a&gt;）&lt;br /&gt;
&lt;h2&gt;
 關於 this 關鍵字的機制&lt;/h2&gt;
很多人會搞糊塗 this 關鍵字，就因為他是一個會被 Execution Contexts 大量影響的東西。JavaScript 在 
Function 或 Context 被執行時，才會依當下情況去決定 this 為何。這代表，使用 this 時在哪一個 function 
或地方不重要，而是當進入這個使用 this 的 context 時（如：Function 被執行時）才會決定 this 是什麼，this 
是什麼只跟你如何去呼叫 this 所在 Function，及 Context 內狀態的改變有關。關於 Function Call 怎麼決定 
this 值，可參考：&lt;a href=&quot;http://www.ecma-international.org/ecma-262/5.1/#sec-11.2.3&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;http://www.ecma-international.org/ecma-262/5.1/#sec-11.2.3&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
當然，純講理論很難懂，可以大概歸納出 this 的使用方式與情景，有開發者已經整理了很清楚的文件，以及常誤觸的陷阱，可參考：&lt;a href=&quot;https://www.facebook.com/l.php?u=https%3A%2F%2Fsoftware.intel.com%2Fzh-cn%2Fblogs%2F2013%2F10%2F09%2Fjavascript-this&amp;amp;h=DAQEi8Tl0&amp;amp;enc=AZMt5J5P435CsAOyiISiobUQcamNiXzCuMzswVDmNTdoBpAJ2Tclq7SlylBk6vmvYvr-bnXtvdA3RaCiVlFM-HLwxHSIxaQzACa6tUCCXrbzvFG1PfnJ4I7Zb35XvSqJ0qDdzKv6IBGDdltjdRPT1bj57rV4bSHKDxQ_k64SCdzHVg&amp;amp;s=1&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;https://software.intel.com/…/blo…/2013/10/09/javascript-this&lt;/a&gt;。&lt;br /&gt;
&lt;br /&gt;
若實在懶得看懂硬梆梆的文件，從實作面來看，就是記住簡單兩種規則而已。&lt;br /&gt;&lt;br /&gt;
 一般來說，決定 Function 內的 this 為何，說明如下：&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt; 只要直接呼叫 Function ，就會代 global 進去當 this，這也是為什麼 Browser 裡面會是 window，因為在 Browser 裡的 global 就是 window。&lt;/li&gt;
&lt;li&gt;如果一個 Function 處於一個 Object 上，像是 a.b() 這樣執行後，b 函數的內部會視 a 為 this。因為當 
JavaScript 在透過 a 物件存取底下的 Function 並呼叫他時，會自動把依附的 a 物件（Caller）帶入當 this。&lt;/li&gt;
&lt;/ol&gt;
&lt;br /&gt;
總歸來說，所有的 this 都是在執行期、呼叫當下所決定的，除非你使用 bind() 這類的做法事先去強制寫死他的 caller。這就能解釋，為什麼 this 總是不能往內部一層層自動傳進去，因為所有的 Function Call 都遵循著同樣的規則。&lt;br /&gt;
&lt;br /&gt;

 註：在嚴格模式下 Function 裡的 this 會是 undefined，不是他預設是 undefined，而是嚴格模式下呼叫一個 
Normal function 時，他不會自動代入 global，所以才是 undefined。在嚴格模式下不能取得 base value（即 
global），這點可參閱：&lt;a href=&quot;http://l.facebook.com/l.php?u=http%3A%2F%2Fwww.ecma-international.org%2Fecma-262%2F5.1%2F%23sec-8.7&amp;amp;h=_AQEPbW85&amp;amp;enc=AZMGzQ5iHXkAaUbMC3HMB5fba7uKFoX7iXTTNcDmuQEgtzexe25Gdj1t2VAK88dWzsfhL2Fu1g7PvUnwWxYAQ5KTt7FLtaiqFTNzUa9OlWxVsg5wHkOYfFZMe5S7pGeDUha3FglyaNo-aIP1nBPEQ0x08WEi10cN5EXwQVt2ySBQiQ&amp;amp;s=1&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;http://www.ecma-international.org/ecma-262/5.1/#sec-8.7&lt;/a&gt;&lt;br /&gt;
&lt;h2&gt;
 Function 的存在&lt;/h2&gt;
因為所有的型別都是 Object 的衍生物，甚至是 Function 也不例外，所以 JavaScript 裡的 Function 
物件可以隨機被動態生成和定義，與其他大多數語言完全不一樣。其他語言多半將 Function 
看做是一個寫死程式碼的區塊，放在一個固定的記憶體位置上，然後跳過去執行。而這樣可以動態定義 Function 的特性，導致 JavaScript
 天生就支援 Lambda 、匿名函數等特色，因為可以依照任何情況，重新排列組合，甚至生成 Function 的執行流程。&lt;br /&gt;
&lt;h2&gt;
 關於記憶體及物件管理機制&lt;/h2&gt;
JavaScript 的設計是以可動態擴充性為考量，所以也衍生出一套記憶體管理機制，以 Object 為基本記憶體管理單位。所有存取 
Object，都透過一套 『Identifier -&amp;gt; Value』 的機制，而在執行上 Value 其實就是 Object。&lt;br /&gt;
&lt;br /&gt;

 與其他語言不太一樣的是，JavaScript 在利用 var 宣告 variable 時，實際上是建立一個 
Identifier，並不是實體配置一塊物件記憶體。所以在 Identifier 
建立的剛開始，並未對應到實際有意義的參考物件。直到我們額外去建立物件後，才讓 Identifier 去對應。可以說，這就是 key-value 
的形式，而 Identifier 就是個 key。這部份可以參閱：&lt;a href=&quot;http://www.ecma-international.org/ecma-262/5.1/#sec-12.2&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;http://www.ecma-international.org/ecma-262/5.1/#sec-12.2&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
 由此可知， Identifier 本身不是 Object 的實體，因為他只是個 key 而已，就像 C/C++ 語言中的指標。&lt;br /&gt;
&lt;br /&gt;

 有趣的是，這樣 Reference 的機制，與 JavaScript object 中的 key-value 機制是一樣的，這也是為什麼 
Object 可以隨意掛勾到任何地方。因為 JavaScript 程式中，我們一定是透過 Identifier/key 去取得並使用 
Object，而且可能有很多不同的 Identifier/key 都指向同一個 Object。&lt;br /&gt;
&lt;h2&gt;
 記憶體回收&lt;/h2&gt;
簡單來說，JavaScript 記憶體回收，就是當 Object 沒有被任何 Identifier 指向時，就被視為垃圾而回收釋放記憶體。&lt;br /&gt;
&lt;h2&gt;
 new 運算子如何建立一個物件&lt;/h2&gt;
在定義上，用 new 建立物件要經歷過一些程序，甚至要檢查 Type 等工作，這部份的細節可參考原始定義：&lt;a href=&quot;http://www.ecma-international.org/ecma-262/5.1/#sec-11.2.2&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;http://www.ecma-international.org/ecma-262/5.1/#sec-11.2.2&lt;/a&gt;&lt;br /&gt;
 &lt;br /&gt;不過，對於大多數開發者來說，不需了解太多細節，較簡化的流程可參考 Mozilla 提供的文件：&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/new&quot; rel=&quot;nofollow&quot; target=&quot;_blank&quot;&gt;https://developer.mozilla.org/…/Jav…/Reference/Operators/new&lt;/a&gt;&lt;br /&gt;
 簡而言之，new operator 會去做到幾件事：&lt;br /&gt;
&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt; 用 Object.create() 等方法去參考 Function.prototype，建立一個物件&lt;/li&gt;
&lt;li&gt;跑 Constructor（也就是開發者定義的 Function）&lt;/li&gt;
&lt;li&gt;如果 Constructor 內沒有回傳一個物件，就以步驟 1 建立的物件回傳出來。&lt;/li&gt;
&lt;/ol&gt;
這一切工作是在『內部（internal）』完成，完成後回傳物件出來。&lt;br /&gt;&lt;br /&gt;
 如果你很清楚這些行為，可以把這些工作往外移，自己實作，並完全不用到『new 關鍵字』，但是通常開發者不會這麼做惡整自己。用 new 以後，等他回傳配置完成的物件，才是一般性的做法。</description><link>https://fred-zone.blogspot.com/2015/05/javascript.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-6503249873820246315</guid><pubDate>Sat, 18 Apr 2015 19:02:00 +0000</pubDate><atom:updated>2015-04-19T03:02:42.380+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">C/C++</category><category domain="http://www.blogger.com/atom/ns#">io.js</category><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">Node.js</category><category domain="http://www.blogger.com/atom/ns#">V8</category><category domain="http://www.blogger.com/atom/ns#">Web</category><category domain="http://www.blogger.com/atom/ns#">Web Framework</category><category domain="http://www.blogger.com/atom/ns#">Web 相關技術</category><category domain="http://www.blogger.com/atom/ns#">技術新知</category><category domain="http://www.blogger.com/atom/ns#">推薦好書</category><title>新書報到！Node.js 模組參考手冊！</title><description>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;http://2.bp.blogspot.com/-CtVw2usPwpI/VRlK3JyfCcI/AAAAAAAAEbA/sIBeI_tplhU/s1600/XW15035_%E5%B0%81%E9%9D%A23%E6%A0%A1-01.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://2.bp.blogspot.com/-CtVw2usPwpI/VRlK3JyfCcI/AAAAAAAAEbA/sIBeI_tplhU/s1600/XW15035_%E5%B0%81%E9%9D%A23%E6%A0%A1-01.jpg&quot; height=&quot;177&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
經過幾個月努力，新書『&lt;a href=&quot;http://www.kingsinfo.com.tw/module.php?i=item&amp;amp;tid=8230&quot;&gt;Node.js 模組參考手冊&lt;/a&gt;』終於誕生。這本新書與前一本『不一樣的 Node.js』稍稍不同，主要訴求不是初學者入門，而是參考用書，無論是新手還是已經很熟練 Node.js 的專家好手，都可以拿來參考，還希望各界多多支持！&lt;br /&gt;
&lt;br /&gt;
有鑑於 Node.js 發展迅速，其應用已經不只是被侷限在 Web 之上，甚至適合各種不同的領域與場景。所以，許多的功能及模組被大量開發出來，從網路應用、桌面應用甚至到嵌入式應用都可以使用 Node.js 來開發。&lt;br /&gt;
&lt;br /&gt;
可以應用在各種場景，代表就會有不少人面對 Node.js 數不清的模組時，總是不知道該怎麼辦，除了很難抉擇之外，更是有很多超出原本常識的模組存在。有些時候，更是需要將許多模組拼湊起來，才能完成一個特殊的功能。&lt;br /&gt;
&lt;br /&gt;
然而，大量的應用範例，也需要花大量時間才能一一摸索，若不是有人事先整理好，對於大多數人來說，無疑是永遠無緣觸碰。因此，這本書為此而生，希望透過大量整理各種應用範例，以減少 Node.js 開發者摸索的時間，加速學習或研究的速度，更甚至縮短產品開發的週期。&lt;br /&gt;
&lt;br /&gt;
如果你是初學者，你可以從中找到一些基本模組的完整範例參考，或是學習模組選擇的方法。如果你已經是很熟悉 Node.js 開發的專家，你可以從本書看到許多使用 Node.js 所開發的特異功能的應用範例，如：Bittorrent、SSH Server、演算法、加密、文件格式、多媒體支援等。此外，本書也將提及 C/C++ 原生模組和 V8 JavaScript 引擎的原理和範例，還有各種資料庫、Message Queue（訊息佇列）等常用的完整使用範例，當然也有今年最新的 io.js 相關內容。&lt;br /&gt;
&lt;br /&gt;
雖然說本書名為模組參考手冊，但整本書卻是以應用範例為導向，並非完全是一個個模組的介紹，有很多是多個模組整合而成的範例，許多部份都是網路上連原文文件都缺少的資訊或文獻，可以說是花大量時間和經驗的結晶。&lt;br /&gt;
&lt;br /&gt;
我只能說，希望讀者們會喜歡！希望能幫上大家工作開發上的忙！請大家多多支持！&lt;br /&gt;
&lt;h2&gt;
後記&lt;/h2&gt;
前一本『不一樣的 Node.js』，因為一些模組更新太快，所以有些章節已經有些老舊，如果你是前一本書的讀者，可以和新書『&lt;a href=&quot;http://www.kingsinfo.com.tw/module.php?i=item&amp;amp;tid=8230&quot;&gt;Node.js 模組參考手冊&lt;/a&gt;』做比對參考，因為裡面有雷同的章節，可以使用新書的方法來取代舊書老舊的部份。&lt;br /&gt;
&lt;br /&gt;
至於舊書翻版，我們已經跟出版社討論，短期內會將老舊的部份章節翻新，然後將這部份放上網路供讀者下載。然後會擬定計畫重新翻新整本書，引入 io.js 和 ES6 的部份，重新再出版。</description><link>https://fred-zone.blogspot.com/2015/04/nodejs.html</link><author>noreply@blogger.com (Fred Chien)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="http://2.bp.blogspot.com/-CtVw2usPwpI/VRlK3JyfCcI/AAAAAAAAEbA/sIBeI_tplhU/s72-c/XW15035_%E5%B0%81%E9%9D%A23%E6%A0%A1-01.jpg" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-9047018339948460772</guid><pubDate>Sat, 04 Apr 2015 05:24:00 +0000</pubDate><atom:updated>2015-04-04T13:30:29.275+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Hackathon</category><category domain="http://www.blogger.com/atom/ns#">Hackathon Taiwan</category><category domain="http://www.blogger.com/atom/ns#">心情筆記</category><category domain="http://www.blogger.com/atom/ns#">有趣新構思</category><category domain="http://www.blogger.com/atom/ns#">活動</category><category domain="http://www.blogger.com/atom/ns#">黑客松</category><title>精彩回顧！平民化的黑客松：Hackathon Taiwan</title><description>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiFaikcc8fsXhOQ_WegI14uCNRVXkjTZLxXaDOSivdku4wBgGnqCXm2HBL_28pCLg5tJtG4mZk-rPeMJtToF9PrKvk5MCV3V_EwH2Yf0py5NOC6jp3EEOLqPSmCWmzbJXfI54zEqUnmrNU/s1600/DSC_1204.JPG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiFaikcc8fsXhOQ_WegI14uCNRVXkjTZLxXaDOSivdku4wBgGnqCXm2HBL_28pCLg5tJtG4mZk-rPeMJtToF9PrKvk5MCV3V_EwH2Yf0py5NOC6jp3EEOLqPSmCWmzbJXfI54zEqUnmrNU/s1600/DSC_1204.JPG&quot; height=&quot;225&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
對很多人來說，黑客松（Hackathon）是一個可怕的詞彙，它神秘且陌生，似乎是只有軟體工程師能參與的活動。事實上，黑客松的本意只是希望大家可以齊聚一堂，跳出原本的生活圈，和其他人完成或創造一些與平常不一樣的東西。&lt;br /&gt;
&lt;br /&gt;
打造一個平民化的黑客松一直是 &lt;a href=&quot;https://hackathon.tw/&quot;&gt;Hackathon Taiwan&lt;/a&gt; 的目標，除了每月都舉辦推廣之外，為了讓活動更親民，還特意引入各種初學手把手的工作坊課程，以減少眾人對黑客松的恐懼，課程內容涵括了網站、軟體、硬體、設計、藝術領域，從 Arduino、Node.js、QML、前端、雲端服務、科技藝術一直到 3D 印表機等整合課程都有。為了讓更多人能學習更多更深入的技術，也陸續開設了不同類型的進階工作坊。無論是已經很厲害的人，還是沒有太多經驗的學生，都可以前來活動，找到自己覺得最舒服的位置，學習發自內心的動手去完成一件事。&lt;br /&gt;
&lt;br /&gt;
這活動的最終目的，是希望見到大家以能力會友，撞出火花，讓大家找到能互補的團隊成員，成就未來有潛力的團隊，實現更多具有影響力的創新。如我們一直所說，這是一個如太古般的生命濃湯，孕育著人才與新創火花。來黑客松能做出什麼成果，或許很難說，但你一定會得到很多的朋友與經驗！&lt;br /&gt;
&lt;br /&gt;
我們期待終有一天，當某個成功的新創公司被問到：『當初創辦人們是如何認識？』，他們會回答：『在 Hackathon 活動』。&lt;br /&gt;
&lt;h2&gt;
精彩回顧影片&lt;/h2&gt;
還不知道黑客松在做些什麼嗎？看看我們三月份兩天一夜活動的精彩回顧吧！&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;iframe allowfullscreen=&quot;&quot; class=&quot;YOUTUBE-iframe-video&quot; data-thumbnail-src=&quot;https://i.ytimg.com/vi/lZr6D11tl-g/0.jpg&quot; frameborder=&quot;0&quot; height=&quot;320&quot; src=&quot;http://www.youtube.com/embed/lZr6D11tl-g?feature=player_embedded&quot; width=&quot;480&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;
&lt;h2&gt;
下次活動訊息&lt;/h2&gt;
4/11-4/12 （六、日）兩天一夜的黑客松活動也開放報名了！完全免費！名額有限，快來報名吧！&lt;br /&gt;
&lt;br /&gt;
&lt;span style=&quot;background-color: white; color: #141823; font-family: helvetica, arial, &#39;lucida grande&#39;, sans-serif; font-size: 15px; line-height: 20px;&quot;&gt;議程頁面 ▶&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;https://hackathon.tw/agenda/agenda.html&quot; rel=&quot;nofollow&quot; style=&quot;color: #3b5998; cursor: pointer; font-family: helvetica, arial, &#39;lucida grande&#39;, sans-serif; font-size: 15px; line-height: 20px; text-decoration: none;&quot; target=&quot;_blank&quot;&gt;https://hackathon.tw/agenda/agenda.html&lt;/a&gt;&lt;br /&gt;
&lt;span style=&quot;background-color: white; color: #141823; font-family: helvetica, arial, &#39;lucida grande&#39;, sans-serif; font-size: 15px; line-height: 20px;&quot;&gt;報名頁面 ▶&amp;nbsp;&lt;/span&gt;&lt;a href=&quot;http://l.facebook.com/l.php?u=http%3A%2F%2Fwww.accupass.com%2Fgo%2Fhackathon06&amp;amp;h=DAQGo77iN&amp;amp;enc=AZPtGsstEBHjMTXlMMBiGJk78PyuEazIT98Dj3PzxFAzcrvAmev04FS1KrwmemZEYi773QyyBLX7z_qM0Tdrbp3jGjWwCiMaNq1pIKgnO5zjC8xG8NiY6ZCvcFegEnOtzJVBC8UT4uWx0UJ7EUR6KOGqo2BYIFnIfRabffDmPmuiTQ&amp;amp;s=1&quot; rel=&quot;nofollow&quot; style=&quot;color: #3b5998; cursor: pointer; font-family: helvetica, arial, &#39;lucida grande&#39;, sans-serif; font-size: 15px; line-height: 20px; text-decoration: none;&quot; target=&quot;_blank&quot;&gt;http://www.accupass.com/go/hackathon06&lt;/a&gt;&lt;br /&gt;
&lt;h2&gt;
社群訊息&lt;/h2&gt;
此外，如果你有興趣，可以加入 Hackathon Taiwan 的 Facebook 粉絲團，關注最新動態！&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://www.facebook.com/HackathonTaiwan&quot;&gt;https://www.facebook.com/HackathonTaiwan&lt;/a&gt;</description><link>https://fred-zone.blogspot.com/2015/04/hackathon-taiwan.html</link><author>noreply@blogger.com (Fred Chien)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiFaikcc8fsXhOQ_WegI14uCNRVXkjTZLxXaDOSivdku4wBgGnqCXm2HBL_28pCLg5tJtG4mZk-rPeMJtToF9PrKvk5MCV3V_EwH2Yf0py5NOC6jp3EEOLqPSmCWmzbJXfI54zEqUnmrNU/s72-c/DSC_1204.JPG" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-5300260327120653477</guid><pubDate>Sat, 21 Mar 2015 10:03:00 +0000</pubDate><atom:updated>2015-03-21T18:06:16.446+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">C/C++</category><category domain="http://www.blogger.com/atom/ns#">Hacking 心得筆記</category><category domain="http://www.blogger.com/atom/ns#">io.js</category><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">Node.js</category><category domain="http://www.blogger.com/atom/ns#">V8</category><category domain="http://www.blogger.com/atom/ns#">技術新知</category><category domain="http://www.blogger.com/atom/ns#">程式心得筆記</category><category domain="http://www.blogger.com/atom/ns#">自由軟體</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><title>NAN：Node.js 與 io.js 的 Native Addon 開發利器</title><description>自從 Node.js v0.11 版之後，內建的 V8 引擎被更新了，於是 JavaScript 引擎的原生 API 大幅度改變，導致很多以 C/C++ 所撰寫的原生模組紛紛出現相容性問題。影響範圍包括了前陣子發佈的 Node.js v0.12 以及 io.js 1.0+，因為都使用了新版的 V8 JavaScript Engine，而有同樣的問題。&lt;br /&gt;
&lt;br /&gt;
其實這樣的問題已經不是新鮮事，自從 Node.js v0.8 到 v0.10 就開始有些許不相容的問題，只是到了 v0.12 和 io.js 之後，出現了更多狀況。因此 Node.js 圈子內的知名開發者 &lt;a href=&quot;https://github.com/rvagg&quot;&gt;Rod Vagg&lt;/a&gt;&amp;nbsp;建立了 &lt;a href=&quot;https://github.com/rvagg/nan&quot;&gt;NAN&lt;/a&gt; 專案，用來解決這樣 Native API 不相容的問題。&lt;br /&gt;
&lt;br /&gt;
NAN 全名為 Native Abstractions for Node.js，目標是設計一系列通用 API 供 Native Add-on 開發者所使用，讓開發者可以使用這通用的 API，一次性開發出支援 v0.8、v0.10、v0.12 和 io.js 1.0+ 的原生模組，不必再為 V8 原生 API 相容性所苦。&lt;br /&gt;
&lt;br /&gt;
若想要使用 NAN，可以直接以 NPM 下載：&lt;br /&gt;
&lt;div&gt;
&lt;pre&gt;$ npm install nan&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
然後在 binding.gyp 中加入 include_dirs 的屬性設定，讓 Node.js 或 io.js 引入 NAN 模組：&lt;br /&gt;
&lt;div&gt;
&lt;pre&gt;&#39;include_dirs&#39;: [
&amp;nbsp; &amp;nbsp; &quot;&amp;lt;!(node -e \&quot;require(&#39;nan&#39;)\&quot;)&quot;
]&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
經過這樣的設定配置後，我們就可以在 C/C++ 程式裡面引入 NAN 的標頭檔，開始使用 NAN 的 API：&lt;br /&gt;
&lt;div&gt;
&lt;pre&gt;#include &amp;lt;nan.h&amp;gt;

NAN_METHOD(Hello) {
&amp;nbsp; &amp;nbsp; NanScope();
&amp;nbsp; &amp;nbsp; NanReturnUndefined();
&amp;nbsp; &amp;nbsp; // 或是這樣寫 NanReturnValue(Undefined());
}&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
上面的程式碼，代換成舊的版本（v0.8、v0.10），就等同於：&lt;br /&gt;
&lt;div&gt;
&lt;pre&gt;Handle&amp;lt;Value&amp;gt; Hello(const Arguments&amp;amp; args)
{
&amp;nbsp; &amp;nbsp; HandleScope scope;
&amp;nbsp; &amp;nbsp; return scope.close(Undefined());
}&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
若在新版的 Node.js v0.12 或是 io.js 之上，就等同於：&lt;br /&gt;
&lt;div&gt;
&lt;pre&gt;void Hello(const v8::FunctionCallbackInfo&amp;lt;Value&amp;gt;&amp;amp; args)
{
&amp;nbsp; &amp;nbsp; Isolate *isolate = Isolate::GetCurrent();
&amp;nbsp; &amp;nbsp; HandleScope scope(isolate);

&amp;nbsp; &amp;nbsp;&amp;nbsp;args.GetReturnValue().Set(Undefined());
}&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
我們可以看到，NAN 已經包裝成一個簡單的 API，方便開發者去移植或開發 Native Add-on。&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;
建立物件&lt;/h2&gt;
此外，NAN 也將許多物件類型的建立方法，利用 Template 統一成 NanNew() 這樣一個 API，例如：&lt;br /&gt;
&lt;div&gt;
&lt;pre&gt;Local&amp;lt;String&amp;gt; string = NanNew&amp;lt;String&amp;gt;(&quot;hello world&quot;);
Local&amp;lt;Boolean&amp;gt; boolean = NanNew&amp;lt;Boolean&amp;gt;(NanTrue());
Local&amp;lt;Number&amp;gt; number = NanNew&amp;lt;Number&amp;gt;(123);
Local&amp;lt;Array&amp;gt; number = NanNew&amp;lt;Array&amp;gt;();&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;h2&gt;
更多功能&lt;/h2&gt;
NAN 有更多的功能，有興趣的人可以參考專案上的說明：&lt;br /&gt;
https://github.com/rvagg/nan&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;
後記&lt;/h2&gt;
因應 io.js 的推出，最近開始搬移手上的 Native Module，包括了 node-dbus、 brig 以及其他種種模組，碰到 V8 API 大改，讓人手足無措，好在 NAN 已經解決了大多數的問題。其實不只是 V8 的更新，libuv 的更新也是問題之一，只不過問題還算小，日後有空再來筆記下來。</description><link>https://fred-zone.blogspot.com/2015/03/nannodejs-iojs-native-addon.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-2106693497091506944</guid><pubDate>Wed, 18 Mar 2015 02:00:00 +0000</pubDate><atom:updated>2015-03-18T10:00:46.376+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Hackathon</category><category domain="http://www.blogger.com/atom/ns#">Hackathon Taiwan</category><category domain="http://www.blogger.com/atom/ns#">HanGee 國民機運動</category><category domain="http://www.blogger.com/atom/ns#">Node.js</category><category domain="http://www.blogger.com/atom/ns#">QML</category><category domain="http://www.blogger.com/atom/ns#">User Interface</category><category domain="http://www.blogger.com/atom/ns#">有趣新構思</category><category domain="http://www.blogger.com/atom/ns#">活動</category><category domain="http://www.blogger.com/atom/ns#">程式心得筆記</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><category domain="http://www.blogger.com/atom/ns#">黑客松</category><title>Hackathon Taiwan 新倒數計時器系統</title><description>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhfLjTwMyIQksPpw4zs9H6ZQP_s-Gd4XP3A2-5vGGpRxLWQIcpZw9HEumasFumNrO83vR9iZDU_6SWxp9w6N2Ok3_KshRh8xKt5v2Y-1BKvaviboxMDKagMF89n9lv-TL7ASpECRMlyYCM/s1600/DSC_1346~2.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhfLjTwMyIQksPpw4zs9H6ZQP_s-Gd4XP3A2-5vGGpRxLWQIcpZw9HEumasFumNrO83vR9iZDU_6SWxp9w6N2Ok3_KshRh8xKt5v2Y-1BKvaviboxMDKagMF89n9lv-TL7ASpECRMlyYCM/s1600/DSC_1346~2.jpg&quot; height=&quot;225&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;
&lt;h2&gt;
話說&lt;/h2&gt;
如果你之前就有參加過我們的 Hackathon 活動，就會對從第一屆黑客松就有的倒數計時器有所記憶。這是我們特別為 Hackathon 活動所開發的倒數計時器，其著漂亮粒子特效，讓許多人印象深刻，尤其是投影在夜晚的牆壁和玻璃上，伴隨著台北 101 和夜空為背景，可說是非常炫麗又有氣氛。&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgKSr0GWoQmQvbA2kf8u-N8QVZp13ILb6LB1UxDAowvhWump7CGAYY_YT8TuuktZltvNGSYndga5KGc-ybbDcDUSvLKRqJqe4yMhnIuQpjXj21qHMSkpb83_UlTuoj9vJKfNA0cQdQS9s8/s1600/FB_IMG_1416578140172.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgKSr0GWoQmQvbA2kf8u-N8QVZp13ILb6LB1UxDAowvhWump7CGAYY_YT8TuuktZltvNGSYndga5KGc-ybbDcDUSvLKRqJqe4yMhnIuQpjXj21qHMSkpb83_UlTuoj9vJKfNA0cQdQS9s8/s1600/FB_IMG_1416578140172.jpg&quot; height=&quot;179&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;iframe allowfullscreen=&quot;&quot; class=&quot;YOUTUBE-iframe-video&quot; data-thumbnail-src=&quot;https://i.ytimg.com/vi/kqBPzIftdm8/0.jpg&quot; frameborder=&quot;0&quot; height=&quot;320&quot; src=&quot;http://www.youtube.com/embed/kqBPzIftdm8?feature=player_embedded&quot; width=&quot;480&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
但經歷過了四次活動，這個計時器也該功成身退了，隨著活動人數成長，活動內容及場地規劃，舊的計時器已經慢慢不敷使用，除了一身漂亮的動畫外，沒有太多的功能。而且，縱使有著一個看起來炫酷的特效，對工作人員和很多參加者來說，也慢慢膩了。&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
於是，我們又提起筆開始設計一個全新的倒數計時器系統，想設計一個更具科技、科幻感及現代感的介面，除此之外，更希望可以在未來可以引入更多的功能和互動機制，如導引、會場資訊查詢、甚至是直播等。就這樣，一個新的計時器誕生，並在 3/7 - 3/8 第五次的黑客松活動被使用。&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
因為我們期望這個計時器，不只是用來倒數計時，未來能變成一個多用途的活動系統，所以在設計上有更多的規劃及保留。也採用更多色塊來進行設計，讓畫面上有更多可利用的空間。&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhG6lGIc-_6tQMMbtZ67b9MzQYUNbN8s_2FWKLybeCY6v7YO9zop2UHJuODH6C2lbjFPq2cmyouymrpzhV6qa_mW6ujqvEe8CjKGeuaxZZBiCx8MbolKjbHZuQIOUdgZ3p8S2R3nSdLX6U/s1600/DSC_1347~2.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhG6lGIc-_6tQMMbtZ67b9MzQYUNbN8s_2FWKLybeCY6v7YO9zop2UHJuODH6C2lbjFPq2cmyouymrpzhV6qa_mW6ujqvEe8CjKGeuaxZZBiCx8MbolKjbHZuQIOUdgZ3p8S2R3nSdLX6U/s1600/DSC_1347~2.jpg&quot; height=&quot;225&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjfOLYQZ2QrGg35I0IAilnk_79_f1A0pCtV6-M1gqDcKog_4MDYU8bzRZqJsVyHi7AfJeZG-5144Hpt0e8e2VVBIy4vaXtuwYm-kr6yjDIYVAjQdfv0XV7g3XqMcyNi_XJ5XrQCUMSUDSo/s1600/DSC_1348~2.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjfOLYQZ2QrGg35I0IAilnk_79_f1A0pCtV6-M1gqDcKog_4MDYU8bzRZqJsVyHi7AfJeZG-5144Hpt0e8e2VVBIy4vaXtuwYm-kr6yjDIYVAjQdfv0XV7g3XqMcyNi_XJ5XrQCUMSUDSo/s1600/DSC_1348~2.jpg&quot; height=&quot;225&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjzEY-WMeqra7IUGy4xBYjUE1kEK8bIGFJTgVY_3KP6uVKrZKMMYKG1942FdKoihWaB499yLVwy3x9fUynWxmiyEVpqAcvTveQBd2R3q55TSlaOzS7v0vJhGdDaaIqNSaiJBPWGhUuh2ag/s1600/DSC_1280.JPG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em; text-align: center;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjzEY-WMeqra7IUGy4xBYjUE1kEK8bIGFJTgVY_3KP6uVKrZKMMYKG1942FdKoihWaB499yLVwy3x9fUynWxmiyEVpqAcvTveQBd2R3q55TSlaOzS7v0vJhGdDaaIqNSaiJBPWGhUuh2ag/s1600/DSC_1280.JPG&quot; height=&quot;225&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;h2&gt;
互動功能&lt;/h2&gt;
在這次活動中，我們觀察到了一個現象，那就是對正在專心進行創作及 Hack 的人，手上的工作雖樂趣無窮，但對於旁觀者來說，卻是無聊透頂，這讓黑客松（Hackathon）活動的現場，時不時會呈現一種沈悶的氛圍。此外，參加者的工作效率往往呈現著週期性，或是專心時間總有一個極限，適當的放鬆和中斷也是常見的情況。但是如果放鬆或中斷手上工作時，沒別的事可以舒展身心，這會感到相當無聊了，於是會看到有些人會選擇用吃東西來打發中間的時間。&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
其實，來黑客松除了動手做之外，人際關係經營及交流也是很重要的一環，但別人正在專心工作時，你不便打攪，尤其是當同隊的人都正在專注做事實，你剛好想要換個氣的時候，更不可能去煩自己的隊友。不過，雖然你的周圍可能沒人可以跟你互動，但你可以去找同樣想換氣的人產生互動，畢竟全場人這麼多，跟你處於同一個狀態的人總是有不少。&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
那麼，如何找到人可以跟自己互動或交流呢？&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
這次活動，我們建立了 IRC Channel ，讓參加者可以上來 IRC 聊天室來說說話。所以，如果你有興趣，除了活動時間之外，平時也可以加入我們的 IRC 聊天室掛著：&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
Server: irc.freenode.net&lt;br /&gt; Channel: #HackathonTaiwan&lt;/blockquote&gt;
&lt;/div&gt;
&lt;div&gt;
若是你不懂 IRC 客戶端程式如何使用，也有 Web 介面可以使用：&lt;br /&gt;
&lt;blockquote class=&quot;tr_bq&quot;&gt;
http://webchat.freenode.net&lt;/blockquote&gt;
&lt;/div&gt;
&lt;div&gt;
雖然我們已經嘗試引入聊天互動的機制，但我們發現仍不足夠，因為大部分的人仍然不知道有這樣的管道可以與其他人聊天及討論，或在活動中根本忘了可以使用這些服務。於是，為了讓更多人可以輕鬆地看到聊天室內的精彩訊息，我們在這次活動空閒時開發了一個新功能，將 IRC Channel 的訊息接到了新倒數計時器系統上，並畫了一個區域讓它顯示聊天內容。如此，參加者在休息或放鬆時，可以側頭看看周遭的計時器投影，順便看看聊天內容，相當有趣。&lt;br /&gt;
&lt;br /&gt;
此外，一旦有了這樣的功能，可以預見的是，參加者能在上面找隊友或跨團隊找人協助，或是打發不專心的時間，當然，更能拓展人際關係與交流，實踐黑客松的價值。&lt;br /&gt;
&lt;h2&gt;
展示影片&lt;/h2&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;iframe allowfullscreen=&quot;&quot; class=&quot;YOUTUBE-iframe-video&quot; data-thumbnail-src=&quot;https://i.ytimg.com/vi/lXTHnZ3kLlk/0.jpg&quot; frameborder=&quot;0&quot; height=&quot;320&quot; src=&quot;http://www.youtube.com/embed/lXTHnZ3kLlk?feature=player_embedded&quot; width=&quot;480&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;h2&gt;
程式原始碼&lt;/h2&gt;
身在這個開放的時代，提供程式原始碼是很正常的一件事。&lt;br /&gt;
&lt;br /&gt;
如果你對這支應用程式有興趣，可以從這取得程式原始碼，如果有什麼想法，也歡迎在 Github 上發 issue 或跟我們說：&lt;br /&gt;
&lt;a href=&quot;https://github.com/HanGee/Hankathon&quot;&gt;https://github.com/HanGee/Hankathon&lt;/a&gt;&lt;br /&gt;
&lt;h2&gt;
後記&lt;/h2&gt;
下次黑客松台灣（Hackathon Taiwan）活動預計是 4/11（六）、4/12（日），記得找朋友一起來參加！也歡迎加入我們的粉絲專頁，取得各種第一手訊息：&lt;br /&gt;
&lt;a href=&quot;https://www.facebook.com/HackathonTaiwan&quot;&gt;https://www.facebook.com/HackathonTaiwan&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
</description><link>https://fred-zone.blogspot.com/2015/03/hackathon-taiwan.html</link><author>noreply@blogger.com (Fred Chien)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhfLjTwMyIQksPpw4zs9H6ZQP_s-Gd4XP3A2-5vGGpRxLWQIcpZw9HEumasFumNrO83vR9iZDU_6SWxp9w6N2Ok3_KshRh8xKt5v2Y-1BKvaviboxMDKagMF89n9lv-TL7ASpECRMlyYCM/s72-c/DSC_1346~2.jpg" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-3128955157415765495</guid><pubDate>Mon, 09 Feb 2015 18:30:00 +0000</pubDate><atom:updated>2015-02-10T02:30:53.514+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Hackathon</category><category domain="http://www.blogger.com/atom/ns#">Hackathon Taiwan</category><category domain="http://www.blogger.com/atom/ns#">HanGee 國民機運動</category><category domain="http://www.blogger.com/atom/ns#">心情筆記</category><category domain="http://www.blogger.com/atom/ns#">活動</category><category domain="http://www.blogger.com/atom/ns#">黑客松</category><title>第四屆 Hackathon Taiwan 回顧！人才們的集散地！</title><description>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://d262ilb51hltx0.cloudfront.net/max/1400/1*tAQ0pTXBuPXFVq7aM_54bA.jpeg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;456&quot; src=&quot;https://d262ilb51hltx0.cloudfront.net/max/1400/1*tAQ0pTXBuPXFVq7aM_54bA.jpeg&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
每個月舉辦的台灣黑客松（Hackathon Taiwan），第四屆活動順利落幕！感謝大家的支持！無論你碰上什麼樣的隊友，有沒有完成作品，下次都歡迎再來！&lt;br /&gt;
&lt;br /&gt;
曾幾何時，許多人對黑客松活動已經感到麻痺，似乎就是一群躲在暗處的宅宅工程師聚集起來自我感覺良好的場合，不然就是許多新創團隊前來展示產品的地方。往往沒有機緣巧合參與的人，總是深怕自己能力不足而無法參與，也有人覺得來這裡看別人發表產品，相當無趣。人們逐漸忘了黑客松的原本出發點，也忘了捲起袖子動手的熱情，更忘了與人交流並相互扶持學習的愉悅。&lt;br /&gt;
&lt;br /&gt;
事實上，一個在大家心目中理想的黑客松，不外乎就是有得玩、有得學、有得做、有得交流，最後，能上台展示自我並取得成就感。這樣的氛圍，不同於補習班，也不同於一般制式學校，因為沒有成績壓力、沒有世俗壓力、更沒有人干預你，這更像是一個大學校園，參與者就在這樣的環境中自由發展，盡情做自己覺得有趣的事。&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;iframe allowfullscreen=&quot;&quot; class=&quot;YOUTUBE-iframe-video&quot; data-thumbnail-src=&quot;https://ytimg.googleusercontent.com/vi/lCUKDyjdtNk/0.jpg&quot; frameborder=&quot;0&quot; height=&quot;480&quot; src=&quot;http://www.youtube.com/embed/lCUKDyjdtNk?feature=player_embedded&quot; width=&quot;640&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;
&lt;br /&gt;
也許是教育體制的問題，也許是歷史文化所致，我們從小就沒有太多自由發展的機會，這讓不少人很晚才找到了自己的興趣。而進入社會後，缺少發自內心驅動的職場生活，讓更多人被生活所困，只能被世界及國內潮流被動的推著往前。我們有多久，沒有因學習新事物而開心？我們有多久，沒有因為與同伴們完成共同目標而感到興奮？那些再簡單不過的樂事，變成是種奢望。&lt;br /&gt;
&lt;br /&gt;
這也是為什麼，我們發起了這樣的黑客松活動，試圖喚起人們的童趣以及熱情，讓人們在這找回自我學習的熱誠，以及團隊合作的心情，更重要的是，能夠學習如何貢獻並展現自己。在我們的活動中，無論結果的作品如何，這些種種對一個好的人才來說，都是不可或缺的要素。因為我們認為，台灣的人才素質普遍不錯，只是尚未有好的方法展示出來，並引出其真正價值而已。&lt;br /&gt;
&lt;br /&gt;
共同持續參與，並逐漸改變這個社會氛圍，在這我們看見了人才們發光，也看見這片土地的希望。如果你問我說台灣黑客松每屆的活動能留下什麼？我會說，別的黑客松我不知道，但我知道我們的活動，讓人們帶回去的是提升後的自己與改變產業、社會的動力。&lt;br /&gt;
&lt;br /&gt;
也許你沒發現，但經歷過這一切後，你已經不一樣了。&lt;br /&gt;
&lt;h2&gt;
後記&lt;/h2&gt;
不同於普通的活動，籌劃黑客松更為累人，除了要照顧的是兩天一夜的會眾，還要規劃各類課程與營造氛圍，更不能為了財務壓力下與中心思想背道而馳，複雜程度完全不可同日而語。&lt;br /&gt;
&lt;br /&gt;
可愛的是，我們的參與者都是發自內心的願意主動參與，讓人非常欣慰。看到越來越多人熱衷於參加活動，讓我們更有信心一屆屆辦下去。&lt;br /&gt;
&lt;br /&gt;
感謝你們，不是單純的被正妹團、龍蝦與砸大錢的大魚大肉所吸引過來，而這就是廣告公司永遠無法理解的文化。</description><link>https://fred-zone.blogspot.com/2015/02/hackathon-taiwan.html</link><author>noreply@blogger.com (Fred Chien)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://img.youtube.com/vi/lCUKDyjdtNk/default.jpg" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-6363646407208411268</guid><pubDate>Wed, 28 Jan 2015 22:59:00 +0000</pubDate><atom:updated>2015-01-29T06:59:30.045+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">ECMAScript 6</category><category domain="http://www.blogger.com/atom/ns#">io.js</category><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">Node.js</category><category domain="http://www.blogger.com/atom/ns#">Web 相關技術</category><category domain="http://www.blogger.com/atom/ns#">技術新知</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><title>io.js 的 ES6 Collection 支援 -  Set</title><description>io.js 不做任何設定下，預設支援了一些 ECMAScript 6 的語法，其中包括一系列的 Collection 支援，這讓 JavaScript 在資料操作上多了一些方便之處。本文將討論到的 Set 物件，就是其中一個 Collection 類別。&lt;br /&gt;
&lt;br /&gt;
簡單來說，Set 是一個類似 Array 的物件型態，可以用於處理有序的資料，而且內建迭代器（Iterator：在某些應用上同樣功能也會稱之游標 Cursor），讓開發者可以很容易對資料進行一筆筆的處理。&lt;br /&gt;
&lt;h2&gt;基本操作&lt;/h2&gt;&lt;div&gt;&lt;pre&gt;基本的 Set 物件使用很簡單，如下所示：
// 建立一個新的 Set
var items = new Set();

// 依序加入資料
items.add(&#39;Fred&#39;);
items.add(&#39;Stacy&#39;);
items.add(&#39;Wesley&#39;);
items.add(&#39;Rance&#39;);
items.add(&#39;Kevin&#39;);
items.add(&#39;Alex&#39;);

// 依序印出 Set 物件內的所有資料
for (var item of items) {
&amp;nbsp; &amp;nbsp; console.log(item);
}&lt;/pre&gt;&lt;/div&gt;比較特別的是，如果要依序印出 Set 物件內的所有資料，需要使用『of』這個關鍵字。原因是 Set 物件本身是一個 Generator 實作所致，如果我們想要一次把 Generator 內的工作跑完，需要運用 for-loop 加上 of 關鍵字來達成。&lt;br /&gt;
&lt;br /&gt;
註：關於 Generator 與 of 關鍵字，日後有空再撰文說明其細節。總之，只要記得必須使用 of 關鍵字才能一一讀取 Set 物件內的資料，就像 Array 的 for-loop 加上 in 關鍵字。&lt;br /&gt;
&lt;h2&gt;刪除資料&lt;/h2&gt;刪除特定資料可以使用 remove() 方法來達成，如下所示：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;items.remove(&#39;Wesley&#39;);&lt;/pre&gt;&lt;/div&gt;若是要清空資料，可以使用 clear() 方法：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;items.clear();&lt;/pre&gt;&lt;/div&gt;&lt;h2&gt;檢查資料是否存在&lt;/h2&gt;我們可以直接利用 has() 方法檢查特定資料是否存在：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;items.has(&#39;Rance&#39;);&lt;/pre&gt;&lt;/div&gt;&lt;h2&gt;迭代器（Iterator）的使用&lt;/h2&gt;Set 的迭代器是圍繞著 Generator 來實作，所以當我們想要利用 Set 的迭代器，可以使用 values() 方法來取得一個新的迭代器，然後開始對資料進行一筆筆的操作：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;var iter = items.values();
// 印出 Fred
console.log(iter.next().value) ;
// 印出 Stacy
console.log(iter.next().value) ;&lt;/pre&gt;&lt;/div&gt;</description><link>https://fred-zone.blogspot.com/2015/01/iojs-es6-collection-set.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-1638676148779745056</guid><pubDate>Tue, 27 Jan 2015 18:54:00 +0000</pubDate><atom:updated>2015-01-28T02:54:56.308+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">ECMAScript 6</category><category domain="http://www.blogger.com/atom/ns#">io.js</category><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">Node.js</category><category domain="http://www.blogger.com/atom/ns#">技術新知</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><title>io.js 的文字範本（Template literals）支援</title><description>io.js 最大的特色，就是採用了最新的 V8 ，而且直接支援了 ECMAScript 6 的語法和特性，其中一項就是文字範本（Template Literals）的支援。這代表我們可以很容易在字串中代入其他的變數內容，就像一般的 Shell Script 一般，不必再像從前的方式，使用串接字串的方式達成。&lt;br /&gt;
&lt;br /&gt;
如果想要使用這樣的文字範本（Template Literals），可以直接使用 ${} 來代入其他變數，然後以『`』字元將字串包起來，然後就可以將指定的變數內容代入到字串中：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;var name = &#39;Fred&#39;;
var stringTemplate = `Hi, I am ${name}!`;

console.log(stringTemplate);&lt;/pre&gt;&lt;/div&gt;接著，如果你使用 io.js 來執行，就可以得到下列結果：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;Hi, I am Fred!&lt;/pre&gt;&lt;/div&gt;</description><link>https://fred-zone.blogspot.com/2015/01/iojs-template-literals.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-7563952113998401810</guid><pubDate>Sat, 24 Jan 2015 06:25:00 +0000</pubDate><atom:updated>2015-01-24T14:25:46.676+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Hackathon</category><category domain="http://www.blogger.com/atom/ns#">Hackathon Taiwan</category><category domain="http://www.blogger.com/atom/ns#">HanGee 國民機運動</category><category domain="http://www.blogger.com/atom/ns#">心情筆記</category><category domain="http://www.blogger.com/atom/ns#">活動</category><category domain="http://www.blogger.com/atom/ns#">黑客松</category><title>Hackathon Taiwan 今年計畫確定！</title><description>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjt5tFxjCsb3L7k9fPEJGpV_QwOz0nqrVeRFrhP5uYExhbawP38J9ENg9yCEzAE2rbTBdjkNEsnPWWfTDzjM2vSz-pDZMiMksRnwyDqi8eDa7Orv48umDou6_T1Zs-7acNmCjF_v63SOlI/s1600/DSC_0677.JPG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjt5tFxjCsb3L7k9fPEJGpV_QwOz0nqrVeRFrhP5uYExhbawP38J9ENg9yCEzAE2rbTBdjkNEsnPWWfTDzjM2vSz-pDZMiMksRnwyDqi8eDa7Orv48umDou6_T1Zs-7acNmCjF_v63SOlI/s1600/DSC_0677.JPG&quot; height=&quot;225&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
人才培育與還原、體現並轉化人才原始價值是我們的目標，這也是目前台灣產業界應該要努力的方向，所以我們希望組織 Hackathon 活動來達成這樣人才拓撲的願景。&lt;br /&gt;
&lt;br /&gt;
我們認為，黑客松的精神與文化需要被好好培養，也需要藉由持續且長久經營才會有意義，其體現的結果不只是聚在一起這樣簡單，而是動嘴、動手還要動腦的交流。因此，在大夥努力之下，Hackathon Taiwan 今年的黑客松計畫已確定，從這個月底開始，將每個月最少舉辦一次黑客松活動，年底前達成千人黑客松！（握拳）&lt;br /&gt;
&lt;br /&gt;
希望藉由黑客文化中親自動手的精神，將不同領域的人才收集起來，並長期持續提供創作氛圍，使參與者相互間產生巨大的化學反應，使創新的可能性有脈絡可循。也期待能將許多具有潛力但不得其門而入的人們，引導進入到這樣熱血的世界，累積更多經驗值。&lt;br /&gt;
&lt;br /&gt;
這樣的目標並不容易達成，自去年幾次的黑客松活動開始，從慘兮兮的幾個人，一直到現在的百人活動，各方面缺失逐漸補強完備，這途中經歷過許多困難，夥伴們也自掏了不少腰包，好不容易才得以存活並開始快速成長。感謝一些企業們的長期關注與投資，以及貴人大力相助，使我們陸續找到可以支撐整個計畫的資源、場地與合作機會，相信今年會是起飛的一年！&lt;br /&gt;
&lt;br /&gt;
今年才剛開始，我們仍有很多事要做，也需要更多的資源與贊助，以及工作人員，畢竟，每個月都要籌劃一場數百人、至上千人齊聚一堂『微創業』的黑客松活動，實屬不易。所以，如果您願意支持並協助我們，請務必與我們聯絡！&lt;br /&gt;
&lt;br /&gt;
我們也歡迎學生或各種單位與我們合作舉辦活動，相互連結，共享成果。如果你想在學校推廣黑客松，也歡迎加入我們的團隊，或與我們一同籌劃。&lt;br /&gt;
&lt;br /&gt;
期許，我們從這個土地上出發，立足在這個現今世界的核心位置上，能使信念綿延拓撲到全亞洲，甚至是全世界，讓此地變成人才們的朝聖之地。:-)&lt;br /&gt;
&lt;br /&gt;
更多資訊，歡迎參考 Hackathon Taiwan 的官方網站：&lt;br /&gt;
&lt;a href=&quot;http://hackathon.tw/&quot;&gt;http://hackathon.tw/&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;</description><link>https://fred-zone.blogspot.com/2015/01/hackathon-taiwan.html</link><author>noreply@blogger.com (Fred Chien)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjt5tFxjCsb3L7k9fPEJGpV_QwOz0nqrVeRFrhP5uYExhbawP38J9ENg9yCEzAE2rbTBdjkNEsnPWWfTDzjM2vSz-pDZMiMksRnwyDqi8eDa7Orv48umDou6_T1Zs-7acNmCjF_v63SOlI/s72-c/DSC_0677.JPG" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-6660595433751631505</guid><pubDate>Thu, 22 Jan 2015 22:49:00 +0000</pubDate><atom:updated>2015-01-24T01:09:08.766+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">ECMAScript 6</category><category domain="http://www.blogger.com/atom/ns#">io.js</category><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">Koa</category><category domain="http://www.blogger.com/atom/ns#">Node.js</category><category domain="http://www.blogger.com/atom/ns#">Web Framework</category><category domain="http://www.blogger.com/atom/ns#">Web 相關技術</category><category domain="http://www.blogger.com/atom/ns#">程式心得筆記</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><title>Koa 的非同步流程設計</title><description>Koa 採用了 Generator 來控制非同步流程，所以在採用 Koa 的程式碼上，我們會看到很多 yield 關鍵字的出現。某程度來說，Generator 讓程式碼變得乾淨許多，也平坦許多，比較少出現太複雜的 Callback 高山。這對 JavaScript 來說是件不錯的事，只是，對很多開發者來說，需要點時間熟悉其概念。&lt;br /&gt;
&lt;br /&gt;
總而言之，所有 yield 的關鍵字，一定只會出現在 Generator 當中，而一個 Generator 長得會是一個以『*』符號開頭的函數模樣：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;function *() {
&amp;nbsp; &amp;nbsp; // 工作流程
}&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
所以若是你仔細觀察，就會發現 Koa 的路由處理，採用的不是 callback 來處理客戶端要求，而是 Generator：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;router.get(&#39;/test&#39;, function *() {
&amp;nbsp; &amp;nbsp; // 處理客戶端要求的工作
});&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
其實不久前，舊文『&lt;a href=&quot;http://fred-zone.blogspot.tw/2015/01/koa-long-polling.html&quot;&gt;如何於 KOA 實作長輪詢（LONG-POLLING）機制&lt;/a&gt;』已提及怎麼來掌控 Koa 的流程，雖然只是簡單提及，但已經大略展示其使用方法。簡單來說，你可以當作這個 Generator 結束後，與客戶端的連線就會結束，所以我們如果要處理非同步的工作，也必須避免和阻止這個 Generator 執行完。&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;使用 yield 控制 Generator 的流程&lt;/h2&gt;為了阻止 Generator 一下就跑完，我們會使用 yield 方法來暫停 Generator 的執行，並等待一個需要花時間的非同步工作完成。如果你對舊文的內容有印象就會知道，想要使用 yield 在 Koa 中去呼叫一個非同步機制，就需要設計一個特別的函數，其函數有一個 callback，當這個 callback 被呼叫時，代表工作完成。&lt;br /&gt;
&lt;br /&gt;
如下面範例就是想要執行一個非同步函數 setTimeout()，並等待其執行完成。在這範例中，我們加上了 console.log() 印出 START 和 END 字串，便於觀察其行為：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;router.get(&#39;/test&#39;, function *() {

&amp;nbsp; &amp;nbsp; console.log(&#39;START&#39;);

&amp;nbsp; &amp;nbsp; // 暫停 Generator 並等待工作完成
&amp;nbsp; &amp;nbsp; yield function(done) {
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; setTimeout(function() {
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; console.log(&#39;TIMEOUT&#39;);

&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // 完成，呼叫 callback 告訴 Generator 可以繼續 yield 以後的工作
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; done();
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }, 1000);
&amp;nbsp; &amp;nbsp; };

&amp;nbsp; &amp;nbsp; console.log(&#39;END&#39;);
});&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
依照以上的例子，如果你用瀏覽器發送要求後，可以在 Server 端馬上看見 START 被印出來，然後經過一秒後會先出現 TIMEOUT 再出現 END。&lt;br /&gt;
&lt;br /&gt;
很多人可能會覺得頭痛，因為照過去 JavaScript 的非同步概念來看，順序應該是要 START、END 然後等待一秒鐘後才是 TIMEOUT。但在 Generator 中，使用了 yield 以後並不是如此，所以你可以把它看作是一個在 JavaScript 中的特殊領域，以同步化（Synchronous）會阻塞（Block）的概念來進行邏輯設計的存在。&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;錯誤處理&lt;/h2&gt;JavaScript 一直以來，最讓人詬病的就是非同步機制的錯誤處理很麻煩，也很囉唆。在使用 Generator 之後，我們可以直接用 try-catch 的方法進行錯誤處理。如下所示：&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;try {
&amp;nbsp; &amp;nbsp; yield function(done) {
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; setTimeout(function() {
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // 拋出錯誤
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; done(new Error(&#39;WRONG&#39;));
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }, 1000);
&amp;nbsp; &amp;nbsp; }
} catch(err) {
&amp;nbsp; &amp;nbsp; // 處理錯誤
&amp;nbsp; &amp;nbsp; console.log(err);
}&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Koa 的設計上，只要將錯誤代入到 callback 的第一個參數，使第一個參數不是 null，就會拋出錯誤，讓 yield 外的 try-catch 去攔截，如此就可以很容易來處理非同步機制的錯誤。&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;從 yield 得到回傳結果&lt;/h2&gt;如果已經瞭解了 yield 的使用方式和邏輯，我們也可以等待非同步工作將一些結果回傳，就像下面的使用方式：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;var result = yield function(done) { ... }&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
在 Koa 中，確實作法只需要代入想要回傳的資料到 callback 的第二個參數即可：&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;var result = yield function(done) {
&amp;nbsp; &amp;nbsp; setTimeout(function() {
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // 拋出錯誤
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; done(null, &#39;hello&#39;);
&amp;nbsp; &amp;nbsp; }, 1000);
}
console.log(result);&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
&lt;h2&gt;包裝成更好用的函數&lt;/h2&gt;我們固然可以直接使用匿名函數，讓 Generator 去執行，但這可能不是一個好的做法，多數情況，我們還是會將功能包裝成 API 函數的形式，增加重複利用的機會，如下：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;yield delay(3000);&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
如果你很熟悉 JavaScript 的開發，應該已經知道怎麼做，邏輯如前面所提及的範例一樣，只是額外進行一層包裝罷了：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;function delay(interval) {
&amp;nbsp; &amp;nbsp; return function(done) {
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // 照 interval 變數所給的數值，決定暫停時間長度
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; setTimeout(done, interval);
&amp;nbsp; &amp;nbsp; };
}&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
&lt;h2&gt;後記&lt;/h2&gt;Generator 是一個會讓傳統 JavaScript 開發者抓狂的東西，不過 Koa 對其已經做了初步的包裝，只要熟悉 callback 的使用，都不會有太大問題。但這也代表 Koa 中所用到的 Generator 機制，和原始的 Generator 使用方法會有些差別，最大差別就是沒有 callback 的設計，得自己實作。&lt;br /&gt;
&lt;br /&gt;
所以，如果你是一個 Generator 的初學者，建議先不要直接學習原始 Generator 的使用方法，可以藉由 Koa 先熟悉並瞭解 Generator 的邏輯。等到都熟悉了 Generator 的概念後，再開始學習怎麼自行實作如同 Koa 的 callback。&lt;br /&gt;
</description><link>https://fred-zone.blogspot.com/2015/01/koa.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-5230159588738380175</guid><pubDate>Wed, 21 Jan 2015 21:33:00 +0000</pubDate><atom:updated>2015-01-23T03:04:28.563+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">ECMAScript 6</category><category domain="http://www.blogger.com/atom/ns#">Express Web Framwork</category><category domain="http://www.blogger.com/atom/ns#">Generator</category><category domain="http://www.blogger.com/atom/ns#">io.js</category><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">Koa</category><category domain="http://www.blogger.com/atom/ns#">Node.js</category><title>如何於 Koa 實作長輪詢（Long-polling）機制</title><description>io.js 的到來以及即將釋出的 Node.js v0.12，意味著我們不能再忽視 ECMAScript 6 的存在，更代表著 Koa Web Framework 開始要正式進入到市場了（如果你對 Koa 有興趣，可以參考『&lt;a href=&quot;http://fred-zone.blogspot.tw/2014/12/hackathon-koa.html&quot;&gt;舊文&lt;/a&gt;』的簡報內容，有說明完整的開發方法，也有說明 Generator 的使用方法）。Koa 大量運用到 ECMAScript 6 新的 Generator 支援，簡化了非同步的機制，更減少了 Callback 的使用場景。&lt;br /&gt;
&lt;br /&gt;
平心而論，Generator 的機制對許多 JavaScript 開發者確是一個不小的門檻，與舊有的習慣格格不入，需要一點時間學習及熟悉。如果你還不熟悉其機制的細節，不如先學習一些習慣的使用，這是一個比較快能上手的方式。所以，我們可以試著來實作 Long-polling（長輪詢）機制，理解如何運用 Generator 在 Koa 中進行非同步的實作。&lt;br /&gt;
&lt;br /&gt;
若開發 Web 應用涉及了即時的需求，不免碰到 Long-polling（長輪詢）的機制， Long-polling（長輪詢）簡單來說，就是讓伺服器可以即時通知客戶端有資料要進行傳送，常被用在聊天室、即時通知訊息等應用，如 Facebook 的訊息通知機制，就是採用這樣的方式。&lt;br /&gt;
&lt;br /&gt;
Long-polling（長輪詢）的運行原理，就是讓客戶端向伺服器要求新的資料，但取得資料後雙方都不中止連線，等到伺服器有資料要通知客戶端時才將連線中斷，而客戶端就知道有新資料要接收，重新建立一次連線以取得新資料，然後又再次保持連線不中斷，等待下次通知。&lt;br /&gt;
&lt;br /&gt;
暸解了原理，我們就可以開始實作伺服器的部分。首先，假設我們現在有一個事件發送器，每次有資料更新，就會觸發一次事件。如下範例，是每秒鐘更新一次資料：&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;// 建立一個事件發送器
var dispatcher = new events.EventEmitter();
var num = 0;

setInterval(function() {
&amp;nbsp; &amp;nbsp; // 將 num 加一，更新資料
&amp;nbsp; &amp;nbsp; num++;

&amp;nbsp; &amp;nbsp; // 通知所有客戶端資料有更新
&amp;nbsp; &amp;nbsp; dispatcher.emit(&#39;update&#39;);
}, 1000);
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
如果是 Express，若要實作 Long-polling 來即時發送最新資料到客戶端，應該不外乎這樣寫：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;var events = require(&#39;events&#39;);

app.get(&#39;/poll&#39;, function(req, res) {

&amp;nbsp; &amp;nbsp; // 送資料到客戶端
&amp;nbsp; &amp;nbsp; res.send(num);

&amp;nbsp; &amp;nbsp; // 當有資料更新
&amp;nbsp; &amp;nbsp; dispatcher.once(&#39;update&#39;, function() {

&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // 中斷連線
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; res.end();
&amp;nbsp; &amp;nbsp; });
});
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
如果改採用 Koa 來實作：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;router.get(&#39;/poll&#39;, function *() {

&amp;nbsp; &amp;nbsp; // 送資料到客戶端
&amp;nbsp; &amp;nbsp; this.body = num;

&amp;nbsp; &amp;nbsp; // yield 將函數內的程式暫停於此，等待之後的函數完成並呼叫 callback
&amp;nbsp; &amp;nbsp; yield function(callback) {

&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // 當有資料更新
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; dispatcher.once(&#39;update&#39;, function() {

&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // 完成工作，繼續執行 yield 之後的程式碼
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; callback();
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; });
&amp;nbsp; &amp;nbsp; };

&amp;nbsp; &amp;nbsp;// Generator 跑完，連線中斷]
});
&lt;/pre&gt;&lt;/div&gt;我們可以看到，Koa 都採用 Generator 來處理客戶端要求，這樣的概念與傳統非同步的 Callback 機制有些不一樣。所以，若要在 Koa 中使用一些非同步機制，就要設計一個特殊函數讓 yield 使用，以等待工作完成，使 Generator 暫停，等 yield 的工作完成後再繼續。&lt;br /&gt;
&lt;br /&gt;
很多人在看到 Generator 時，不免和過去的非同步設計搞混，一時間會懷疑 yield 是否會使 JavaScript 的事件引擎阻塞，但這其實是多慮的，因為 Generator 內的機制與一般函數不一樣，而重點是 yield 也只能在 Generator 中使用，不需要怕誤用而導致事件引擎卡死。我們應該用另一個角度來看待 Generator 的邏輯，雖然他長得很像普通的函數。&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;客戶端實作&lt;/h2&gt;雖然客戶端的實作不是本文的重點，但也在此補上，供讀者參考，以下是採用 jQuery 的實作：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;function poll() {
&amp;nbsp; &amp;nbsp; $.ajax({
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; url: &#39;/poll&#39;,
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; type: &#39;get&#39;,
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; timeout: 120000,
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; complete: poll,
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; success: function(data) {
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; console.log(data);
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }
&amp;nbsp; &amp;nbsp; });
}

poll();&lt;/pre&gt;&lt;/div&gt;</description><link>https://fred-zone.blogspot.com/2015/01/koa-long-polling.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-1522294372413893270</guid><pubDate>Sun, 18 Jan 2015 09:53:00 +0000</pubDate><atom:updated>2015-01-18T19:34:14.634+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">io.js</category><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">Node.js</category><category domain="http://www.blogger.com/atom/ns#">技術新知</category><category domain="http://www.blogger.com/atom/ns#">自由軟體</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><title>注意！io.js 報到！</title><description>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://iojs.org/images/1.0.0.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;400&quot; src=&quot;https://iojs.org/images/1.0.0.png&quot; width=&quot;350&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
如果你有關注這兩個月 Node.js 的發展，就會發現一些大變動，尤其在最近半路殺出了一個名為 io.js 的 Application Framework，其來勢洶洶朝著 Node.js 而來。於是，很多人看糊塗了，開始搞不清楚兩者的關係，甚至以為他是一個全新的技術。事實上，一切緣由皆由 Node.js 而起，這是起圈內的八卦事件，當然，我們也可以將其看作是社群對把持開放原始碼專案的商業團體，進行的抗議行動。&lt;br /&gt;
&lt;br /&gt;
io.js 的官方網站：&lt;br /&gt;
&lt;div&gt;
&lt;pre&gt;&lt;a href=&quot;https://iojs.org/&quot;&gt;https://iojs.org&lt;/a&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
由於 Node.js 的發展日益興旺，掌握著 Node.js 的發展進度就代表掌握著利益，所以 Joyent 開始掌控著 Node.js 的開發進度。縱使 Node.js 有著開放原始碼社群，也有著許多貢獻者，但新版本的釋出一直受到 Joyent 的掌控，最讓社群不滿的是 v0.10 版之後，已經快兩年沒有發佈新的穩定版本。這意味著，無論社群絞盡腦汁去協助開發 Node.js，成果永遠無法面市，也意味著大家所期待的 ECMAScript 6，遲遲無法在 Node.js 上被支援。&lt;br /&gt;
&lt;br /&gt;
終於，社群等不下去了，在 2014 年底發起新的計劃 io.js ，試圖 fork 原本的 Node.js 專案，目標是如果 Joyent 遲遲不發佈 v0.12 版的 Node.js，就打算自己動手。這個 io.js 的新專案，緊鑼密鼓的展開，吸引了許多開發者的關注及加入，不到兩個月的時間，在 2015 年 1 月中推出了 io.js v1.0.0 的版本，並且打算加緊步調，讓開發與發布週期更為密集。&lt;br /&gt;
&lt;br /&gt;
其實，我們可以將 io.js 的 v1.0 版視為 Node.js v0.12 版，它們本質上是一樣的東西，所以 io.js 也可以支援 NPM，有著一樣的使用方式、API 和架構設計，更相容於 Node.js v0.11 及未來的 v0.12。這代表，原本以 Node.js 所開發的應用程式，理論上都可以正常以 io.js 執行。&lt;br /&gt;
&lt;br /&gt;
至於未來 io.js 是否會將 Node.js 取代呢？我們拭目以待。</description><link>https://fred-zone.blogspot.com/2015/01/iojs.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-6763944520991388824</guid><pubDate>Sat, 27 Dec 2014 03:16:00 +0000</pubDate><atom:updated>2014-12-27T11:16:56.661+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">bittorrent</category><category domain="http://www.blogger.com/atom/ns#">C/C++</category><category domain="http://www.blogger.com/atom/ns#">FUSE</category><category domain="http://www.blogger.com/atom/ns#">Node.js</category><category domain="http://www.blogger.com/atom/ns#">心情筆記</category><category domain="http://www.blogger.com/atom/ns#">技術新知</category><category domain="http://www.blogger.com/atom/ns#">自由軟體</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><title>Node.js 的精彩應用！整合 bittorrent 與 FUSE 其實不難！</title><description>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgeVU8gei0zZFogKzgmZcNUd36JBAOsRZwPAe9_QxpKklpNETR50u7aoqs5_m8v17DXr8oenbCFNrCZjmhs_xXO8mfEmyFxzhkD8KwVqfvwezxhF7opuIBa5Cgyyq7_DomMQso4EqJ_wlY/s1600/DSC_0745.JPG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgeVU8gei0zZFogKzgmZcNUd36JBAOsRZwPAe9_QxpKklpNETR50u7aoqs5_m8v17DXr8oenbCFNrCZjmhs_xXO8mfEmyFxzhkD8KwVqfvwezxhF7opuIBa5Cgyyq7_DomMQso4EqJ_wlY/s1600/DSC_0745.JPG&quot; height=&quot;360&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div style=&quot;color: #141823; font-family: Helvetica, Arial, &#39;lucida grande&#39;, tahoma, verdana, arial, sans-serif; font-size: 15px; line-height: 20px; margin-bottom: 6px;&quot;&gt;
寫書寫到通訊協定一章，就想起今年來台灣 JSDC 的 Mathias，在台上展示以 Node.js 開發的 Bittorrent 影片下載工具『peerflix』，該工具可以結合 vlc、mplayer 這類的播放器進行 BT 種子的影片播放，相當有趣。此外，他亦展示了使用 VirtualBox 直接開 BT 上的 Ubuntu 安裝ISO 檔的實作。&lt;/div&gt;
&lt;div style=&quot;color: #141823; font-family: Helvetica, Arial, &#39;lucida grande&#39;, tahoma, verdana, arial, sans-serif; font-size: 15px; line-height: 20px; margin-bottom: 6px;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style=&quot;color: #141823; font-family: Helvetica, Arial, &#39;lucida grande&#39;, tahoma, verdana, arial, sans-serif; font-size: 15px; line-height: 20px; margin-bottom: 6px; margin-top: 6px;&quot;&gt;
很多人都說很神、很有趣，但看完後不見得會去了解實作細節。有些人更覺得這是他們無法碰觸的領域，只是當一個神在展示他的神力而已。這樣的情況，讓我感到難過，因為很多這樣的技術細節並不難，除了 bittorrent 本身的演算法本身的實作，或是開發其 Binding 是有困難度之外，其餘的東西根本上都是相當簡單的概念。&lt;/div&gt;
&lt;div style=&quot;color: #141823; font-family: Helvetica, Arial, &#39;lucida grande&#39;, tahoma, verdana, arial, sans-serif; font-size: 15px; line-height: 20px; margin-bottom: 6px; margin-top: 6px;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style=&quot;color: #141823; font-family: Helvetica, Arial, &#39;lucida grande&#39;, tahoma, verdana, arial, sans-serif; font-size: 15px; line-height: 20px; margin-bottom: 6px; margin-top: 6px;&quot;&gt;
我想，這樣並不困難的東西，如果不去解析或了解它，實在是可惜了 JavaScript 至今的發展。&lt;/div&gt;
&lt;div style=&quot;color: #141823; font-family: Helvetica, Arial, &#39;lucida grande&#39;, tahoma, verdana, arial, sans-serif; font-size: 15px; line-height: 20px; margin-bottom: 6px; margin-top: 6px;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style=&quot;color: #141823; font-family: Helvetica, Arial, &#39;lucida grande&#39;, tahoma, verdana, arial, sans-serif; font-size: 15px; line-height: 20px; margin-bottom: 6px; margin-top: 6px;&quot;&gt;
事實上， bittorrent client 的 binding 早有人開發出來並寫成模組。我們只需要使用種子檔案或是 magnet link，就可以透過 binding 去取得檔案資料，甚至是特定段落的資料，就像我們實作 HTTP 續傳那樣簡單，只需指定資料的開始處而已，全都有現成的 API 可使用。&lt;/div&gt;
&lt;div style=&quot;color: #141823; font-family: Helvetica, Arial, &#39;lucida grande&#39;, tahoma, verdana, arial, sans-serif; font-size: 15px; line-height: 20px; margin-bottom: 6px; margin-top: 6px;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style=&quot;color: #141823; font-family: Helvetica, Arial, &#39;lucida grande&#39;, tahoma, verdana, arial, sans-serif; font-size: 15px; line-height: 20px; margin-bottom: 6px; margin-top: 6px;&quot;&gt;
若要播放這些影片內容，只需要建立一個 HTTP Server，再將從 bittorrent 下載完成的影片資料檔，運用 Stream 的技巧導向 HTTP 輸出即可。實作到此，我們就可以透過支援 HTML5 Video 的瀏覽器或是其他支援串流的多媒體播放器，以 HTTP URL 打開影片觀看。&lt;/div&gt;
&lt;div style=&quot;color: #141823; font-family: Helvetica, Arial, &#39;lucida grande&#39;, tahoma, verdana, arial, sans-serif; font-size: 15px; line-height: 20px; margin-bottom: 6px; margin-top: 6px;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style=&quot;color: #141823; font-family: Helvetica, Arial, &#39;lucida grande&#39;, tahoma, verdana, arial, sans-serif; font-size: 15px; line-height: 20px; margin-bottom: 6px; margin-top: 6px;&quot;&gt;
至於 VirtualBox 怎麼去開啟 bittorrent 上的 ISO 檔，然後讀到哪載到哪的技術，其實也不是非常難，運用 Linux/BSD(Mac OS) 上的 FUSE（Filesystem in Userspace）的技術即可達成。雖然這是熟悉系統開發才會知道的技術，但概念與開發上其實並不難，尤其是也有人寫了 Node.js binding，我們可以完全使用 JavaScript 輕易實作。&lt;/div&gt;
&lt;div style=&quot;color: #141823; font-family: Helvetica, Arial, &#39;lucida grande&#39;, tahoma, verdana, arial, sans-serif; font-size: 15px; line-height: 20px; margin-bottom: 6px; margin-top: 6px;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style=&quot;color: #141823; font-family: Helvetica, Arial, &#39;lucida grande&#39;, tahoma, verdana, arial, sans-serif; font-size: 15px; line-height: 20px; margin-bottom: 6px; margin-top: 6px;&quot;&gt;
簡單來說，使用 FUSE，可以讓我們不必觸碰到作業系統核心的驅動程式技術，就可以開發一個自己的檔案系統。然後，我們可以把自己實作的檔案系統掛載到一個本機目錄上。這代表，當其他應用程式使用標準操作、讀取（如 open、read、write 等）這個目錄裡面的檔案時，我們的程式就要接手這些操作要求的工作，你可以想像是其他應用程式跟我們要資料，而這個檔案系統是一個虛擬的介面。&lt;/div&gt;
&lt;div style=&quot;color: #141823; font-family: Helvetica, Arial, &#39;lucida grande&#39;, tahoma, verdana, arial, sans-serif; font-size: 15px; line-height: 20px; margin-bottom: 6px; margin-top: 6px;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style=&quot;color: #141823; font-family: Helvetica, Arial, &#39;lucida grande&#39;, tahoma, verdana, arial, sans-serif; font-size: 15px; line-height: 20px; margin-bottom: 6px; margin-top: 6px;&quot;&gt;
註：熟悉 C/C++ 的人都知道，無論上面檔案操作 API 怎麼包裝，系統底層最終依舊以 open()、read()、write() 等這類 API 進行檔案操作。FUSE 可以讓我們開發程式模擬一個檔案系統，接受其他應用程式的檔案操作要求，並進行處理。&lt;/div&gt;
&lt;div style=&quot;color: #141823; font-family: Helvetica, Arial, &#39;lucida grande&#39;, tahoma, verdana, arial, sans-serif; font-size: 15px; line-height: 20px; margin-bottom: 6px; margin-top: 6px;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style=&quot;color: #141823; font-family: Helvetica, Arial, &#39;lucida grande&#39;, tahoma, verdana, arial, sans-serif; font-size: 15px; line-height: 20px; margin-bottom: 6px; margin-top: 6px;&quot;&gt;
所以，假設我們的程式把 FUSE 接上 ssh、ftp 或是網路硬碟的 Web API，使用者就可以像是在操作本機目錄一樣，操作網路硬碟。&lt;/div&gt;
&lt;div style=&quot;color: #141823; font-family: Helvetica, Arial, &#39;lucida grande&#39;, tahoma, verdana, arial, sans-serif; font-size: 15px; line-height: 20px; margin-bottom: 6px; margin-top: 6px;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style=&quot;color: #141823; font-family: Helvetica, Arial, &#39;lucida grande&#39;, tahoma, verdana, arial, sans-serif; font-size: 15px; line-height: 20px; margin-bottom: 6px; margin-top: 6px;&quot;&gt;
運用這個技術，只要把 ISO 檔以 FUSE 包上一層，當 VirtualBox 來存取這個 ISO 檔時，我們就可以得知 VirtualBox 想要讀取 ISO 檔上哪一段資料，如果被要求的部分尚未從 bittorrent 上抓回來，就立即去抓取，並使 VirtualBox 進行等待（對 VirtualBox 而言，它只會覺得是硬碟讀取速度太慢）。而如果 VirtualBox 是跳著讀取 ISO 檔上的其他部分，也是同樣作法即可完成。&lt;/div&gt;
&lt;div style=&quot;color: #141823; font-family: Helvetica, Arial, &#39;lucida grande&#39;, tahoma, verdana, arial, sans-serif; font-size: 15px; line-height: 20px; margin-bottom: 6px; margin-top: 6px;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style=&quot;color: #141823; display: inline; font-family: Helvetica, Arial, &#39;lucida grande&#39;, tahoma, verdana, arial, sans-serif; font-size: 15px; line-height: 20px; margin-top: 6px;&quot;&gt;
總之，這一切真的不難，只不過有些跨領域而已。:-)&lt;/div&gt;
</description><link>https://fred-zone.blogspot.com/2014/12/nodejs-bittorrent-fuse.html</link><author>noreply@blogger.com (Fred Chien)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgeVU8gei0zZFogKzgmZcNUd36JBAOsRZwPAe9_QxpKklpNETR50u7aoqs5_m8v17DXr8oenbCFNrCZjmhs_xXO8mfEmyFxzhkD8KwVqfvwezxhF7opuIBa5Cgyyq7_DomMQso4EqJ_wlY/s72-c/DSC_0745.JPG" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-3655068983410853947</guid><pubDate>Mon, 22 Dec 2014 23:24:00 +0000</pubDate><atom:updated>2014-12-23T07:26:11.439+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">ECMAScript 6</category><category domain="http://www.blogger.com/atom/ns#">Embedded System</category><category domain="http://www.blogger.com/atom/ns#">Hackathon</category><category domain="http://www.blogger.com/atom/ns#">Koa</category><category domain="http://www.blogger.com/atom/ns#">Node.js</category><category domain="http://www.blogger.com/atom/ns#">心得分享、講座</category><category domain="http://www.blogger.com/atom/ns#">心情筆記</category><category domain="http://www.blogger.com/atom/ns#">技術新知</category><category domain="http://www.blogger.com/atom/ns#">自由軟體</category><category domain="http://www.blogger.com/atom/ns#">黑客松</category><title>Hackathon 活動的『Koa 正在等一個人』簡報釋出！</title><description>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhK89hIENuL4RBNDpUEbx7t7Y1FDR0opnzTGXKExnLHMrYX4U2sr1a4vtKi9oM3YYwBwKfWBTon62ja90OUQlC-Ki2sIDgTyyvh6h4iWWVTI9_3jq1SMr0qW6g5isTrmafr1R-bqhp84Ew/s1600/DSC_0670.JPG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhK89hIENuL4RBNDpUEbx7t7Y1FDR0opnzTGXKExnLHMrYX4U2sr1a4vtKi9oM3YYwBwKfWBTon62ja90OUQlC-Ki2sIDgTyyvh6h4iWWVTI9_3jq1SMr0qW6g5isTrmafr1R-bqhp84Ew/s1600/DSC_0670.JPG&quot; height=&quot;360&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
很開心，這次十二月的『&lt;a href=&quot;http://hackathon.tw/&quot;&gt;Hackathon Taiwan&lt;/a&gt;』相當多人參與，百人的報到數讓所有工作人員都感到興奮。最重要的，多虧了活動夥伴的遊戲設計，這次讓很多隻身而來的人，化解尷尬、相互認識並熱絡起來，最後順利組成隊伍，這算是種格外特別的創舉，過去很少有黑客松能做到這樣的地步。&lt;br /&gt;
&lt;br /&gt;
有趣的是，這次活動找來了重量級的神秘講師 Jeremy Lu （對，就是那位最近紅極一時的 React.js 講者）開課，讓聽眾大飽耳福，絕對不枉來這一趟。有很多人也衝著 Qt 的課程而來，學習 QML 這樣先進的 UI 技術，這次的 QML 遊戲開發課程，相信讓很多人也開心地學會很多技術。&lt;br /&gt;
&lt;br /&gt;
小弟在第二天的結尾，也給了篇一小時的分享『Koa 正在等一個人』，其內容在探討並帶領上手 Koa 這個下一代的網站框架、ECMAScript 6 Generator，以及 Node.js 和 io.js 的最新動態。（事實上是把最近將要出的書，擷取片段內容出來分享）&lt;br /&gt;
&lt;br /&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;
&lt;iframe allowfullscreen=&quot;&quot; frameborder=&quot;0&quot; height=&quot;355&quot; marginheight=&quot;0&quot; marginwidth=&quot;0&quot; scrolling=&quot;no&quot; src=&quot;//www.slideshare.net/slideshow/embed_code/42911997&quot; style=&quot;border-width: 1px; border: 1px solid #CCC; margin-bottom: 5px; max-width: 100%;&quot; width=&quot;425&quot;&gt; &lt;/iframe&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;h2&gt;
後記&lt;/h2&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;
對於我而言，活動內容再豐富，都比不上找到了好隊友可以一起創作。這次活動，得到了一個硬體工程師加入我們的團隊，所以可以開始進行硬體設計及控制等工作，而且大家相約了下次一起再來。下次，我們將會開始進行各種智慧家電的控制，以及 IoT 的相關應用。看來，我們只差一個設計師的加入了！如果你有興趣，快來加入我們！:-D&lt;/div&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style=&quot;text-align: left;&quot;&gt;
下次黑客松活動預計將於 2015 年 1/31、2/1（六、日）兩天，無論你是軟體開發者、硬體開發者、設計師還是學生，都歡迎來參加！而且，一樣會有神秘講師！&lt;/div&gt;
</description><link>https://fred-zone.blogspot.com/2014/12/hackathon-koa.html</link><author>noreply@blogger.com (Fred Chien)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhK89hIENuL4RBNDpUEbx7t7Y1FDR0opnzTGXKExnLHMrYX4U2sr1a4vtKi9oM3YYwBwKfWBTon62ja90OUQlC-Ki2sIDgTyyvh6h4iWWVTI9_3jq1SMr0qW6g5isTrmafr1R-bqhp84Ew/s72-c/DSC_0670.JPG" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-3678543090598612954</guid><pubDate>Fri, 28 Nov 2014 06:52:00 +0000</pubDate><atom:updated>2014-11-28T14:52:50.452+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">HanGee 國民機運動</category><category domain="http://www.blogger.com/atom/ns#">QML</category><category domain="http://www.blogger.com/atom/ns#">Qt</category><category domain="http://www.blogger.com/atom/ns#">User Interface</category><category domain="http://www.blogger.com/atom/ns#">心得分享、講座</category><category domain="http://www.blogger.com/atom/ns#">手機平台</category><category domain="http://www.blogger.com/atom/ns#">技術新知</category><category domain="http://www.blogger.com/atom/ns#">活動</category><category domain="http://www.blogger.com/atom/ns#">自由軟體</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><category domain="http://www.blogger.com/atom/ns#">顧問咨詢</category><category domain="http://www.blogger.com/atom/ns#">黑客松</category><title>OwaViewer 釋出！不用安裝肥滋滋的 Qt 也可以開發 QML！</title><description>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi06NoASjEMGX20_aXZaAQT-sjXIrLx-HU1fxLfHe5V-uMkrTNahaqLI68pBtB0jx7P-DuA4I0nDrxdyRklWeViC7qdAUHmGrWEImjm9AyjtbMSny-2D3Id5kCdO2Z9-LhrJ5EPZ903cxo/s1600/DSC_0128.JPG&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi06NoASjEMGX20_aXZaAQT-sjXIrLx-HU1fxLfHe5V-uMkrTNahaqLI68pBtB0jx7P-DuA4I0nDrxdyRklWeViC7qdAUHmGrWEImjm9AyjtbMSny-2D3Id5kCdO2Z9-LhrJ5EPZ903cxo/s1600/DSC_0128.JPG&quot; height=&quot;360&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div&gt;
在國內，很多人或許對 Qt 很陌生，但這一直是筆者近年來想要推廣的技術，尤其是 QML 這個自 Qt Project 衍生出來的新 UI 技術，更讓人著迷。具有原生效能和擁有炫麗效果的 QML ，確實是一個打造產品的好東西，如大家近來所聽到的 Tesla 汽車、Jolla 手機等，其 UI 就是使用 QML 所開發。當然，之前舊文章中所展示的 UI 和介面，以及能跑在 Android 手機上的 OwaNEXT，也有不少是結合 QML 技術所開發。&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;iframe allowfullscreen=&#39;allowfullscreen&#39; webkitallowfullscreen=&#39;webkitallowfullscreen&#39; mozallowfullscreen=&#39;mozallowfullscreen&#39; width=&#39;320&#39; height=&#39;266&#39; src=&#39;https://www.youtube.com/embed/l0pUgAUDy7Q?feature=player_embedded&#39; frameborder=&#39;0&#39;&gt;&lt;/iframe&gt;&lt;iframe allowfullscreen=&#39;allowfullscreen&#39; webkitallowfullscreen=&#39;webkitallowfullscreen&#39; mozallowfullscreen=&#39;mozallowfullscreen&#39; width=&#39;320&#39; height=&#39;266&#39; src=&#39;https://www.youtube.com/embed/kqBPzIftdm8?feature=player_embedded&#39; frameborder=&#39;0&#39;&gt;&lt;/iframe&gt;&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;/div&gt;
&lt;div&gt;
推廣過程中總有人質疑，覺得 QML 不是一個設計師能學會的東西。事實上，它的語法相當簡單且模組化，所以也聽聞一些國外的設計公司，其設計師都直接用 QML 做出 UI ，再交給工程師去接上功能。這也是為什麼 HanGee 國民機在開發手機和各種裝置的 UI 時，要選用 QML 為底層的原因了。&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;
&lt;div&gt;
&lt;/div&gt;
&lt;div&gt;
因此，筆者自己的公司最近也在籌備許多課程和分享，也與許多朋友成立了一些社群（你可以在 Facebook 加入我們的 Qt @ Taiwan 社群），並設計開發一些相關的工具，以協助更多人能更快速進入到 QML 的世界。不過，因為 UI 與設計師更密切相關，近期應該會針對不懂技術的設計師，推出許多相關的 UI 開發課程。（如果你對這樣的技術有興趣，歡迎與我們聯絡）&lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;iframe allowfullscreen=&#39;allowfullscreen&#39; webkitallowfullscreen=&#39;webkitallowfullscreen&#39; mozallowfullscreen=&#39;mozallowfullscreen&#39; width=&#39;320&#39; height=&#39;266&#39; src=&#39;https://www.youtube.com/embed/mLcwy5aPOs4?feature=player_embedded&#39; frameborder=&#39;0&#39;&gt;&lt;/iframe&gt;&lt;/div&gt;
&lt;div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
其實，這次的黑客松活動與 HanGee 社群合作開設 QML 課程就是一個嘗試，也想收集一些 Qt/QML 初入門者狀況和資料。其中發現一個最大的問題是，無論是技術還非技術人員（如設計師），在安裝 Qt 時就會碰到各種狀況，而且 Qt 開發環境因為功能繁複且龐大，安裝一整套下來要花上不少時間，在學習上也就是一個阻礙。這對技術人員來說，可能不是什麼大問題，但這對設計師來說，就是一個極大的挫折。&lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;/div&gt;
&lt;div&gt;
因此，我們、講師和 HanGee 社群合作，開發了一個新的工具『OwaViewer』。原本這個工具只是被設計來開發 HanGee 手機介面，但現在可以用來讀取和執行一般的 QML 檔案。這意味著，你再也不需要安裝 Qt 和學習一堆知識，就可以使用 QML 開發使用者介面了，也可以很容易提供一個成果給客戶直接操作，這對許多設計師來說應該是個好消息。&lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;/div&gt;
&lt;div&gt;
OwaViewer 是個 Open Source Project，專案網址：&lt;/div&gt;
&lt;div&gt;
&lt;pre&gt;&lt;a href=&quot;https://github.com/HanGee/OwaViewer&quot;&gt;https://github.com/HanGee/OwaViewer&lt;/a&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;
如果你是 Windows 或 Mac 的使用者，你也可以直接取得已編譯好的 OwaViewer 來使用：&lt;/div&gt;
&lt;div&gt;
&lt;pre&gt;&lt;a href=&quot;http://seerinn.com/dl/&quot;&gt;http://seerinn.com/dl/&lt;/a&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;br /&gt;
&lt;h2&gt;
&lt;b&gt;後記&lt;/b&gt;&lt;/h2&gt;
你可以看到，無論是 HanGee 還是任何參與推廣的人員，我們都期望，設計師的美好成果，能夠有更高的轉換率，落實在真實產品之上。而不是與工程師戰友進行長期的內耗拉鋸戰，最後妥協生成一個不堪的使用者介面。&lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;div&gt;
&lt;/div&gt;
&lt;div&gt;
這也是為什麼，縱使好萊塢電影的 UI 設計如此一年比一年進步，Apple 和遊戲公司這樣的市場領導者也一次次證明有進步的空間，但大多數產品設計師，仍然保守的採用一些排列組合式的老式設計方式，並放棄許多互動性的細節。&lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;/div&gt;
&lt;div&gt;
就過去經驗，很多設計師都以為最大的敵人是工程師，但事實上，最大的問題是出在開發和設計工具上。你要一個工程師懂系統、程式、架構、圖學、物理學，甚至是各種演算法，這也太強人所難。雖然，這的確是做一個新一代 UI 的必要技能，但這每一個項目都是要曠日費時才能掌握的技術，找齊這些人才更要些機運和金錢，所以沒有多少產品公司或團隊能夠一次完備。&lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;iframe allowfullscreen=&#39;allowfullscreen&#39; webkitallowfullscreen=&#39;webkitallowfullscreen&#39; mozallowfullscreen=&#39;mozallowfullscreen&#39; width=&#39;320&#39; height=&#39;266&#39; src=&#39;https://www.youtube.com/embed/7p-0r91x8yk?feature=player_embedded&#39; frameborder=&#39;0&#39;&gt;&lt;/iframe&gt;&lt;/div&gt;
&lt;div&gt;
&lt;/div&gt;
</description><link>https://fred-zone.blogspot.com/2014/11/owaviewer-qt-qml.html</link><author>noreply@blogger.com (Fred Chien)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi06NoASjEMGX20_aXZaAQT-sjXIrLx-HU1fxLfHe5V-uMkrTNahaqLI68pBtB0jx7P-DuA4I0nDrxdyRklWeViC7qdAUHmGrWEImjm9AyjtbMSny-2D3Id5kCdO2Z9-LhrJ5EPZ903cxo/s72-c/DSC_0128.JPG" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-3948106786282553887</guid><pubDate>Mon, 27 Oct 2014 02:23:00 +0000</pubDate><atom:updated>2014-10-27T10:23:00.357+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">HanGee 國民機運動</category><category domain="http://www.blogger.com/atom/ns#">OwaNEXT</category><category domain="http://www.blogger.com/atom/ns#">User Interface</category><category domain="http://www.blogger.com/atom/ns#">有趣新構思</category><title>逼自己思考：嘗試再次思考 UI 設計</title><description>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhuni_mNwbZxUDVpxsRVbQ2HUikewm2AeQcHxAiBbEByWJoGwFx1BEuT-b7GcPAois5mSQFJ34RXMKCIJIEQF9S_3omQXkZlj5TCLDIsryL2bbJfb5NJD6Wfk6bbgZZf9b6tnrsgDiXDc8/s1600/1016747_943599568986718_7281329120878856991_n.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhuni_mNwbZxUDVpxsRVbQ2HUikewm2AeQcHxAiBbEByWJoGwFx1BEuT-b7GcPAois5mSQFJ34RXMKCIJIEQF9S_3omQXkZlj5TCLDIsryL2bbJfb5NJD6Wfk6bbgZZf9b6tnrsgDiXDc8/s1600/1016747_943599568986718_7281329120878856991_n.jpg&quot; height=&quot;480&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
雖然自己從小喜愛設計，殘害過不少紙筆和滑鼠，但也因為懶惰和時間有限，長大後開始比較少開始動手自己做一些設計。所以，除了突然有一些想法和點子之外，多數時候都會把『設計』這件事推給『設計師』來做，講好聽點叫術業有專攻，講不好聽點就是有想法卻不行動的懶惰派。&lt;span id=&quot;goog_680306650&quot;&gt;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
如許多人所知，我這幾年一直都在做 JavaScript OS 的開發，嘗試把 JavaScript 應用在各種大大小小的硬體和裝置之中，使嵌入式應用的開發工作更有效率和彈性。又由於這一年來物聯網（IoT）的風潮大爆發，也因此有更多人使用 JavaScript 為基礎的開發，或是形形色色的跨界需求，讓我需要更多時間和金錢去投入這些發展。&lt;br /&gt;
&lt;br /&gt;
不過人總是視覺化的動物，任各種應用發展再多，眼睛看得到的 UI 仍不能缺少。開發的過程中，難免會有一些『有螢幕』的應用。這也是為什麼這幾個月 HanGee 國民機一直在做『OwaNEXT』這樣的 UI 設計工具，而我最近也時常去思考一些 UI 的設計。&lt;br /&gt;
&lt;br /&gt;
如下面展示影片，就是最近這幾周所嘗試開發出來的一個 UI 選單介面概念原型，雖然說是概念原型，但實際上是可以使用的程式，不是一個純展示動畫（畢竟我仍然是一個程式開發者 :-D）。這個 UI 因為是設計給大尺寸螢幕所用，所以是用『Everywhere』為出發點，讓人可以在任何一個地方憑空叫出選單，效果和使用方法都是以一個手掌可掌控為目標：&lt;br /&gt;
&lt;br /&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;
&lt;iframe allowfullscreen=&quot;&quot; frameborder=&quot;0&quot; height=&quot;360&quot; src=&quot;//www.youtube.com/embed/7p-0r91x8yk?rel=0&quot; width=&quot;480&quot;&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;/div&gt;
&lt;br /&gt;
話說，我一直都不是個走純 Web 路線的開發者，過去有好幾年完不碰 Web 開發，只因為當時 JavaScript 和瀏覽器的效能和效果讓我很不滿意，然後也發現因為太依賴瀏覽器，讓自己的視野窄了許多。也許是因為這樣，這些年許多從前端所發起的各種 UI 設計討論，我沒有接觸太多，也沒有被同化成一份子。&lt;br /&gt;
&lt;br /&gt;
所以，大家在前端很熟悉的 UI 切版、排版等等平常再不過的工作，對我來說反而都是可以再思考和需要再質疑的東西，也仍然保留著強烈的好奇心和探索的意圖。所以最近一直在思考設計 UI 時，一直在想『跳脫出 2D 排版的思維』這個議題。&lt;br /&gt;
&lt;br /&gt;
不知道是因為工具的關係，還是設計方法的關係，現今的 UI 設計，總跳脫不出平面排版的框框，這個東西擺左邊一點、擺高一點、放大一點等等修改，一直是現今許多 UI 設計的討論重點。很少聽到在討論，這個速度要慢一點、緩一點、從哪開始、又從哪定位。&lt;br /&gt;
&lt;br /&gt;
是的，因為討論到這些東西，就會開始討論到動畫，討論到動畫，就會開始討論到分鏡，討論到分鏡，甚至還得討論到電影學。你會發現，這將會變成一條非常難走的道路，於是，現今主流的 UI 設計，大多仍然停留在平面上。但你也會發現，像 Apple 這樣只要多用心一點點跳出常規平面設計，UI 就讓人覺得美得冒泡或更善體人意。&lt;br /&gt;
&lt;br /&gt;
事實上，過去我一直覺得是工具不夠進步，讓我們設計總是無法讓人像電影中那樣讓人震撼或感動。這或許是個原因，但當自己投入下去做且開發工具時才發現，有更多的原因是自己素養不夠。像是當我要思考物件之間的關係，前後順序的意義表達時，我發現，自己沒有很好的分鏡技能，甚至可能是素描技能、說故事的能力。&lt;br /&gt;
&lt;br /&gt;
因為技能不足，於是，我需要花更多時間在調整細節，並用毫無標準的本能去感受微調結果。過程中發現，工具或許可以幫助我們減短微調的時間，但無法減少微調或打掉重練的次數。這就像是一些專業或天才的音樂家可以在腦中譜曲後（甚至是多部同時、以不同樂器同時譜曲），到一定程度再寫下來或演奏出來；而半路出家或比較沒天份的音樂創作者，只能用哼唱出來或利用樂器演奏完後，才能確定旋律好壞。&lt;br /&gt;
&lt;br /&gt;
不過還好，UI 設計所需要的技能，可以靠後天補足，這是我們值得慶幸的。我也仍然會不時去創作和發想一些 UI 的點子，並實作出來。:-)</description><link>https://fred-zone.blogspot.com/2014/10/ui.html</link><author>noreply@blogger.com (Fred Chien)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhuni_mNwbZxUDVpxsRVbQ2HUikewm2AeQcHxAiBbEByWJoGwFx1BEuT-b7GcPAois5mSQFJ34RXMKCIJIEQF9S_3omQXkZlj5TCLDIsryL2bbJfb5NJD6Wfk6bbgZZf9b6tnrsgDiXDc8/s72-c/1016747_943599568986718_7281329120878856991_n.jpg" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-872612906549134117</guid><pubDate>Tue, 23 Sep 2014 03:19:00 +0000</pubDate><atom:updated>2014-09-23T11:19:06.174+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Hackathon</category><category domain="http://www.blogger.com/atom/ns#">HanGee 國民機運動</category><category domain="http://www.blogger.com/atom/ns#">有趣新構思</category><category domain="http://www.blogger.com/atom/ns#">活動</category><category domain="http://www.blogger.com/atom/ns#">自由軟體</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><category domain="http://www.blogger.com/atom/ns#">黑客松</category><title>Hankathon 黑客松專用倒數計時器！</title><description>不用覺得奇怪，Hankathon 沒有拼錯，這是取自 HanGee&amp;nbsp;+ Hackathon 的命名。是這次為了 9/20 - 9/21 兩天的 HanGee Hackathon 活動，設計的一個騷包倒數計時器，也是我在邊辦活動邊做的成果之一，用來提醒眾參加者們，時間正在毫不留情的流逝！&lt;br /&gt;
&lt;br /&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;
&lt;iframe allowfullscreen=&quot;&quot; frameborder=&quot;0&quot; height=&quot;360&quot; src=&quot;//www.youtube.com/embed/kqBPzIftdm8?rel=0&quot; width=&quot;480&quot;&gt;&lt;/iframe&gt;&lt;br /&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
Source Code:&amp;nbsp;&lt;a href=&quot;https://github.com/HanGee/Hankathon&quot;&gt;https://github.com/HanGee/Hankathon&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
如果你的黑客松、活動需要用到這個倒數計時器，歡迎取用！也歡迎修改成你想要的功能！只是，如果可以的話，麻煩留下蕃薯的小圖，協助讓 HanGee 亮點像吧！:-)&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;後記&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;&lt;br /&gt;&lt;/b&gt;
這次的黑客松活動，就在經歷過兩天美好的夜景、日出景色，最後經歷颱風大雨的情境下落幕。雖然中間出了一些烏龍，仍有一些朋友們沒有被好好照顧到，但都在歡樂的氣氛下和滿滿的產出中結束。下我們肯定會再次舉辦，讓更多無論有沒有資身技術背景的朋友，都能夠共同參與並學到東西和經驗回家。:-D&lt;br /&gt;
&lt;br /&gt;
此外，如果有社群或企業想要一同舉行下次的 Hackathon，歡迎連絡我們！</description><link>https://fred-zone.blogspot.com/2014/09/hankathon.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-3593641502455764590</guid><pubDate>Wed, 03 Sep 2014 03:14:00 +0000</pubDate><atom:updated>2014-09-03T11:14:01.903+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Hackathon</category><category domain="http://www.blogger.com/atom/ns#">心得分享、講座</category><category domain="http://www.blogger.com/atom/ns#">技術新知</category><category domain="http://www.blogger.com/atom/ns#">有趣新構思</category><category domain="http://www.blogger.com/atom/ns#">活動</category><category domain="http://www.blogger.com/atom/ns#">黑客松</category><title>【萌典 x HanGee x DOITT x 黑客松】睡不著嗎？那就不要睡了！</title><description>熱血的開發馬拉松活動（Hackathon）來囉！讓我們吃吃喝喝、不眠不休地設計應用吧！一群素不相識的設計人，無論是程式設計師、視覺設計師、UI/UX設計師，還是任何一種從事『設計』和『開發』工作的人們，在短短有限的時間內組隊並相互合作，一起投入設計同一個專案，與不同隊伍相互較勁！&lt;br /&gt;
&lt;br /&gt;
這次萌點、DOITT 和 HanGee 合作，將於 9/20 - 9/21 兩天，聯合舉辦 80 人不中斷黑客松活動，熱血的朋友們，快來報名並幫忙宣傳吧！如果你願意贊助，也歡迎聯絡！&lt;br /&gt;
&lt;br /&gt;
報名網址：&lt;br /&gt;
&lt;a href=&quot;http://han-gee.kktix.cc/events/hangeehackathon201409&quot;&gt;http://han-gee.kktix.cc/events/hangeehackathon201409&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;
讓我們回歸原點，什麼是黑客松呢？&lt;/h2&gt;
由於這幾年國內舉辦過很多大大小小的黑客松活動，大多數人對於黑客松早就耳熟能詳。不過，除了由技術背景的單位和社群自發性舉辦的活動外，許多黑客松活動，比較像是純粹的比賽，讓許多團隊來展示自己研發已久的專案或產品，慢慢脫離了開發馬拉松的原意。所以，因此不免也有許多人對黑客松產生誤解，覺得就只是個普通的比賽活動。&lt;br /&gt;
&lt;br /&gt;
事實上，和時下流行的路跑活動一樣，黑客松並也不是個普通的比賽活動，而是一個讓設計人在閒暇時間，參與的開發運動。解放自己，做自己想做的設計和開發，挑戰自己，在有限時間內做出東西來，才是黑客松活動的原始目的。&lt;br /&gt;
&lt;h2&gt;
我能參加嗎？&lt;/h2&gt;
對開放社群的朋友們來說，『黑客松』已經是一個再熟悉不過的名詞了，但對普通人來說，只要看到『黑客』兩個字，就覺得是一個敬而遠之的東西。不只如此，很多技術人員和設計師也對這活動有所誤解，覺得一定要非常有實力才能參加黑客松活動。&lt;br /&gt;
&lt;br /&gt;
其實，黑客松和路跑活動一樣，並非只有專業選手才能參加。只要你有興趣、想實際參與多人設計和開發，無論你是學生、普通程式人員、還是新手設計師、初級自造者，都歡迎帶著學習、共樂的心前來參加！&lt;br /&gt;
&lt;br /&gt;
你可以來找人一起完成自己天馬行空的想法，也可以和別人一起完成其他人的鬼點子，亦或是在這找到同伴共同想出新點子！&lt;br /&gt;
&lt;h2&gt;
我能得到什麼？&lt;/h2&gt;
在這活動中，參加者將帶著愉快的心情前來，一邊吃吃喝喝交朋友，一邊與從未合作過的朋友和高手們組隊，做出作品！然後你會發現，因為只有短短的一兩天，大家將使出混身解數，在短時間內做出出乎意料的驚人成果。最重要的是，也能從其他高手身上學會不少東西！兩天所獲得的東西，絕對物超所值！&lt;br /&gt;
&lt;br /&gt;
此外，黑客松就像微創業，有限的時間、有限的資源、三五好友聚在一起，然後打造出產品，上台公開發表。這是找回自信心和工作效率，以及想體驗創業的最佳途徑！&lt;br /&gt;
&lt;h2&gt;
活動上有什麼？&lt;/h2&gt;
有高手大大、帥哥與美女人才們，有電有網路、有吃的有喝的，既然台灣的夏天嚴熱，當然還有附上空調視野絕佳的舒適環境，讓大家安心動手做設計、做開發！挑戰自我！&lt;br /&gt;
&lt;h2&gt;
歡迎更多合作單位聯名加入！找人才？重視人才？快來吧！&lt;/h2&gt;
HanGee 將會持續不定期舉辦黑客松活動，歡迎各界加入並可以以任何形式共同合作。舉辦一個黑客松活動需要食物、人力、場地等各種資源，歡迎您的參與和協助！&lt;br /&gt;
&lt;br /&gt;
如果您是公司行號，這樣的活動，非常值得重視人才的您長期贊助。此外，這也將是你挖掘頂尖人才的最佳場合，履歷和面試怎麼也比不上近距離接觸觀察，實際與人才們合作從頭完成專案，才是最好的方法！&lt;br /&gt;
&lt;br /&gt;
有興趣嗎？請務必與 HanGee 聯絡！&lt;br /&gt;
&lt;br /&gt;
也想要辦黑客松（Hackathon）嗎？也請務必與 HanGee 聯絡！&lt;br /&gt;
&lt;br /&gt;
聯絡方式：hackathon@han-gee.com&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;</description><link>https://fred-zone.blogspot.com/2014/09/x-hangee-x-doitt-x.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-5776832842897930664</guid><pubDate>Tue, 02 Sep 2014 19:53:00 +0000</pubDate><atom:updated>2014-09-03T03:54:37.196+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">C/C++</category><category domain="http://www.blogger.com/atom/ns#">Filesystem</category><category domain="http://www.blogger.com/atom/ns#">FUSE</category><category domain="http://www.blogger.com/atom/ns#">Security</category><category domain="http://www.blogger.com/atom/ns#">自由軟體</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><category domain="http://www.blogger.com/atom/ns#">雲端(Cloud)</category><title>嘴炮不再！MDS 加密檔案系統釋出！</title><description>&lt;div&gt;
台灣的電玩機臺一直是一個很少人探討的遊戲產業，他們外銷機台到國際各大賭場，除了一直在設計讓人願意掏錢的有趣遊戲機外，也真真實實用到很多資訊科技。在多年前一次南部的經驗，有幸接觸到這樣神秘的產業，也幫他們開發過一些東西。而其中一項專案，就是一個加密檔案系統，讓他們可以保護他們的遊戲程式，避免被對手輕易偷走自家的程式。&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
於是就有了這個 MDS 檔案系統（Middle Stone Filesystem），採用 blowfish 演算法加密，支援 384-bit key 和同時多組 key 的存取。由於作業系統本身會很容易破解修改，要保留可以將解密工作和 key 放在額外的硬體鎖或晶片上，甚至是多組 key 可跨多個硬體晶片存放，增加破解的難度。所以因此也設計了一些單獨的讀取工具範例，讓硬體鎖的開發人員，可以參考做法，寫在其硬體韌體（Firmware）中。不過，更近一步和硬體整合的版本，礙於敏感，這次就沒有釋出了。&lt;br /&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
專案網址：&lt;/div&gt;
&lt;div&gt;
&lt;a href=&quot;https://github.com/cfsghost/mdsfs&quot;&gt;https://github.com/cfsghost/mdsfs&lt;/a&gt;&lt;br /&gt;
&lt;div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
其實，這專案多年來一直對周圍的朋友嘴炮說要開放出來，可是一直沒負諸行動（當然也是因為懶惰）。而今天看到 iCloud 被破解的新聞，受到一點資料安全問題的刺激，於是想起這個專案，所以就花點時間挖出來整理一下文件並往 Github 上丟。由於，這個基礎版本很久沒維護了，安全性如何我不能保證，但拿來做學習 FUSE 和加密演算法應用，倒是有點參考價值。如果有人想要接著開發，也非常歡迎！&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
另外，有些人反應不知道能拿來幹麻。關於這件事，其實把 MDS 當做一個簡單的加密打包工具，也是可以的。&lt;/div&gt;
&lt;h2&gt;
後記&lt;/h2&gt;
&lt;div&gt;
&lt;div&gt;
看到 iCloud 被破解的新聞，不由得讓人重新思考雲端和未來物聯網時代，資料安全的重要性。稍為瞭解真象的人通常都會跟你說，千萬不要相信任何人，將東西和隱私放在雲端。而通常你第一個要害怕的是雲端系統管理人員和系統設計人員的操守，他們是不需要經過任何破解手段，即可全權存取你資料的人。再來，我們要擔心的是系統的安全性，設計者和管理者能否真有能力去保護使用這的資料？這從使用者手上的 App、作業系統、瀏覽器，一直到雲端上的一切系統，都是必需要考量的範圍。也因為這樣種種的複雜情境，使用者實在難以得到真正安全的保護。&lt;br /&gt;
&lt;br /&gt;
原因其實很簡單，當檔案自你手邊的機器準備發出去的一刻開始，你的資料便以明碼的方式在程式、網路和機器間處理，或許你手上的 App 很安全，沒有後門；或許你的網路傳輸很安全，有加密所以不會被人監聽；或許雲端伺服器也很安全，經過重重保護。但是，只要有人在過程中找到漏洞，或在其中找到缺口並進入了資料庫，你的秘密就赤裸裸的躺在哪。&lt;br /&gt;
&lt;br /&gt;
包括我自己，總不認為自己運氣會這麼差，不會有人這麼有空或有能力去破解取得自己放在雲端上的資料。畢竟，這些意圖不軌的駭客，要對抗的是像 Apple、Google 這類比哥吉拉還可怕的大公司。我們也總是想，真要是被人看光光了，就當隱私賣給了別人，自己可以向這些大公司求償噱一筆。&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
就是這樣自我安慰的想法，讓我們從來就不真正在乎資料的安全性。那些所有信件和檔案都要用 GPG Key 先加密過再傳送的人，在我們的眼中，總覺得他們是個怪咖。但，可能隨時我們的報應就會來了呀。就算他們的檔案被偷光，沒有他們的 Key，那些檔案資料根本就是一堆沒意義的亂碼垃圾；而我們，就是什麼都被人看光光。&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
</description><link>https://fred-zone.blogspot.com/2014/09/mds.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-798228053387274467</guid><pubDate>Thu, 26 Jun 2014 01:13:00 +0000</pubDate><atom:updated>2014-06-26T09:13:51.155+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Node.js</category><category domain="http://www.blogger.com/atom/ns#">Web 相關技術</category><category domain="http://www.blogger.com/atom/ns#">心得分享、講座</category><category domain="http://www.blogger.com/atom/ns#">程式心得筆記</category><category domain="http://www.blogger.com/atom/ns#">自由軟體</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><title>【Node.js 小密技】使用 crypto 模組亂數產生字串</title><description>已非常熟悉 Node.js 的人都知道，crypto 模組是一個無中生有的神器。我們可以用它的 API 加工加密資料，也可以解開加密後的包裹。既然和密碼處理相關，當然也有亂數產生器，可供開發者產生一大堆的亂數資料。&lt;br /&gt;
&lt;br /&gt;
如果你需要一個 32Bytes 長度的亂數資料，可以直接呼叫 crypto.randomBytes() 並帶入長度後生成：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;var crypto = require(&#39;crypto&#39;);
var buf = crypto.randomBytes(32);&lt;/pre&gt;&lt;/div&gt;註：回傳的結果 buf ，是一個 binary buffer，而每一個 Bytes 都會是範圍在 0 ~ 255 （0x00 ~ 0xff）的數字。&lt;br /&gt;
&lt;br /&gt;
不過，在多數應用中，我們更常需要一個亂數字串，而非二進位的亂數資料，這時就需要做一些進一步的加工處理。方法其實不只一種，如建表、取代不可見字元碼等方式都可以，沒有標準做法。但懶惰的開發者們，多半是直接利用 Buffer 內建的 API 來將資料轉換成字串，並指定編碼方式來達成。&lt;br /&gt;
&lt;br /&gt;
下面兩種方法都可行：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;buf.toString(&#39;hex&#39;);
buf.toString(&#39;base64&#39;);&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
只是，使用十六進位（hex），會讓你的最後的字串單純只有 0-9 和 a-f 的字元存在，所以使用 base64 是比較好的選擇，讓字串多變並複雜些。&lt;br /&gt;
&lt;br /&gt;
註：base64 編碼是以 6 bits 為單位來進行處理，而 1 Byte 有 8bits。如果你的資料長度非 6 bits 的整數倍長度，base64 會在結尾補上『=』。有些人覺得多了一些『=』符號很醜，就會用 replace() 替代掉或者是一開始就取 6 bits 整數倍長度的亂數資料（如：3 bytes、6 bytes 等等）。&lt;br /&gt;
&lt;br /&gt;
有些情況，我們需要特定長度的亂數字串，也可以搭配使用 substr()，只是要確定生成的字串長度，比我們想要的字串長度還要長。下面的範例，就是取八個字元長度的字串：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;buf.toString(&#39;base64&#39;).substr(0, 8);&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
當然，若你想要一次搞定，也可以將之前所說的，全部串在一起寫：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;var randomString = crypto.randomBytes(32).toString(&#39;base64&#39;).substr(0, 8);&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
&lt;h2&gt;後記&lt;/h2&gt;最近在開發的 Project 中，一直在處理 Token 以及密碼相關的東西，常用到亂數字串。之前用過不少方法，每次要寫不少 code 相當讓人煩燥。所以記錄下來，以後自己直接 copy &amp;amp; paste 即可，也讓有相同需求的人可以直接參考使用。</description><link>https://fred-zone.blogspot.com/2014/06/nodejs-crypto.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>2</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-2365263831422066094</guid><pubDate>Thu, 19 Jun 2014 00:06:00 +0000</pubDate><atom:updated>2014-06-19T08:09:09.079+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Startup</category><category domain="http://www.blogger.com/atom/ns#">商業</category><category domain="http://www.blogger.com/atom/ns#">心情筆記</category><category domain="http://www.blogger.com/atom/ns#">雲端(Cloud)</category><title>我的創業原點：科技和藝術的結合</title><description>&lt;table align=&quot;center&quot; cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;margin-left: auto; margin-right: auto; text-align: center;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;http://chinese.engadget.com/images/2006/03/P1000708.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://chinese.engadget.com/images/2006/03/P1000708.jpg&quot; height=&quot;359&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;義大利咖啡館－科技和人文的結合&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
看到時不時有新聞在報 POS，一時興起，就去把舊照片翻出來，沒想到癮科技還保留了快十年前的照片和報導『&lt;a href=&quot;http://chinese.engadget.com/2006/03/25/use-tpc-in-ordering-food/&quot;&gt;義大利咖啡館－科技和人文的結合&lt;/a&gt;』。&lt;br /&gt;
&lt;br /&gt;
這套 POS，是在當時在經營 Coffee Shop （以現在的角度來看，其實是半個 Working Space）時所開發的，店在板橋江子翠捷運站旁（建築物前還有一座三層樓高的羅馬柱），共三層樓約四五百坪左右，店裡沒有對外掛招牌並擺滿了各種收藏品和古董家具，不知道的人看起來像間古董家俱店，不太敢進來，所以來的都是慕名或是口耳相傳，以及喜歡我們咖啡的人，因此也有人以『神秘咖啡廳』稱呼。&lt;br /&gt;
&lt;br /&gt;
不過，Coffee Shop 其實一直不是主業，只是公司的招待、展示中心和團隊休閒區，而應用、工程、軟體開發和軟硬體整合的產品開發才是我們主要的工作。所以我永遠窩在店裡的某一角，寫著我喜歡寫的程式，周遭來來去去的客人並不真的清楚我是誰。&lt;br /&gt;
&lt;br /&gt;
還記得，點餐系統軟體、平板硬體和後面印單系統整套做出來並實際上線，大概是在 2004 年（大概是高二時），報導則是 2006 年的事了。而且因為這套系統和管理流程設計，讓幾百坪的店面，只需要一兩個人就可以完全掌控、經營和服務，讓我們著實省下不少功夫。雖然很懷念當時的日子，但一想到惡房東後來給我帶來的多年痛苦，以及官司上的問題，至今我仍然還是恨的牙癢癢，因而有很長很長一段時間，我絕口不提這段往事。&lt;br /&gt;
&lt;br /&gt;
所以這也是為什麼，多年後看到這標題，感觸極深，當時『科技與人文的結合』和『科技與藝術的結合』這個標語，一直都掛在店裡的展示大螢幕上，因為這是我們自己對設計的最高期許，也是一直身體力行的方向。許多進進出出拜訪我們的人和國內外大佬們，應該多少都看過。只是後來大多數人再次使用這段話時，是在多年後 Apple 的產品身上，這也算是種台灣的悲哀吧。&lt;br /&gt;
&lt;br /&gt;
很多人不知道，我其實非常喜歡喝咖啡，甚至會燒，只不過後來若不是真的跟我很熟的人，可能從來都沒看過我喝。當時店裡的紀念款 La Marzocco 機器，現在還好好保存著，期盼哪一天能夠在我比較穩定的時候，再次拿出來使用或是與朋友分享。&lt;br /&gt;
&lt;br /&gt;
不曉得還有多少人記得我們這家店？但無論如何，總有一天，我會讓他復業。不為別的，就為了從小所抱著的夢，這是我一頭栽進創業和改變世界的原點。</description><link>https://fred-zone.blogspot.com/2014/06/blog-post.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-507987350168364792</guid><pubDate>Sat, 14 Jun 2014 22:15:00 +0000</pubDate><atom:updated>2014-06-16T01:28:23.082+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Express Web Framwork</category><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">Node.js</category><category domain="http://www.blogger.com/atom/ns#">Web 相關技術</category><category domain="http://www.blogger.com/atom/ns#">技術新知</category><category domain="http://www.blogger.com/atom/ns#">程式心得筆記</category><category domain="http://www.blogger.com/atom/ns#">自由軟體</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><title>Express 4 來了！好用的 Router 機制</title><description>還記得前陣子，就在書將出版之際，半路殺出來最新的 Express 4，使得筆者手忙腳亂，緊急通知出版社修改書的內容。將所有使用到 Express 的範例，都特別加上版本號的指定，讓讀者在照著範例做時，能夠使用舊的 Express 3.0，而不會發生任何問題。最新的 Express 4 雖然效能提升不少，也有一些好用的新東西，但和舊版有些微 API 的不相容，此外，也因為是新東西，可能很多東西的支援會發生問題，如果舊版本在上線的產品中運行相當穩定，建議先不要急著升級。&lt;br /&gt;
&lt;br /&gt;
Express 4 做了一些改進，有不少新東西，其中相當值得提的一項就是路由機制（Router）的翻新。如果你以前在使用 Node.js + Express 開發網站服務時，因為 Router 很難管理而痛苦不已，這將是一個你會愛上的功能改進。&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;傳統的使用方法&lt;/h2&gt;傳統的 Router 設定方法依然可以運作，我們可以放心使用。&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;var express = require(&#39;express&#39;);

// Create express application
var app = express();

// Apply this router on (/apis/user)
app.get(&#39;/&#39;, function(req, res) {
&amp;nbsp; &amp;nbsp; // Home
});
app.listen(8000);
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;新 Router 物件的使用方法範例&lt;/h2&gt;除了舊的方法之外，你可以用 Router() 去建立一個 Express 的 Router 物件，然後管理你一系列的路由規則。&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;var express = require(&#39;express&#39;);

// Create express application
var app = express();

// Create a router to handle routes for a set of &amp;nbsp;user API
var userAPI = express.Router();

// Sign in (/apis/user/signin)
userAPI.post(&#39;/signin&#39;, function(req, res) {
&amp;nbsp; &amp;nbsp; // ...
});

// Sign out (/apis/user/signout)
userAPI.get(&#39;/signout&#39;, function(req, res) {
&amp;nbsp; &amp;nbsp; // ...
});

// Sign up (/apis/user/signup)
userAPI.post(&#39;/signup&#39;, function(req, res) {
&amp;nbsp; &amp;nbsp; // ...
});

// Apply this router on (/apis/user)
app.use(&#39;/apis/user&#39;, userAPI);
app.listen(8000);
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
&lt;h2&gt;一次指定多種方法到同個路徑&lt;/h2&gt;有時候，同一個路徑我們想賦予他不同的方法（如：GET 或 POST），讓它有不同的功能。這尤其是在開發 Restful API 時，非常常用。像是如果我們要設計一套管理使用者帳號（User）的 API 時。&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;var userMgrAPI = express.Router();
// A set of API to manage users (/apis/admin/user/:username)
userMgrAPI.route(&#39;user/:username&#39;)
&amp;nbsp; &amp;nbsp; .get(function(req, res) {
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // Getting user information
&amp;nbsp; &amp;nbsp; })
&amp;nbsp; &amp;nbsp; .post(function(req, res) {
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // Create a new user
&amp;nbsp; &amp;nbsp; })
&amp;nbsp; &amp;nbsp; .delete(function(req, res) {
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // Delete specific user
&amp;nbsp; &amp;nbsp; })
&amp;nbsp; &amp;nbsp; .put(function(req, res) {
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // Update
&amp;nbsp; &amp;nbsp; });

app.use(&#39;/apis/admin&#39;, userMgrAPI);
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
&lt;h2&gt;檢查 Parameter 的 Middleware&lt;/h2&gt;前面的例子，在路由上設計了一個 username 的 Parameter，每個方法的 Handler 都會用到它。但有一個需求是，既然每個 Handler 都會用到這個 Parameter，也需要檢查這個使用者帳號是否存在，那麼是否可以用類似 Middleware 的做法，寫一次程式套用在所有方法上？&lt;br /&gt;
&lt;br /&gt;
答案是可以的，你可以直接使用 param() 設計一個 Middleware，去安插一個檢查程式在前面，並指定他要檢查什麼 Parameter 欄位。&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;userMgrAPI.param(&#39;username&#39;, function(req, res, next, username) {
&amp;nbsp; &amp;nbsp; if (username == &#39;fred&#39;) {
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; next();
&amp;nbsp; &amp;nbsp; }else {
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; res.send(404);
&amp;nbsp; &amp;nbsp; }
});

// userMgrAPI.route(&#39;user/:username&#39;) ...&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
&lt;h2&gt;設計一個其他的 Middleware&lt;/h2&gt;如果你過去已經熟悉 Express 的開發，應該對開發一個 Middleware 不陌生，在 Express 4 的 Router 上，你也可以引用。通常我們會設計一個 Middleware 去檢查使用者登入與否，或是前面例子所提及的，檢查使用者是否存在，而不需要在每個路徑的 Handler 中都寫一次。&lt;br /&gt;
&lt;br /&gt;
下面的例子和前一個例子有相同功能：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;userMgrAPI.use(function(req, res, next) {
&amp;nbsp; &amp;nbsp; if (req.params.username == &#39;fred&#39;) {
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; next();
&amp;nbsp; &amp;nbsp; }else {
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; res.send(404);
&amp;nbsp; &amp;nbsp; }
});

// userMgrAPI.route(&#39;user/:username&#39;) ...&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
&lt;h2&gt;後記&lt;/h2&gt;&lt;br /&gt;
Express 4 帶來了新的方法，讓我們可以更好的管理 Router，這讓開發者們可以輕鬆許多，如果你有新的專案要開跑，不妨快來嘗試一下全新的 Express！</description><link>https://fred-zone.blogspot.com/2014/06/express-4-router.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-5049328936503549000</guid><pubDate>Sat, 07 Jun 2014 22:44:00 +0000</pubDate><atom:updated>2014-06-08T14:28:45.343+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">Node.js</category><category domain="http://www.blogger.com/atom/ns#">Web 相關技術</category><category domain="http://www.blogger.com/atom/ns#">技術新知</category><category domain="http://www.blogger.com/atom/ns#">有趣新構思</category><category domain="http://www.blogger.com/atom/ns#">程式心得筆記</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><title>Feature-oriented Programming with Node.js</title><description>Feature-oriented Programming（FOP）這個名詞對很多人來說應該相當陌生，這是一個鮮少人討論的開發模式，尋找了一下，也找不太到中文的說明和翻譯，所以我就暫時稱它為『特色導向程式開發』。如果你有興趣，可以去搜尋它，或使用關鍵字『Feature-oriented Software Development』找到更多資源。也有一個以 FOP 概念所改良的 C++ 語言『FeatureC++』，可以參考。&lt;br /&gt;
&lt;br /&gt;
因為筆者眼前專注於 Node.js 的發展，所以也用 Node.js 實作了一非常個簡單的 Framework 『wag.js』，讓我們可以在 JavaScript 語言的環境下，引用 FOP 的開發概念來設計軟體。你可以用 npm 直接安裝他：&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;npm install wag.js&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
之所以會一時興起研究 FOP，是因為多年以來，一直為了重覆開發所苦，很多相同的東西一直不停重覆做了又做，總覺得自己一直在浪費時間，所以想試著尋找個方法來改善眼前的窘境。最麻煩的問題在於，雖然早就應用了許多模組化方法，我們也能把很多功能事先做好打包好，但仍然像個有數不清節點的連連看遊戲，讓人花不少時間在上面編織程式軟體。重點是，當其中有絕大多數的過程是一樣的時候，總讓人做得很無力。&lt;br /&gt;
&lt;br /&gt;
你可以想一想，當你想要寫一個新的專案時，你通常最大的阻力是什麼？又有多少繁雜的事，每次開新專案時都重覆在做？而最令人煩燥的是，因為專案類型的不同，有些微的差異，所以難以用一個標準自動化的方式去完成。&lt;br /&gt;
&lt;br /&gt;
這也是一般人和工程師最大的差別，甚至是造成溝通不良。一般人總是想著是我要什麼功能、再增加哪些功能；工程師則是一直在想著，我要挑選使用哪些功能模組，又該怎麼從數不清的可能中，選一個可行的方法組裝他們？如果想成捏黏土來比喻，一般人會捏個大概形狀，再往上慢慢堆想要的東西，慢慢趨近成果，而工程師會捏好各個部位，再把他一一拼湊起來。&lt;br /&gt;
&lt;br /&gt;
我想，當你能回憶起客戶或老闆跟你說：『這功能很簡單，你應該能馬上做好』，你應該就能更明白我所說的，一般人與工程師的思維差異。不過，事實上，工程師思維和一般人沒兩樣，也是把東西堆起來，只是，工程師所設計出來的各種部位的元件，總是很難拼湊在一起，必需要花些功夫。&lt;br /&gt;
&lt;br /&gt;
到底，有沒有什麼辦法，可以減少各種元件和功能模組的拼湊困難呢？讓我們可以更專心的做一個有更多功能的產品？或是更快將產品捏出來呢？&lt;br /&gt;
&lt;br /&gt;
經過一些試驗後發現，Feature-oriented Programming 是個很值得嘗試的開發模式，概念如其名特色功能導向（Feature-oriented），選擇需要或想實作的產品特色後，再做細節修正。你可以到 wag.js 的 examples 目錄找到一些範例，會更瞭解其概念。&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;&lt;a href=&quot;https://github.com/cfsghost/wag.js&quot;&gt;https://github.com/cfsghost/wag.js&lt;/a&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
&lt;h2&gt;Hello World&lt;/h2&gt;首先看 helloworld 這支範例程式：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;var Wag = require(&#39;wag.js&#39;);
var wag = new Wag();

// Create an app with web engine
var app = wag.mixin(&#39;web&#39;);

// Run app
app.run(function() {
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; app.engine.listen(8000);
});&lt;/pre&gt;&lt;/div&gt;這支程式執行後，會運行一支 Web Server 在 8000 port，若使用瀏覽器去開，則會得到一個 『Hello world!』的訊息。&lt;br /&gt;
&lt;br /&gt;
程式的執行邏輯很簡單：建立一個 wag.js 的物件，然後用這個物件去建立一個 app，並為其引入 web 引擎，最後將這個 app 跑起來初始化，待初使化完成後就去跑 app 裡的 listen() ，開始監聽 8000 port。&lt;br /&gt;
&lt;br /&gt;
比較特別要說明的部份，是在建立 app 時，便要使用 mixin() 選擇需要引入哪些引擎功能。因為大多數 Node.js 開發者對 Express 非常熟悉，且有高需求量，所以筆者已經設計了一個名為『web』的功能模組來包裝了 Express ，以便示範和供多數開發者使用。為了與 Node.js 本身的『模組』一詞區隔，在 wag.js 裡面便把它叫做引擎（Engine）。&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;更複雜的應用程式&lt;/h2&gt;若是要開發更複雜的應用程式，我們除了引入 wag.js 已經內建的 web 引擎外，還可以以 web 引擎為基礎，做擴充、修改或是引伸設計。接下來的另一個範例 guestbook （留言板）就可以說明：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;var Wag = require(&#39;wag.js&#39;);

var app = new Wag();
app.addPath(&#39;engineDirs&#39;, __dirname + &#39;/engines&#39;);
app.mixin([
&amp;nbsp; &amp;nbsp; &#39;web&#39;,
&amp;nbsp; &amp;nbsp; &#39;guestbook&#39;
]);

app.run(function() {
&amp;nbsp; &amp;nbsp;app.engine.listen();
});&lt;/pre&gt;&lt;/div&gt;和前一個 helloworld 相較之下，我們多指定一個路徑，讓 wag.js 去找到我們自定的引擎（Engine）載入。然後除了 web 引擎之外，我們也另外增加了一個自定的 guestbook 引擎去擴充 app 的功能，讓 app 可以顯示留言的內容。&lt;br /&gt;
&lt;br /&gt;
如前文所提到，開發概念上，就是在一開始選擇並引入想要的功能，創造一個合成好的程式，最後再去執行和啟動這個程式。&lt;br /&gt;
&lt;br /&gt;
wag.js 尋找並載入引擎的順序是：&lt;br /&gt;
&lt;br /&gt;
&lt;ol&gt;&lt;li&gt;wag.js 內建的引擎&lt;/li&gt;
&lt;li&gt;開發者指定的路徑&lt;/li&gt;
&lt;/ol&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;br /&gt;
在這個範例中， wag.js 會載入三個引擎的程式碼然後合成：&lt;br /&gt;
&lt;br /&gt;
&lt;ol&gt;&lt;li&gt;wag.js 內建的 web&amp;nbsp;引擎&lt;/li&gt;
&lt;li&gt;開發者自定的&amp;nbsp;web&amp;nbsp;引擎&lt;/li&gt;
&lt;li&gt;開發者自定的&amp;nbsp;guestbook 引擎&lt;/li&gt;
&lt;/ol&gt;&lt;br /&gt;
&lt;h3&gt;基於 web 引擎的擴充&lt;/h3&gt;你可以在 engines 裡面找到一個自定的 web 引擎，事實上，如果你比對了 wag.js 內建的 web 引擎後就會發現，我們並非是在重寫一個新的，而是繼承了 wag.js 內建的 web 引擎後，修改並加上了一些功能。&lt;br /&gt;
&lt;br /&gt;
wag.js 內建原生的 web 引擎長下面這模樣：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;var express = require(&#39;express&#39;);

var Web = module.exports = function() {
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; var self = this;

&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; self.express = express();
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; self.server = null;
};

Web.prototype.configure = function(callback) {
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; var self = this;

&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; callback();
};

Web.prototype.routers = function(callback) {
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; var self = this;

&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; self.express.get(&#39;/&#39;, function(req, res) {
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; res.send(&#39;Hello! Wag Web!&#39;);
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; });

&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; callback();
};

Web.prototype.listen = function(port) {
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; var self = this;

&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; var _port = port || Web.Wag.settings.web.port;

&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; self.configure(function() {
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; self.routers(function() {
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; self.server = self.express.listen(_port);
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; });
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; });
};&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
我們在 app 中自定的引擎：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;var path = require(&#39;path&#39;);
var express = require(&#39;express&#39;);
var bodyParser = require(&#39;body-parser&#39;);

var Web = module.exports = function() {
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; var self = this;

&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Web.super_.call(this);
};

Web.prototype.configure = function(callback) {
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; var self = this;

&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; self.express.set(&#39;view engine&#39;, &#39;jade&#39;);
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; self.express.set(&#39;views&#39;, path.join(__dirname, &#39;..&#39;, &#39;..&#39;, &#39;views&#39;));
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; self.express.use(express.static(path.join(__dirname, &#39;..&#39;, &#39;..&#39;, &#39;public&#39;)));
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; self.express.use(bodyParser());

&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; callback();
};

Web.prototype.routers = function(callback) {

&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // Clear route settings
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; callback();
};&lt;/pre&gt;&lt;/div&gt;當這兩支引擎合成後，會和下面的程式有相同功能：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;var express = require(&#39;express&#39;);

var Web = module.exports = function() {
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; var self = this;

&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; self.express = express();
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; self.server = null;
};

Web.prototype.configure = function(callback) {
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; var self = this;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; self.express.set(&#39;view engine&#39;, &#39;jade&#39;);
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; self.express.set(&#39;views&#39;, path.join(__dirname, &#39;..&#39;, &#39;..&#39;, &#39;views&#39;));
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; self.express.use(express.static(path.join(__dirname, &#39;..&#39;, &#39;..&#39;, &#39;public&#39;)));
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; self.express.use(bodyParser());
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; callback();
};

Web.prototype.routers = function(callback) {
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; var self = this;


&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; callback();
};

Web.prototype.listen = function(port) {
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; var self = this;

&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; var _port = port || Web.Wag.settings.web.port;

&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; self.configure(function() {
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; self.routers(function() {
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; self.server = self.express.listen(_port);
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; });
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; });
};&lt;/pre&gt;&lt;/div&gt;&lt;h4&gt;這支自定的 web 引擎程式，大概有三個部份要說明：&lt;/h4&gt;&lt;br /&gt;
首先，運用 Web.super_.call()，讓引擎被初始化時，可以引用 wag.js 內建的建構子（Constructor），而不是直接取代 wag.js 內建的 web 引擎。&lt;br /&gt;
&lt;div&gt;&lt;br /&gt;
接著，取代原生的 configure 方法，為其加上 Jade 和 view 的支援，也讓 express 可以存取 public 路徑底下的靜態文件，也支援 body 的處理。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
將原生的 routers 方法給取代並不做任何事，讓原生引擎中對應『/』路徑的 hello world 訊息失效。（我們會在稍後的 guestbook 引擎內設計新的路徑和頁面內容）&lt;br /&gt;
&lt;h3&gt;建立一個新的引擎，為應用程式增加功能&lt;/h3&gt;既然要做留言板，就少不了顯示留言和張貼留言的功能，為了增加這些功能，我們可以設計一個新的引擎『guestbook』來達成。&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;var Guestbook = module.exports = function() {
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; var self = this;

&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Guestbook.super_.call(this);

&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; self.articles = [
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; {
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; title: &#39;What\&#39;s Wag.js&#39;,
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; text: &#39;Wag.js is a feature-oriented based application framework.&#39;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; },
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; {
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; title: &#39;What\&#39;s News&#39;,
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; text: &#39;Guestbook example was added.&#39;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ];
};

Guestbook.prototype.routers = function(callback) {
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; var self = this;

&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; function routeInit() {

&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; self.express.get(&#39;/&#39;, function(req, res) {
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; res.render(&#39;index&#39;, {
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; articles: self.articles
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; });
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; });

&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; self.express.get(&#39;/post&#39;, function(req, res) {
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; res.render(&#39;post&#39;);
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; });

&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; self.express.post(&#39;/post&#39;, function(req, res) {

&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if (!req.body.title || !req.body.text) {
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; res.end(&#39;Please enter title and message!&#39;);
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }

&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; // Save to article database
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; self.articles.push({
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; title: req.body.title,
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; text: req.body.text
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; });

&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; res.redirect(&#39;/&#39;);
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; });

&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; callback();
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }

&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; Guestbook.super_.prototype.routers.apply(this, [ routeInit ]);
};&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
簡單來說，guestbook 引擎，也是繼承了之前的 web 引擎再做擴充，所以可以看到在一開始的建構子（Constructor）中，也用到了 super_.call()。然後我們定義了一個名為『articles』的 Member，做為存放留言使用。&lt;br /&gt;
&lt;br /&gt;
為了要顯示網頁，想當然爾要定義路徑和將回傳的網頁內容，分別為『/』、『/post』。而你可以看到的 Guestbook.super_.prototype.routers.apply()，只是為了繼承之前 web 引擎的 routers 方法，避免覆概舊有的功能（雖然之前的 web 引擎內的 routers 已經沒有任何功能，所以要省略也可以）。&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;總結&lt;/h2&gt;&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;p1 = j • f &amp;nbsp; &amp;nbsp; &amp;nbsp; -- program p1 has features j and f&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
在 FOP 的概念下，應用程式就是許多元件（在 Wag.js 之下是以引擎 Engine 為單位）所拼成的一個大集合體，如果你有 OOP （物件導向的概念）你可以想像它是一個不斷串接並繼承下去的物件，此外，引入的先後順續是有差別的，這會牽涉到相依性的問題。&lt;br /&gt;
&lt;br /&gt;
FOP 能讓開發者如預期的慢慢把功能疊上去，如果設計得當，同一個功能可以拿到各種專案上去使用，只需要讓應用程式引入便馬上可以運作。如筆者最近正在嘗試用 wag.js 開發數個完全不同用途的網站專案，都會用到會員註冊和管理的功能。但藉由引擎這樣的設計，只需要將直接將功能搬移到不同專案中，就可以讓會員系統在不同專案中運作，而且具有擴充的彈性，而不需要為不同專案重新開發。&lt;br /&gt;
&lt;br /&gt;
&lt;h2&gt;後記&lt;/h2&gt;&lt;br /&gt;
wag.js 尚未完全實做 FOP 的所有概念，只是粗淺的起頭而已。&lt;br /&gt;
&lt;br /&gt;
未來應該要讓引擎本身，也能像 app 那樣，可以用數個引擎合成出來。&lt;br /&gt;
&lt;br /&gt;
若是 wag.js 這樣的開發概念可行，之後也許可以推出像 npm 這樣的線上模組系統也說不定。&lt;/div&gt;</description><link>https://fred-zone.blogspot.com/2014/06/feature-oriented-programming-with-nodejs.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-8018987378842561687</guid><pubDate>Sat, 03 May 2014 23:30:00 +0000</pubDate><atom:updated>2014-05-04T07:30:30.587+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Embedded System</category><category domain="http://www.blogger.com/atom/ns#">Express Web Framwork</category><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">Node.js</category><category domain="http://www.blogger.com/atom/ns#">Web</category><category domain="http://www.blogger.com/atom/ns#">Web 相關技術</category><category domain="http://www.blogger.com/atom/ns#">商業</category><category domain="http://www.blogger.com/atom/ns#">心情筆記</category><category domain="http://www.blogger.com/atom/ns#">推薦好書</category><title>不一樣的 Node.js！終於完成的新書前來報到！</title><description>這陣子，最怕的就是有人問：『書什麼時候要發行？』，參加社群聚會或活動時，有人會問我，平常周圍朋友也會問我，工作上隊友和客戶都會問我，只有一些親人根本不知道不知道我在寫書，所以不會問我。如果你問我，寫書這件事上，你最怕的是什麼？我會跟你說，出版社截稿日期的死線（Deadline）並不是最可怕的，最可怕的是，它是一種詛咒，讓周圍人都會特別關心你的一種魔法。而這個關心的壓力，讓人喘不過氣來。&lt;br /&gt;
&lt;br /&gt;
還好，書總算從初稿、校稿到排版完成，直到出版社編輯傳來最後的封面設計，才讓人鬆了一口氣。可以告訴大家，書總算要真正出來了！&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEidLmabXDUy6HvubZMQgj5wuJN0AKclipt9QKrWmH0ivBtpduaRUCBkabla_cPkvK7RPMTttYyxX4n08ooUvN3RDBZa_RLz7OAm_3SjVXyyyd4WzwQvuvWSuZXlZEZRcGiIqT2gH2ve2Ho/s1600/0426_XW14002%E5%B0%81%E9%9D%A2%E5%AE%8C%E7%A8%BF-01.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEidLmabXDUy6HvubZMQgj5wuJN0AKclipt9QKrWmH0ivBtpduaRUCBkabla_cPkvK7RPMTttYyxX4n08ooUvN3RDBZa_RLz7OAm_3SjVXyyyd4WzwQvuvWSuZXlZEZRcGiIqT2gH2ve2Ho/s1600/0426_XW14002%E5%B0%81%E9%9D%A2%E5%AE%8C%E7%A8%BF-01.jpg&quot; height=&quot;297&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
事實上，國內 Node.js 的書其實並不多，其中幾乎多為翻譯書，或是從簡體直接翻成繁體中文的書。這讓這幾年一直在到處推廣 Node.js 的我，相當困擾。每當有人問起 Node.js 有什麼書可以看時，我總是無法推薦出很多本給大家參考。所以，當出版社的編輯跟我說，希望寫一本 Node.js 的中文書時，我其實相當開心。&lt;br /&gt;
&lt;br /&gt;
還記得當時接下這個任務後，我便開始思考和回想，在推廣和使用 Node.js 時，所遭遇到的困難是什麼？我能否藉這本書來幫助讀者排除這些困難？甚至給讀者一個更大的視野，一個 Node.js 可以發揮在各種領域的可能性。&lt;br /&gt;
&lt;br /&gt;
此外，網路時代來臨後，有太多的新資料都可以從網路上取得，這本書要提供什麼內容，更是需要被探討的問題。尤其是 Node.js 這樣版本號快速上升的新技術，可能書還沒寫完，內容就已經過時。在種種思索下，期望本書的定位，將帶給大家的不是絕對的技術新知，而是一本學習 Node.js 的參考書和技術指南，讓想入門的讀者可以學會怎麼使用 Node.js，讓已經懂的人可以知道 Node.js 還能做些什麼不一樣的事，更進一步知道從哪裡去得到更多 Node.js 的新知。如果這本書在幾年後，仍有六成的資料值得參考，那就算完美達成我賦予它的任務了。&lt;br /&gt;
&lt;br /&gt;
從引領初學者的角度來看，並參考了一些市上的 Node.js 書籍，也從過去教育訓練和推廣的場合發現一個現象。Node.js 不是什麼很難的新東西，如果有人已經很熟悉 JavaScript 語言，而且能寫的很好，Node.js 根本是小菜一碟。所以，多半不得其門而入的人，是對 JavaScript 不這麼熟悉，或是想使用 Node.js 技術進行第一次接觸程式開發的人。只不過，往往市面上的 Node.js 書籍，多半假設讀者已經相當了解 JavaScript 語言，所以對 JavaScript 不會多加說明，這的確會造成一些人的困擾。&lt;br /&gt;
&lt;br /&gt;
所以，在寫這本『不一樣的 Node.js』時，就希望從 JavaScript 語言開始講起，除了先破除許多人對 JavaScript 的長久的迷思，也讓從來沒接觸過程式語言或是半路出家來寫 JavaScript 程式的人，可以從語言的角度入門，再去一步步學習 Node.js 的應用程式開發。又因為在使用 Node.js 開發時，有很多的問題是出現在 JavaScript 語言的概念上，所以在書的最後，也會特別提到一些 JavaScript 語言的一些問題和陷阱。因此，稍微著重或是從 JavaScript 語言的角度出發，是本書比較大的特色之一。&lt;br /&gt;
&lt;br /&gt;
當然，介紹 Node.js 基本知識是肯定有的，只是與一些快速直接切入開發網站系統的教學書不一樣，這本書在進入 MVC Web Framework 章節前，會先說明如何撰寫『純』的 Node.js 應用程式，包括了開發傳檔程式和後門程式等。&lt;br /&gt;&lt;br /&gt;前面說到這本書也期望給讀者更大的視野，讓 Node.js 不只是聚焦在 Web 上。所以，在本書的後段章節，除了會提及和整理桌面應用程式的開發，也會討論如何在嵌入式（Embedded System）上使用 Node.js。&lt;br /&gt;
&lt;br /&gt;
無論如何，書總算是寫完了，雖然校稿了很多次，但難免有遺漏，還請包涵。只希望這本書能夠讓更多人喜愛使用 Node.js！&lt;br /&gt;
&lt;h3&gt;
後記&lt;/h3&gt;
世界上已經有太多人在探討如何使用各種 Web Framework 來開發網站應用程式，所以其實如果可以，想花多點章節在討論使用純的 Node.js，以及利用第三方模組來快速實作許多強大的功能。例如，教讀者使用 Node.js 寫 IRC Bot，然後讓大家可以自己寫 Twitch 的聊天室 Bot。&lt;br /&gt;&lt;br /&gt;
不過，這些想法，也許要等收集完讀者回饋後，或有要寫下本書後，再來看怎麼改進吧！不過至少有機會的話，肯定會在 Blog 持續討論相關的議題。:-)</description><link>https://fred-zone.blogspot.com/2014/05/nodejs.html</link><author>noreply@blogger.com (Fred Chien)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEidLmabXDUy6HvubZMQgj5wuJN0AKclipt9QKrWmH0ivBtpduaRUCBkabla_cPkvK7RPMTttYyxX4n08ooUvN3RDBZa_RLz7OAm_3SjVXyyyd4WzwQvuvWSuZXlZEZRcGiIqT2gH2ve2Ho/s72-c/0426_XW14002%E5%B0%81%E9%9D%A2%E5%AE%8C%E7%A8%BF-01.jpg" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-6927974454995218462</guid><pubDate>Fri, 25 Apr 2014 09:37:00 +0000</pubDate><atom:updated>2014-04-25T17:51:29.306+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">HanGee 國民機運動</category><category domain="http://www.blogger.com/atom/ns#">OwaNEXT</category><category domain="http://www.blogger.com/atom/ns#">User Interface</category><category domain="http://www.blogger.com/atom/ns#">手機平台</category><category domain="http://www.blogger.com/atom/ns#">技術新知</category><category domain="http://www.blogger.com/atom/ns#">有趣新構思</category><category domain="http://www.blogger.com/atom/ns#">系統研發手札</category><title>OwaNEXT 計畫來吧！不懂技術也可以設計可用的 Android Launcher！</title><description>自 HanGee 國民機運動發起之後，我們開始從一個全新的思維和高度，來看待這個手機科技產業到底需要些什麼東西，甚至是創新發明這件事的困難是什麼。尤其是排除掉鬼遮眼的短線商業問題後，專注考慮怎麼讓產業提升、科技產業改變，更可以發現有很多東西是我們可以去嘗試的&lt;br /&gt;
&lt;br /&gt;
其中，使用者介面（User Interface、UI/UX）是一個相當重要的課題，當人類和使用者越來越依賴顯示和觸控技術與機器溝通，虛擬形式的使用者介面就更是不可缺少。因此近年來，這也是許多科技人之間的熱門話題。只是，在我們深入了解後發現，仍然存在許多可以改進的空間。&lt;br /&gt;
&lt;br /&gt;
講可以改進，並非是我們真有一個絕佳的 UI/UX 想法，或是覺得現今已經存在的使用者介面和模式有什麼不好，而是我們認為，設計方法和工具有很大的進步空間。我們都相信，在手機產業蓬勃的今天，設計師們早就已經花了大量時間去研究使用者的行為，並為其設計出不少使用者界面，甚至直到此時此刻，世界上隨時都有新的 UI 被設計出來。不過最大的問題是，設計師腦中的想法很容易被創造，但可以用的原型（Prototype）卻難以被製造出來，需要很大的成本和很高的門檻。&lt;br /&gt;
&lt;br /&gt;
的確，打造實際可以用的原型（Prototype）無論在哪裡，一直都是人類進步的過程中最大的問題之一，更可怕的是，往往人類的思考和創意，都因為牽就工具和打造原型的方法，而受到挶限。還記得在你設計出一個自認傑作的使用者界面當下，工程師告訴你做不到時，你有多麼難過嗎？還記得你告訴自己開始要有 Sense 的去設計 UI 後，你總是設計出平淡無奇的作品嗎？這些都是我們被制約的實際例子，當我們無法很容易取得或使用多彩多姿的顏料，我們就只能畫沒有色彩的素描，你我的創意和創作就沒有了明天。&lt;br /&gt;
&lt;br /&gt;
那麼，我們可以做些什麼呢？&lt;br /&gt;
&lt;br /&gt;
事實上，就在 HanGee 國民機運動開始不久後，筆者就建立了一個『 UI 先進技術研究組』，並創造了一個名為 OwaNEXT 的專案，試圖找尋協助設計師更快打造出 UI 的方法，然後進一步設計出可用的工具。如果可以讓不懂程式技術的人和設計師，都能輕易打造出可用的 UI 原型，而且能夠真正直接被使用在實際環境上，勢必能讓 UI/UX 領域的工作和視野往前跨更大一步。&lt;br /&gt;
&lt;br /&gt;
OwaNEXT 的名字由來，一方面是以 Owa （芋頭的台語念法）來呼應蕃薯（HanGee），另一方面是向已逝的 Steve Jobs 所致敬。由於 Steve Jobs 在生前所創辦的 NeXT 公司和其先進的 NEXTSTEP 圖型化介面（後來被 Apple 收購回去，成為現今 Mac OS X 的前身），對後世資訊科技的 UI/UX 產生深遠的影響，故取用 NEXT 來當做專案名稱，更有期望 UI 系統的設計能進入下一個世代之意。&lt;br /&gt;
&lt;br /&gt;
經過了一些努力後，OwaNEXT 總算也實作出一些東西，盡可能讓設計師在不懂任何程式的情況下，可以設計出 Android Launcher，而且設計成果是實際可以在手機上使用的東西。&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;iframe allowfullscreen=&#39;allowfullscreen&#39; webkitallowfullscreen=&#39;webkitallowfullscreen&#39; mozallowfullscreen=&#39;mozallowfullscreen&#39; width=&#39;320&#39; height=&#39;266&#39; src=&#39;https://www.youtube.com/embed/gMOycMwNvFw?feature=player_embedded&#39; frameborder=&#39;0&#39;&gt;&lt;/iframe&gt;&lt;/div&gt;
&lt;br /&gt;
當然，如果只是平淡無奇的成果，也就沒有什麼什麼意思，也進一步探討實作酷炫特效並引入 3D 效果的可行性，而且更重要的是讓設計師選用或是剪貼（Copy&amp;amp;Paste）即可達成（一些人或許開始懷念早期網頁設計的剪貼設計模式了）：&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;iframe allowfullscreen=&#39;allowfullscreen&#39; webkitallowfullscreen=&#39;webkitallowfullscreen&#39; mozallowfullscreen=&#39;mozallowfullscreen&#39; width=&#39;320&#39; height=&#39;266&#39; src=&#39;https://www.youtube.com/embed/l0pUgAUDy7Q?feature=player_embedded&#39; frameborder=&#39;0&#39;&gt;&lt;/iframe&gt;&lt;/div&gt;
&lt;br /&gt;
這些都是初步成果，雖然已經不用寫太多程式即可完成這個 Launcher，但目前還沒有辦法讓設計師在完全不碰到程式碼的情況下實作這些 UI，這部份還有改進的空間。但是，藉由 OwaNEXT 的架構開發 Launcher，已經可以做到動態抽換其中的模組修改 UI，不只是改變美術風格而已，還可以改變整個使用者介面的排版和行為，就像一個全新的 Launcher 介面一般。如同小米 UI 那樣可客製化的功能，也可以藉由 OwaNEXT 達成，甚至是有更大的自由度供設計師們把玩。&lt;br /&gt;
&lt;br /&gt;在確認想法可行性之後，OwaNEXT 的下一個階段性任務，就是將設計方法系統化，除了更進一步包裝以減少技術門檻之外，也將開始著手打造真正的視覺化設計工具。&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;
&lt;b&gt;後記&lt;/b&gt;&lt;/h4&gt;
&lt;br /&gt;
在台灣創業多年，有太多的想法和願景，改變並創造一個更好的世界的心願一直存在於心中。但無奈過去總是因為眼前資源有限或是短線商業問題，讓許多認為正確的方向和想法，執行進度緩慢或胎死腹中，又或者是遭到許多業界朋友們誤會了我真實的想法。&lt;br /&gt;
&lt;br /&gt;
相當有趣，自從 HanGee 國民機運動發起後，拳腳得以發揮，由於周圍的朋友，看待事情的角度和高度不一樣了，也逐漸理解我真正想面對的問題是什麼，慢慢獲得許多人的關心和幫助。:-)</description><link>https://fred-zone.blogspot.com/2014/04/owanext-android-launcher.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>1</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-5783428950365056455</guid><pubDate>Sat, 19 Apr 2014 10:55:00 +0000</pubDate><atom:updated>2014-04-19T18:55:15.335+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">C/C++</category><category domain="http://www.blogger.com/atom/ns#">libuv</category><category domain="http://www.blogger.com/atom/ns#">Node.js</category><category domain="http://www.blogger.com/atom/ns#">QML</category><category domain="http://www.blogger.com/atom/ns#">Qt</category><category domain="http://www.blogger.com/atom/ns#">技術新知</category><category domain="http://www.blogger.com/atom/ns#">程式心得筆記</category><category domain="http://www.blogger.com/atom/ns#">自由軟體</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><title>Node.js 的原罪，和 QML 的不合作運動</title><description>在上個星期剛結束的 OSDC.tw 2014 活動上給了一個 Talk ，提及用 Node.js 結合 QML 來開發應用程式，讓我們可以使用 Node.js 和 JavaScript 來寫 QML 元件，不需要再使用 C/C++。事實上，這樣的技術看起來簡單，但實際上是困難重重。&lt;br /&gt;
&lt;br /&gt;
其實，要做 Qt 的 Node.js binding，有很多問題要克服，最直接的不外乎是 Event Loop，Node.js 本身是 JavaScript 語言，所以有自己的事件引擎，而 Qt 是一個圖形界面的 Toolkit，一樣也有自己的事件引擎，因此，我們要再解決一次與過去整合 GLib 一樣的問題（詳見舊文：&lt;a href=&quot;http://fred-zone.blogspot.tw/2012/09/glib-main-event-loop-nodejs-libuv.html&quot;&gt;探究如何整合 GLib Main Event Loop 和 Node.js 的 libuv&lt;/a&gt;），只不過這次要實作的是 Qt 的 QAbstractEventDispatcher。&lt;br /&gt;
&lt;br /&gt;
為了讓 Node.js 可以順利驅動 Qt，筆者之前花了一些時間研究，所以有了一個實驗性質的 Project 『&lt;a href=&quot;https://github.com/cfsghost/brig/&quot;&gt;Brig&lt;/a&gt;』在 Github，也為了 Qt 重新寫了一個新的 libuv 版本的 EventDispatcher。很幸運的，在實作完新的 EventDispatcher 後，已經可以順利的將 Node.js 和 Qt 的 Event Loop 接起來。&lt;br /&gt;
&lt;br /&gt;
但別高興的太早，還有個大問題存在。Qt 5.2 之後將 QML 內的 V4 JavaScript 換成了 Google V8 JavaScript Engine，並且為了一些特性，修改了 V8 的部份實作。很不巧的是，Node.js 的核心也是 V8，這意味著，如果我們使用自己的 Node.js Binding 去建立 QML Engine，程式會馬上崩潰並結束（Segmentation Fault）。&lt;br /&gt;
&lt;br /&gt;
種種問題，你可以說這是 Node.js 的原罪所致，這也是為什麼相當難幫 Qt 寫一個 Node.js 的 Binding，你在 NPM 上也根本找不到良好的 Qt Module 存在。最終我們的做法，還是修改了 Node.js 的部份程式，才能將他們整合在一起。</description><link>https://fred-zone.blogspot.com/2014/04/nodejs-qml.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-1971179480694759967</guid><pubDate>Wed, 16 Apr 2014 23:36:00 +0000</pubDate><atom:updated>2014-04-17T07:40:38.241+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Hacking 心得筆記</category><category domain="http://www.blogger.com/atom/ns#">OSDC</category><category domain="http://www.blogger.com/atom/ns#">心得分享、講座</category><category domain="http://www.blogger.com/atom/ns#">技術新知</category><category domain="http://www.blogger.com/atom/ns#">有趣新構思</category><category domain="http://www.blogger.com/atom/ns#">自由軟體</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><title>【OSDC.TW 2014 簡報釋出】當 QML 娶了 Node.js</title><description>&lt;a href=&quot;http://upload.wikimedia.org/wikipedia/commons/thumb/5/51/Rubber_Duck_Front_View_in_Night_20140107.jpg/800px-Rubber_Duck_Front_View_in_Night_20140107.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;br class=&quot;Apple-interchange-newline&quot; /&gt;&lt;/a&gt; 去年一年在萬里當兵，基隆對我來說就像旁邊的公園一樣，時常去走走逛逛，然後去廟口吃點東西，最後吹吹帶有奇怪味道的海風，然後依依不捨的回去萬里山中隱居。很遺憾，黃色小鴨在我退伍的第二天，才來到基隆，一直沒機會去看看牠，這也是為什麼我為這次的 Talk 取了一個這麼奇怪的題目。當然，在 OSDC 的活動上，大多數人無法理解這樣奇怪的主題，所以我也在場上直接換了一個時事標題，詳見已釋出的簡報檔：&lt;br /&gt;
&lt;br /&gt;
&lt;iframe frameborder=&quot;0&quot; height=&quot;400&quot; marginheight=&quot;0&quot; marginwidth=&quot;0&quot; scrolling=&quot;no&quot; src=&quot;http://www.slideshare.net/slideshow/embed_code/33621901&quot; width=&quot;476&quot;&gt;&lt;/iframe&gt;&lt;br /&gt;
&lt;br /&gt;
本次簡報的主題，將提到如何使用 Node.js 開發一個 QML 應用程式，以及如何使用 Node.js 去括充 QML 的功能。QML 能讓開發者很快設計出極酷炫的 UI，但最大的問題是他擁有一個殘廢的 JavaScript 支援，所以任何額外的功能擴充，都必需學會 C/C++ 才能達成，相當的難以開發。&lt;br /&gt;
&lt;br /&gt;
此外，在過去如果 QML 要與 Node.js 做橋接，通常都是以 Node.js 建一個本機的 HTTP Server，然後運用 QML 內建的仿 XHR（使用方式和 API 真的完全模仿），去本機 HTTP Server 要資料。或許這對很多 Web 開發者來說相當容易上手，但有趣的是，他不會有 jQuery，所以你要自己手刻 Ajax 的各種機制，甚至是 Long-polling，痛苦至極。&lt;br /&gt;
&lt;br /&gt;
想一下，我們即便是要與本機溝通，取得或監聽機器上的一些資訊，都要使用 Long-polling，這未免也太小題大做，一點也不簡單和直接。所以這也是為什麼要做 Qtjs，讓我們可以用 Node.js 直接來開發 QML 的元件，直接與 QML 溝通和傳遞資料，而不必再透過 HTTP 的方式。&lt;br /&gt;
&lt;br /&gt;
就以本次簡報的例子來說，我們用 QML 設計了一個 IRC 聊天室的前端，然後運用 Node.js 和第三方模組去連線到 IRC Server，最後把聊天內容收回來畫在 QML 的界面上。並使用 Node.js 設計了一個 QML 元件，使用方式如下：&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;
&lt;pre&gt;IRC {
&amp;nbsp; &amp;nbsp;&amp;nbsp;onReceived: {
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; console.log(nickname + &#39;: &#39;&amp;nbsp;+&amp;nbsp;message);
&amp;nbsp; &amp;nbsp; }
}&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
註：過去開發 QML 元件必需使用 C/C++，現在用 Node.js 即可開發。&lt;br /&gt;
&lt;br /&gt;
這次的範例程式，可以在 Github 上取得：&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://github.com/cfsghost/osdc-qtjs-example&quot;&gt;https://github.com/cfsghost/osdc-qtjs-example&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;後記&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
不過，正當有點空可以去看牠時，就聽到牠破掉的消息，然後點子原創者和基隆主辦單位的爭執，讓人好生無言。我實在很難想像，泡在基隆帶有異味的海裡，是什麼樣的感覺。黃色小鴨來到基隆受苦受難，牠大概一輩子無法忘懷這次的經驗。&lt;br /&gt;
&lt;br /&gt;
說實話，黃色小鴨與基隆一點不搭軋，這根本是種奇怪的組合呀！&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;http://upload.wikimedia.org/wikipedia/commons/thumb/5/51/Rubber_Duck_Front_View_in_Night_20140107.jpg/800px-Rubber_Duck_Front_View_in_Night_20140107.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://upload.wikimedia.org/wikipedia/commons/thumb/5/51/Rubber_Duck_Front_View_in_Night_20140107.jpg/800px-Rubber_Duck_Front_View_in_Night_20140107.jpg&quot; height=&quot;480&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;</description><link>https://fred-zone.blogspot.com/2014/04/osdctw-2014-qml-nodejs.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-2363700416025351753</guid><pubDate>Tue, 15 Apr 2014 12:57:00 +0000</pubDate><atom:updated>2014-04-15T20:58:49.646+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">HanGee 國民機運動</category><category domain="http://www.blogger.com/atom/ns#">心情筆記</category><category domain="http://www.blogger.com/atom/ns#">有趣新構思</category><title>HanGee 國民機運動</title><description>你可能已經知道，我最近相當忙錄，到處奔走，犧牲睡眠，不為別的，就是為了 HanGee （台語發音：蕃薯）國民機運動。過去，心中一直抱持著想改變產業、改變世界的想法，雖然一直在用自己的方式推進，但總是缺少契機去做更大的發展。令人相當意外的是，這次一樣是在自己的 Facebook 動態上許願，許了親手打造國民機的機會，本以為如同過去一樣會石沉大海，但換來的是短短兩天內，幾千個讚的回饋和熱烈支持，一直到現在，關注的人、企業和媒體也越來越多。&lt;br /&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhGyYvNbKLq9eyHePjy0IXeQPSECkZ2fJ_P7hsj5HazVudqSl7LX3Ub4mhbJ_1tqQLRcSrv8Pu9buAnuDbIfwZXeNy9UAE0xEoPx5V4a1ajWyCcYuRGIzN9HewWv6qKoXfDGMXIObguP0k/s1600/hackpad.com_p7MDTxOFGS4_p.135533_1395633749180_0321HanGee-identity-02.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhGyYvNbKLq9eyHePjy0IXeQPSECkZ2fJ_P7hsj5HazVudqSl7LX3Ub4mhbJ_1tqQLRcSrv8Pu9buAnuDbIfwZXeNy9UAE0xEoPx5V4a1ajWyCcYuRGIzN9HewWv6qKoXfDGMXIObguP0k/s1600/hackpad.com_p7MDTxOFGS4_p.135533_1395633749180_0321HanGee-identity-02.jpg&quot; height=&quot;449&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
說實在的，剛開始我僅僅只是想藉由自己的手，完整參與打造一支手機。但是到了今天， HanGee 國民機運動的目標，已經不僅僅是打造一支手機，更重要的是，藉由打造手機的過程，讓世界能透過『HanGee』看見台灣的科技實力和人才；集結有想法，有能力的人們在這個開放創新的過程中發光發熱，共創價值。&lt;br /&gt;
&lt;br /&gt;
這一個多月以來，看到許多業內外的設計、科技、行銷企劃、學生和富有想法的人才陸續加入，更讓我覺得，當我們能夠靠自己發願的力量，把世界上這最複雜的科技產品打造出來，實踐改變和刺激產業昇華的夢想將不是遙不可及！&lt;br /&gt;
&lt;br /&gt;
我想，這個共創的過程一定能造就無數種創意可能，而創意最後的落實能力，就是台灣的產業優勢，或許也讓我們在社群共創的開放歷程中邀請他們，HanGee 將試圖打造一個完善的開放平台，廣納各方的想法和資源，然後藉國內產業的能力優勢，進一步成就共創價值。如果 HanGee 運動能成功，除了一支手機會誕生，屆時，這裹也將證明台灣是世界上各種創意的實現中心，世界創新的凝聚地。HanGee &amp;nbsp;開放創新的構想是一場台灣科技公民的冒險行動，歡迎人人都加入了這個未知且令人興奮的旅程。&lt;br /&gt;
&lt;br /&gt;
接下來，HanGee 將會隨著打造手機的過程，逐一建立起線上共創（Co-creation）平台，包括投稿平台、解決方案整合平台等，並著手打造許多工具和標準，讓各界都能參與過程，建立對話平台，讓產業也能持續創造價值。&lt;br /&gt;
&lt;br /&gt;
有興趣的人可以造訪 HanGee 的官方網站：&lt;br /&gt;
&lt;a href=&quot;http://han-gee.com/&quot;&gt;http://han-gee.com/&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
也歡迎加入 HanGee 的 Facebook 社團，貢獻你對國民機的想法，或是出一份力讓我們更接近目標：&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://www.facebook.com/groups/HanGee/&quot;&gt;https://www.facebook.com/groups/HanGee/&lt;/a&gt;&lt;br /&gt;
&lt;br class=&quot;Apple-interchange-newline&quot; /&gt;
無論你是個人還是組織單位，無論你是否為科技圈的人，亦或是學生，都可以參與計劃，共同打造出心目中的國民機。如果有公司組織團體願表達支持，也可以私下來信與我們聯繫。&lt;br /&gt;
&lt;br class=&quot;Apple-interchange-newline&quot; /&gt;
我們期待，能迎接並協助落實你我的創意。而打造一支手機，只是個開始而已。</description><link>https://fred-zone.blogspot.com/2014/04/hangee.html</link><author>noreply@blogger.com (Fred Chien)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhGyYvNbKLq9eyHePjy0IXeQPSECkZ2fJ_P7hsj5HazVudqSl7LX3Ub4mhbJ_1tqQLRcSrv8Pu9buAnuDbIfwZXeNy9UAE0xEoPx5V4a1ajWyCcYuRGIzN9HewWv6qKoXfDGMXIObguP0k/s72-c/hackpad.com_p7MDTxOFGS4_p.135533_1395633749180_0321HanGee-identity-02.jpg" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-9198563569364913547</guid><pubDate>Wed, 26 Feb 2014 04:41:00 +0000</pubDate><atom:updated>2014-02-26T12:43:41.730+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">心情筆記</category><title>沒經驗，就別擅做主張</title><description>這幾年下來，有一種感觸，覺得自己可以為為某行業做些什麼，是一種錯誤的思考模式，原因是我們根本就沒參與過這個行業，自己憑什麼認為自己的方案是有效的？再者，又有什麼人願意承擔風險，讓你做大量實驗證明自己的想法？在這種情況下，當你做越多，越可能是他們用不到且無法接受的東西，完全失敗也就指日可待。&lt;br /&gt;
&lt;br /&gt;
想要改善，就要親自接觸和體驗，這也是為什麼有些成功的管理者，會跳下去基層做起，感受問題所在。事實上，挖黑幕只是順帶的工作，主要還是找出真正可以改善的問題。&lt;br /&gt;
&lt;br /&gt;
所以，行銷企劃人員雖自己不知道，但會正確使用辦活動和引進噱頭的方法去包裝想法，一方面是就算活動不成功，也只是短暫且單次的實驗失敗，就算發現想引進的想法不可長期使用，也至少在第一時間獲得人流和其他的利益回補而達到損益平衡。&lt;br /&gt;
&lt;br /&gt;
我們必須注意，如果我們沒煮過菜，沒有像快炒店那樣大量煮菜的經驗，我們就不要想設計一個改善煮菜工作的好用鍋鏟。你以為廚師會給你機會而用你的東西？先不論他們會不會買單你的想法、你的想法是不是真能改善你要改善的問題。至少廚師們從一開始就會先入為主，不認為你的東西會是好用的，那怕是他們自己的問題，只要有一次煮出來的東西難吃，他們就會認定你的方案存在問題。還沒切入正題前就阻力重重，失敗機率非常高。&lt;br /&gt;
&lt;br /&gt;
另一個更直接的例子，身為程式開發者，如果你會相信完全沒開發過程式且沒經歷過軟體開發專案的管理者，在無知的情況下，可以設計一套管理流程和工具，或引進一套高科技，幫助你更快速完成你的程式開發工作。那麼，你就繼續自我感覺良好，覺得自己可以為某自己毫無經驗的行業做些什麼吧。:-S&lt;br /&gt;
&lt;br /&gt;
註：會寫程式的人很幸運，就像全民超人中的 Hancock 一樣，有不凡的超能力。但我們要學會控制和感受，才可能很好的發揮能力在正確的地方。</description><link>https://fred-zone.blogspot.com/2014/02/blog-post.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-1371800421919236630</guid><pubDate>Fri, 10 Jan 2014 00:57:00 +0000</pubDate><atom:updated>2017-04-04T23:31:29.223+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Session</category><category domain="http://www.blogger.com/atom/ns#">Web</category><category domain="http://www.blogger.com/atom/ns#">Web 相關技術</category><category domain="http://www.blogger.com/atom/ns#">程式心得筆記</category><category domain="http://www.blogger.com/atom/ns#">雲端(Cloud)</category><title>Web 技術中的 Session 是什麼？</title><description>&lt;div dir=&quot;ltr&quot;&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjh5GT4pXom96zfRYZtxtInGOVCgbobhqKdCO1RC5yR4HucgUhe-diGFM5QTrOA2R1HmGLa3VYcejG1tB0YAQEgbHlK8XfGia86x06fE-bAPkB9UXPZsNQBz1lKvuQhmn_KwilHGSLwdk8/s1600/WebSession.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjh5GT4pXom96zfRYZtxtInGOVCgbobhqKdCO1RC5yR4HucgUhe-diGFM5QTrOA2R1HmGLa3VYcejG1tB0YAQEgbHlK8XfGia86x06fE-bAPkB9UXPZsNQBz1lKvuQhmn_KwilHGSLwdk8/s1600/WebSession.png&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
很久沒更新了，自從十二月底退伍之後，一直忙著將書完稿給出版社，沒空坐下來好好在 Blog 上寫點東西。沒辦法，在當兵時閒不下來，就答應了出版社的邀約開始寫 Node.js 的書，因此欠下了書的債。還好，如預期的寫完了，預計這一兩個月應該就會上市，還請各界多多捧場！&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
會寫這篇文章的原故，是因為在 Facebook 的社群上，有人問起了關於 Session 和 Cookie 相關性的問題。其實，社群的人都很好，有許多熱心的網友願意幫助別人。不過，為了幫忙回答問題，大家七嘴八舌的東說一句西說一句，甚至引起莫明的論戰，實在不是一個好的現象。況且，使用不同語言、有不同技術背景的人，都各自用自己的角度去描述和解說，讓原本就已經不懂的人更有如瞎子摸象，見不到全貌，搞更糊塗了。對發問的人來說，也不知該聽誰的好。&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
說來湊巧，筆者剛好在剛截稿的 Node.js 書籍中，對 Session 概念也有許多著墨。所以之前稍微在網路上查過，發現鮮少有人提出對 Session 技術的完整說明，而多半是討論 ASP、.Net、Java、PHP 等語言或框架中如何使用 Session，以及一些延伸的特性，以致很多人對 Session 的概念相當片面。因此這次的事件後，一些網友也建議我把在 Facebook 上回應的完整內容，在 Blog 發表記錄下來，讓更多人對 Session 有個比較完整且正確的認識。&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;h2&gt;
&lt;b&gt;本文目標&lt;/b&gt;&lt;/h2&gt;
&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
在 Web 的世界裡，Session 已經是被廣泛使用的一種技術，大多數的 Web 開發者，肯定都能運用自如。在現今的各類 Web 應用程式的開發框架和工具中，Session 也已經被包裝成容易使用的模式，所以本文也就不再多討論 Session 怎麼去使用，而是著重在 Session 的原理和實現上。要知道，無論是使用哪一種語言，這 Session 概念上都是通用的，沒有什麼不一樣。&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;b&gt;&lt;br /&gt;
&lt;/b&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;h2&gt;
&lt;b&gt;Session 是什麼？&lt;/b&gt;&lt;/h2&gt;
&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
Session 之所以會存在，是因為 HTTP 為 stateless 的設計，Server 和 Client 不會一直保持連線狀態，也不會有雙方狀態的即時更新，所以，Server 並不知道 Client 的狀態（像是是否已經登入）。因此，後來的網站開發者，採用 Session 這樣的設計來解決這問題。有趣的是，Session 機制在最早的 CGI 年代，是完全要程式設計師徒手寫出來的，搞死了很多人，不像現在的網站開發者有現成的許多解決方案可以選擇，不必完全了解也可以使用。&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;b&gt;&lt;br /&gt;
&lt;/b&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;h2&gt;
&lt;b&gt;Session 的原理&lt;/b&gt;&lt;/h2&gt;
&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
很多人在討論 Session 時，不免與 Cookie 牽扯上關係，反而不小心偏了重點，所以在了解 Session 時必須有個觀念，就算沒有 Cookie 的存在，Session 機制也可以正常運作。Cookie 在 Session 機制中，可以扮演許多角色，也可以對原始的 Session 機制做許多改良，但不用急著過早討論。從 Session 為什麼被需要來切入，自運作原理來了解，反而比較實際也易懂。最後再來討論 Cookie 在裡面所扮演的角色，才會比較清楚。&lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
簡單來說，Session 的機制就像是你去飲料店下了單以後，得到號碼牌，然後你走開幾步，店員就忘了你是誰。所以，如果你想去取飲料，你就得靠這張號碼牌，去跟店員領，店員會跟據這號碼牌，認定你是顧客、是否點過餐、知道你點了什麼東西，然後可以接著給你屬於你的飲料。&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
理解 Session 的原理後，回到 HTTP 上就是一樣的。只是，在網頁技術中，有兩種方法讓 Client 取得號碼牌，一個是用 Cookie，另一個是直接輸出並嵌入頁面之中的方法（就是要你把號碼背起來）。&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
拿號碼牌去 Server 要資料，主要也分為兩種方法，Cookie 和運用標準的 Query string/POST body方法。（其實只要能把號碼傳到 Server 上，任何方法都行）&lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
只不過，因為實作上的困難度考量，還有現今的 Browser 預設都支援 Cookie，所以在現有的網站框架中，都預設採用 Cookie 來發號碼牌和兌換資料。Cookie 的交換會在建立連線時，在背景自動完成，因此開發者不必考慮Client/Server的號碼牌交換問題。因為 Browser 會在建立連線後，第一時間就自動在背景把 Cookie 上傳到 Server，Server 也在回傳資料時，第一時間自動把 Cookie 回傳給 Client。&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
所以，除非是有必要（像是 Browser/Client 不支援 Cookie 的情況），才會保留另一種實作。&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
BTW, 飲料店拿號碼牌的例子應該很容易解釋清楚 Session 的原理。&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;b&gt;&lt;br /&gt;
&lt;/b&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;h2&gt;
&lt;b&gt;Cookie-based Session&lt;/b&gt;&lt;/h2&gt;
&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
這邊要注意的是，有一種 Session 會讓很多很多人感到混亂，就是 cookie-based session。問題在於名稱上的誤導，你可能會想，既然幾乎所有的 Session 機制都會用到 cookie，是否都可以稱做 cookie-based session？&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
其實大多數人說的 cookie-based session ，指的是儲存資料方式的不同，不是指領號碼牌時，是否有用到 cookie。（不過如果早個十年，當時講的 cookie-based session，可能多半指的就是領號碼牌時，有沒有用到 cookie。摔碗！）&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
很多人在討論 Cookie 與 Session 的關係時，兩種用到 Cookie 的地方，時常混在一起講，這就不免要雞同鴨講筆戰一番了。&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
事實上，在最原始的 Session 設計，大多開發者都將資料存在 Server 上，也就是你點了什麼飲料，都是記錄在 Server 裡，可能是 Database、記憶體或是檔案，可以以任何一種形式儲存。然後，當你去領飲料時，店員會輸入你的號碼，用你的號碼得知你是否點過餐、點了什麼東西。&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
一般的小網站，這樣的解決方案並沒有什麼問題。但是對今天這種超大流量的網站服務來說，因為他們有無數台對外的 Server，有如無數個服務窗口，讓顧客總是隨機進入其中一個窗口來兌換飲料，所以後端怎麼存放和共享這個 session 資料，又要兼顧效能和方便維護，就變成是很大的問題。&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
因此 Cookie-based Session 就被提出為一個解決方案，把資料暫存放在 Cookie 中，讓 Client 自己負責保存。簡單來說，就是把你點什麼飲料，通通直接寫在號碼牌上。Server 就可以直接看你的號碼牌上寫了什麼，而不必花大量時間去後面建立大規模的 Server 來處理 Session 。&lt;br /&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
不過，這邊要特別提到，因為 cookie 有 4K 資料大小的限制，很多網站服務會選擇 cookie-based 和後端儲存並行的方案。&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
或許有人會問到 Cookie 是否有可能被篡改？這類安全問題，通常會使用加密手段來解決。一般來說，Cookie-based session 的 cookie 會被加密，只有 Server 才知道如何解開，Client 並沒有能力可以存取，只是得到一個看不懂的包裹，所以不會有安全性的問題（當然還是有機會被破）。&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
由於仍然存在風險，這也是其中一個原因，為什麼有些網站仍然會採用 cookie-based 和後端儲存 Session 並行的解決方案，或是會避免把敏感資料放在 Cookie-based Session 上。&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;h2&gt;
&lt;b&gt;什麼是 Session 傳值？&lt;/b&gt;&lt;/h2&gt;
&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;b&gt;&lt;br /&gt;
&lt;/b&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
時常會聽到『使用 Session 傳值』這類說法，其實就是利用 Session 機制儲存資料，讓不同頁面之間可以互相傳遞資料。其原理通常是使用 Query String 或 POST body 等方法，把資料往 Server 傳之後，在 Server 端將 Client 上傳的資料存在 Session 之中。之後的連線或開啟其它頁面時，因為你拿的號碼牌是同一個，所以在不同的頁面之下，仍然可以讀到前一次所儲存在 Session 的狀態。&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;h2&gt;
&lt;b&gt;後記&lt;/b&gt;&lt;/h2&gt;
&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;b&gt;&lt;br /&gt;
&lt;/b&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
當完兵回來了，腦袋還好還算靈活呀。:-)&lt;/div&gt;
</description><link>https://fred-zone.blogspot.com/2014/01/web-session.html</link><author>noreply@blogger.com (Fred Chien)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjh5GT4pXom96zfRYZtxtInGOVCgbobhqKdCO1RC5yR4HucgUhe-diGFM5QTrOA2R1HmGLa3VYcejG1tB0YAQEgbHlK8XfGia86x06fE-bAPkB9UXPZsNQBz1lKvuQhmn_KwilHGSLwdk8/s72-c/WebSession.png" height="72" width="72"/><thr:total>4</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-6705076367877448088</guid><pubDate>Thu, 10 Oct 2013 10:06:00 +0000</pubDate><atom:updated>2013-10-10T19:45:57.492+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">Node.js</category><category domain="http://www.blogger.com/atom/ns#">自由軟體</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><title>Node Knockout Taiwan 2013 駭客松活動報名開跑！</title><description>Node Knockout Taiwan又要來啦，將於11/9（六） - 11/10（日）舉行！這是一個讓Hacker、程式開發者們熱血沸騰，挑戰48小時不間斷開發的Node.js Hackathon（駭客松）鐵人活動。不用煩腦吃飯、零食、飲料、住宿等生活問題，主辦單位將解決所有的問題，讓你在有限時間內，專心與自己的隊友們從無到有打造自己的作品，並與來自各方的好手們互相交流，贏得比賽。無論你是學生、職業開發者還是從事非程式相關職業的人，都歡迎來共襄盛舉！&lt;br /&gt;
&lt;br /&gt;
立即報名網址：&lt;br /&gt;
&lt;a href=&quot;http://nodeknockout.nodejs.tw/&quot;&gt;http://nodeknockout.nodejs.tw/&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;後記&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
筆者也將會參加這個活動，但還沒找到隊友，也還沒想好題目，有興趣的人可以來湊團。:-P&lt;br /&gt;
&lt;br /&gt;
對了！別偷跑！逼自己48小時內完成作品，才能挑戰自己的極限！超越自己！</description><link>https://fred-zone.blogspot.com/2013/10/node-knockout-taiwan-2013.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-4317088909342216774</guid><pubDate>Sun, 22 Sep 2013 12:50:00 +0000</pubDate><atom:updated>2013-09-24T20:38:55.449+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">DBus</category><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">Linux</category><category domain="http://www.blogger.com/atom/ns#">Node.js</category><category domain="http://www.blogger.com/atom/ns#">Stem OS</category><category domain="http://www.blogger.com/atom/ns#">技術新知</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><title>重新開發的DBus Module for Node.js</title><description>&lt;p dir=ltr&gt;Stem OS&amp;#20173;&amp;#25345;&amp;#32396;&amp;#38283;&amp;#30332;&amp;#20013;&amp;#65292;&amp;#26368;&amp;#36817;&amp;#37341;&amp;#23565;&amp;#31227;&amp;#26893;&amp;#21040;Cubieboard&amp;#30340;&amp;#24037;&amp;#20316;&amp;#20570;&amp;#20102;&amp;#24456;&amp;#22810;&amp;#21162;&amp;#21147;&amp;#65292;&amp;#28858;&amp;#20102;&amp;#25913;&amp;#21892;&amp;#31337;&amp;#23450;&amp;#24615;&amp;#21644;&amp;#22686;&amp;#21152;&amp;#26356;&amp;#22810;&amp;#21151;&amp;#33021;&amp;#65292;&amp;#25105;&amp;#20497;&amp;#37325;&amp;#26032;&amp;#38283;&amp;#30332;&amp;#20102;DBus&amp;#30340;&amp;#27169;&amp;#32068;&amp;#65292;&amp;#35731;&amp;#28982;&amp;#24460;&amp;#30452;&amp;#25509;&amp;#36339;&amp;#20491;&amp;#22823;&amp;#29256;&amp;#26412;&amp;#34399;&amp;#21040;v0.1&amp;#12290;&amp;#20294;&amp;#30446;&amp;#21069;&amp;#20173;&amp;#34389;&amp;#26044;&amp;#26368;&amp;#24460;&amp;#28204;&amp;#35430;&amp;#38542;&amp;#27573;&amp;#65292;&amp;#24453;&amp;#31337;&amp;#23450;&amp;#24460;&amp;#26371;&amp;#19978;&amp;#20659;&amp;#33267;NPM&amp;#65292;&amp;#35731;&amp;#20219;&amp;#20309;&amp;#20154;&amp;#37117;&amp;#21487;&amp;#20197;&amp;#20351;&amp;#29992;&amp;#12290;&amp;#26377;&amp;#33288;&amp;#36259;&amp;#22039;&amp;#39854;&amp;#30340;&amp;#20154;&amp;#65292;&amp;#20173;&amp;#21487;&amp;#21435;git repository&amp;#65292;&amp;#24478;development&amp;#20998;&amp;#25903;&amp;#19979;&amp;#36617;&amp;#22238;&amp;#20358;&amp;#20351;&amp;#29992;&amp;#12290;&lt;/p&gt;
&lt;p dir=ltr&gt;&amp;#26032;&amp;#30340;DBus&amp;#27169;&amp;#32068;&amp;#28858;&amp;#20102;&amp;#32302;&amp;#23567;footprint&amp;#65292;&amp;#26356;&amp;#36969;&amp;#21512;&amp;#23884;&amp;#20837;&amp;#24335;&amp;#25033;&amp;#29992;&amp;#65292;&amp;#36991;&amp;#20813;&amp;#24341;&amp;#20837;GLib&amp;#65292;&amp;#28858;&amp;#20102;&amp;#25552;&amp;#21319;&amp;#25928;&amp;#33021;&amp;#65292;&amp;#20063;&amp;#19981;&amp;#20351;&amp;#29992;GContext&amp;#20358;&amp;#34389;&amp;#29702;event loop &amp;#65292;&amp;#32780;&amp;#26159;&amp;#23436;&amp;#20840;&amp;#25505;&amp;#29992;&amp;#32020;C/C++&amp;#21644;libuv&amp;#20358;&amp;#23526;&amp;#20316;&amp;#12290;&lt;/p&gt;
&lt;p dir=ltr&gt;&amp;#27492;&amp;#22806;&amp;#65292;&amp;#33290;&amp;#29256;&amp;#30340;&amp;#27169;&amp;#32068;&amp;#19968;&amp;#30452;&amp;#26377;&amp;#20491;&amp;#21839;&amp;#38988;&amp;#65292;&amp;#23601;&amp;#26159;&amp;#22823;&amp;#22810;&amp;#25976;&amp;#21151;&amp;#33021;&amp;#20197;&amp;#21516;&amp;#27493;(Sync)&amp;#26041;&amp;#24335;&amp;#22312;&amp;#22519;&amp;#34892;&amp;#65292;&amp;#20063;&amp;#26377;&amp;#33287;&amp;#38750;&amp;#21516;&amp;#27493;(Asynchronous)&amp;#35373;&amp;#35336;&amp;#28151;&amp;#25645;&amp;#30340;&amp;#24773;&amp;#27841;&amp;#65292;&amp;#23566;&amp;#33268;Node.js&amp;#31243;&amp;#24335;&amp;#22519;&amp;#34892;&amp;#19978;&amp;#26371;&amp;#19981;&amp;#38918;&amp;#26274;&amp;#65292;&amp;#19988;&amp;#26377;&amp;#22196;&amp;#37325;&amp;#38283;&amp;#30332;&amp;#37007;&amp;#36655;&amp;#19981;&amp;#19968;&amp;#33268;&amp;#30340;&amp;#21839;&amp;#38988;&amp;#12290;&amp;#26377;&amp;#37969;&amp;#26044;&amp;#27492;&amp;#65292;&amp;#22312;&amp;#26032;&amp;#30340;&amp;#23526;&amp;#20316;&amp;#20013;&amp;#65292;API&amp;#34987;&amp;#37325;&amp;#26032;&amp;#35373;&amp;#35336;&amp;#36942;&amp;#65292;&amp;#20840;&amp;#38754;&amp;#25505;&amp;#29992;&amp;#38750;&amp;#21516;&amp;#27493;&amp;#30340;&amp;#35373;&amp;#35336;&amp;#37007;&amp;#36655;&amp;#12290;&amp;#28858;&amp;#20102;&amp;#35731;DBus&amp;#30340;&amp;#25805;&amp;#20316;&amp;#33021;&amp;#26356;&amp;#36028;&amp;#36817;JavaScript &amp;#30340;&amp;#32722;&amp;#24931;&amp;#65292;&amp;#20063;&amp;#27169;&amp;#20223;EventEmitter&amp;#30340;on()&amp;#21644;emit()&amp;#26041;&amp;#27861;&amp;#21435;&amp;#25805;&amp;#20316;Signal&amp;#12290;&lt;/p&gt;
&lt;p dir=ltr&gt;&amp;#38500;&amp;#20102;&amp;#25928;&amp;#33021;&amp;#21644;&amp;#37007;&amp;#36655;&amp;#30340;&amp;#25913;&amp;#36914;&amp;#20043;&amp;#22806;&amp;#65292;&amp;#26032;&amp;#30340;DBus&amp;#27169;&amp;#32068;&amp;#25903;&amp;#25588;&amp;#20102;&amp;#21516;&amp;#26178;&amp;#24314;&amp;#31435;&amp;#22810;&amp;#20491;connection&amp;#30340;&amp;#33021;&amp;#21147;&amp;#12290;&amp;#36889;&amp;#24847;&amp;#21619;&amp;#33879;&amp;#65292;&amp;#38283;&amp;#30332;&amp;#32773;&amp;#21487;&amp;#20197;&amp;#22312;&amp;#21516;&amp;#19968;&amp;#20491;process&amp;#19978;&amp;#25552;&amp;#20379;&amp;#22810;&amp;#20491;DBus&amp;#30340;&amp;#26381;&amp;#21209;&amp;#12290;&amp;#21033;&amp;#29992;&amp;#36889;&amp;#20491;&amp;#26032;&amp;#29305;&amp;#33394;&amp;#65292;&amp;#25105;&amp;#20497;&amp;#24471;&amp;#20197;&amp;#28187;&amp;#23569;StemOS&amp;#31995;&amp;#32113;&amp;#19978;&amp;#24120;&amp;#35387;&amp;#31243;&amp;#24335;&amp;#30340;&amp;#25976;&amp;#37327;&amp;#12290;&lt;/p&gt;
&lt;p dir=ltr&gt;&amp;#26368;&amp;#24460;&amp;#65292;&amp;#38656;&amp;#35201;&amp;#27880;&amp;#24847;&amp;#30340;&amp;#26159;&amp;#65292;&amp;#22823;&amp;#37327;&amp;#30340;&amp;#37325;&amp;#26032;&amp;#35373;&amp;#35336;&amp;#65292;&amp;#35731;&amp;#26032;&amp;#27169;&amp;#32068;&amp;#30340;API&amp;#33287;&amp;#33290;&amp;#29256;&amp;#23436;&amp;#20840;&amp;#19981;&amp;#30456;&amp;#23481;&amp;#65292;&amp;#22914;&amp;#20320;&amp;#19981;&amp;#25171;&amp;#31639;&amp;#25913;&amp;#23531;&amp;#20320;&amp;#30340;&amp;#31243;&amp;#24335;&amp;#65292;&amp;#23601;&amp;#19981;&amp;#35442;&amp;#21319;&amp;#32026;&amp;#21040;v0.1&amp;#29256;&amp;#26412;&amp;#12290;&lt;/p&gt;
&lt;p dir=ltr&gt;&lt;b&gt;&amp;#24460;&amp;#35352;&lt;/b&gt;&lt;/p&gt;
&lt;p dir=ltr&gt;&amp;#20570;&amp;#20102;&amp;#36889;&amp;#40636;&amp;#22810;&amp;#24180;OS&amp;#30340;&amp;#26481;&amp;#35199;&amp;#65292;&amp;#21508;&amp;#31278;&amp;#29992;&amp;#36884;&amp;#30340;&amp;#31995;&amp;#32113;&amp;#37117;&amp;#26377;&amp;#19981;&amp;#23569;&amp;#32147;&amp;#39511;&amp;#65292;&amp;#20294;&amp;#29694;&amp;#22312;&amp;#20173;&amp;#28982;&amp;#35258;&amp;#24471;&amp;#21040;&amp;#36889;&amp;#26159;&amp;#20491;&amp;#36027;&amp;#21147;&amp;#21448;&amp;#32321;&amp;#35079;&amp;#30340;&amp;#24037;&amp;#20316;&amp;#21568;&amp;#12290;&lt;/p&gt;
&lt;p dir=ltr&gt;&amp;#19981;&amp;#36942;&amp;#65292;&amp;#21162;&amp;#21147;&amp;#32047;&amp;#31309;&amp;#25104;&amp;#26524;&amp;#65292;&amp;#25645;&amp;#19978;Node.js&amp;#30340;&amp;#21015;&amp;#36554;&amp;#12290;&amp;#26368;&amp;#24460;&amp;#33021;&amp;#23565;&amp;#20154;&amp;#22823;&amp;#32882;&amp;#35498;&amp;#36889;&amp;#26159;&amp;#25105;&amp;#20497;&amp;#33258;&amp;#24049;&amp;#35373;&amp;#35336;&amp;#30340;JavaScript OS&amp;#65292;&amp;#20854;&amp;#33707;&amp;#22823;&amp;#30340;&amp;#25104;&amp;#23601;&amp;#24863;&amp;#23559;&amp;#28961;&amp;#21487;&amp;#26367;&amp;#20195;&amp;#65281;&lt;/p&gt;
</description><link>https://fred-zone.blogspot.com/2013/09/dbus-module-for-nodejs.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-7118926167089416373</guid><pubDate>Sun, 08 Sep 2013 05:55:00 +0000</pubDate><atom:updated>2013-09-08T13:55:22.354+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Op</category><category domain="http://www.blogger.com/atom/ns#">心得分享、講座</category><category domain="http://www.blogger.com/atom/ns#">心情筆記</category><title>Hacker 精神，就是一種認真過活的精神</title><description>在這一年平淡的公務員生涯裡，想起了某人的一句話：『一年的經驗，重覆了 N 次。』，這幾年自己做過了一些案子，也與很多人交流，發現，這現象似乎真的在業界是很常發生的。在工作上，一件事或東西只要搞到不要出問題的程度就好，所以永遠也僅此於做完交差，解決完大多能預期的眼前問題，然後交差。所以，在這樣的生態中，無論對任何事，進一步的探討、研究和改進，都是種奢望，更不用想從東西的根本改善。&lt;br /&gt;
&lt;br /&gt;
在這樣的情況下，我們仍然敢說我們有 N 年經驗嗎？&lt;br /&gt;
&lt;br /&gt;
當然，就國內企業的現況和體質來說，想要活的好、拉高員工薪資都還是個問題。我們目前無法期望國內企業們能像 Google 等一些公司那樣，讓每個員工除了完成工作外，也可以有機會挑戰自我和做天馬行空的嘗試、研究，進而改進和發展出許多能開花結果的東西。&lt;br /&gt;
&lt;br /&gt;
但是，撫心自問，就算工作上、公司不給我們這樣的機會，我們又是否願意自己投資去花這些看似沒立即意義的時間呢？&lt;br /&gt;
&lt;br /&gt;
還記得，在某一次的朋友引見中，認識了年輕時曾在 NeXT（就是 Steve Job 創辦的那家電腦公司，其系統就是現代 Mac OS X 的前身）工作過的人，他大方跟我聊起了許多年輕時的故事。其中，最讓我有感覺的一個故事，就是早在 90 年代，Mach kernel 和 OS 就已經被移植到 X86 架構的 PC 上（註：X86 版的 Mac，是 2005 年之後才有的）。而更重要的是，在當時 Steve Job 完全不知道這件事，是 RD 私底下的熱血行徑。雖然不知是真是假，但我仍一直在思考著，這件事有可能在國內的企業中發生嗎？有多少人會在下班後，仍然花時間去投入這樣的工作？這對理解 Hacker 文化和精神的人，應該不會太過驚訝，但這對許多一般人來說可能永遠無法理解。&lt;br /&gt;
&lt;br /&gt;
回頭看看，我比較幸運，還沒畢業就選擇了創業一路，別人沒辦法給我機會，我給了我自己機會。基本上在發展自己產品的過程中，就是在挑戰自我、不斷的創造、改變和嘗試，所以雖然在業界也打滾了幾年，至少還沒有停止過學習和研究的念頭，更不惜一切追求自己的目標，過程中對自己沒有交差了事，只有達到一個個里程碑。&lt;br /&gt;
&lt;br /&gt;
更幸運的是，身為一個愛 Coding 的傢伙，在偶然的機會一腳踏入了 Open Source 的世界，這世界沒有苟且，隨時隨地都有人虎視耽耽把眼前的軟體做改進。你不自覺就感染了這樣的動力，狠狠的盯注各式各樣的軟體，在機會來臨時，就改得它面目全非。是啊，在這樣『不好就打掉重練』的過程中，確實滿足了我愛 Coding 也 Coding 自己的內心渴望，然後一直不斷精進自己的技術、能力和創意。試想，如果我跑去業界打一份普通的工，只是每天上班寫 Code、下班收工，我又有多少機會可以得到這些滿足呢？&lt;br /&gt;
&lt;br /&gt;
在這社會中，我們都扮演著各種螺絲釘的角色，但與死物不同，不會一輩子只做同樣的事，人會成長、改變而且有感染力。只要能不斷精進自己，今年你是被用來掛牆上的畫，明天你可能會被用來固定保險箱的門，然後你家的保險箱，可能是全世界最堅固的也說不定。&lt;br /&gt;
&lt;br /&gt;
我想，我雖然不敢說有 N 年經驗，但好在我還有 N 年的小宇宙可以燒。:-)</description><link>https://fred-zone.blogspot.com/2013/09/hacker.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-3986641429152594722</guid><pubDate>Sun, 01 Sep 2013 05:35:00 +0000</pubDate><atom:updated>2013-09-18T13:51:59.993+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Embedded System</category><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">Linux</category><category domain="http://www.blogger.com/atom/ns#">Node.js</category><category domain="http://www.blogger.com/atom/ns#">Stem OS</category><category domain="http://www.blogger.com/atom/ns#">自由軟體</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><title>jsdx-disk 讓你使用 JavaScript 管理儲存裝置</title><description>『&lt;a href=&quot;http://stem.mandice.org/&quot;&gt;Stem OS&lt;/a&gt;』是一個 JavaScript 作業系統，內建 Node.js 和許多 JavaScript 的系統 APIs。如果你有在筆者的開發動態，就會發現 Stem OS 是我們 Mandice 團隊目前的開發重點和產品，我們一直期望能讓懂 JavaScript 的人，都可以來開發系統程式、嵌入式系統，此外，也想運用 JavaScript 語言的流行度，讓嵌入式系統開發門檻降低，使其更為簡單、快速。如果你家中有看似沒用的舊電腦，在安裝 Stem OS 之後，便可以立即用 JavaScript 自行寫一些應用程式，賦予這舊電腦全新的用途。還記得，在今年的 OSDC 活動上，小弟上台給了一場 lightening Talk，即是探討如何將舊電腦改造成一個無紙化傳真機，而改造的時間只需要 5 分鐘，包括安裝 Stem OS 和寫幾行 JavaScript 程式。有興趣的人可以參閱當時的投影片『&lt;a href=&quot;http://fred-zone.blogspot.tw/2013/05/jsdc-2013-npk-javascript-os-stem.html&quot;&gt;JSDC 2013 簡報釋出！NPK 與 JavaScript OS (Stem) 登場！&lt;/a&gt;』。&lt;br&gt;
&lt;br&gt;
JavaScript 語言的好處就是用它寫的程式無需編譯，效能也不俗，這意味著，當你在開發 PC 平台之外的嵌入式裝置（如：ARM），你不必再忍受跨平台編譯（cross-compile）的痛苦，可以很快速開發你的應用。此外 Stem OS 已經將系統和硬體的支援都已經為你處理好，也提供許多低階的系統 APIs，且一開機就是跑你的專屬程式，你可以專心的打造自己的嵌入式應用，完全不用再煩腦和花大量時間在系統整合和移植的開發上。&lt;br&gt;
&lt;br&gt;
既然 Stem OS 已經將許多系統功能包裝成 APIs，讓 JavaScript 可以透過這些介面從 UI 控制到硬體，代表我們實際上做了很多這方面的準備工作，本文將要提到的 jsdx-disk，便是其中之一。&lt;br&gt;
&lt;br&gt;
jsdx-disk 顧名思義是一個控制硬碟的系統 APIs，你可以用他掛載（Mount）、切割磁區（Partition）和格式化（Format）硬碟。更進一步，你可以用他監控系統上的儲存裝置，以及取得這些儲存裝置的硬體資訊，更進一步處理熱插拔（Hotplug）的行為。在 Stem OS 中，我們以 JavaScript 寫了一支常註程式（Daemon），監控熱插拔隨身碟，自動掛載（Automount）的機制，而這功能就是運用了 jsdx-disk。&lt;br&gt;
&lt;br&gt;
Git Repository：&lt;br&gt;
&lt;a href=&quot;https://github.com/cfsghost/jsdx-disk&quot;&gt;https://github.com/cfsghost/jsdx-disk&lt;/a&gt;&lt;br&gt;
&lt;br&gt;
使用 jsdx-disk 相當簡單，如果不在 Stem OS 上，你也可以透過 NPM 安裝使用：&lt;br&gt;
&lt;div&gt;&lt;pre&gt;npm install jsdx-disk&lt;/pre&gt;&lt;/div&gt;&lt;br&gt;
jsdx-disk 的使用方法相當簡單，如果你要掛載一個 Block device：&lt;br&gt;
&lt;div&gt;&lt;pre&gt;var Disk = require(&#39;jsdx-disk&#39;);

var disk = new Disk();

disk.init(function() {

        var obj = disk.getStorageObject(&#39;Block&#39;, &#39;sdb1&#39;);

        obj.mount(function(err, mountPath) {

                if (err) {
                        console.log(err);
                        process.exit();
                        return;
                }

                console.log(&#39;It was mounted on &#39; + mountPath);
                process.exit();
        });
});
&lt;/pre&gt;&lt;/div&gt;&lt;br&gt;
如果你要監控儲存裝置的新增、移除等熱插拔機制：&lt;br&gt;
&lt;div&gt;&lt;pre&gt;var Disk = require(&#39;jsdx-disk&#39;);

var disk = new Disk();

disk.init(function() {

        disk.on(&#39;interfaces_added&#39;, function(data) {
                console.log(&#39;INTERFACE ADDED: &#39; + data.objectName);
                console.log(data);
        });

        disk.on(&#39;interfaces_removed&#39;, function(data) {
                console.log(&#39;INTERFACE REMOVED: &#39; + data.objectName);
                console.log(data);
        });

});
&lt;/pre&gt;&lt;/div&gt;&lt;br&gt;
如同開頭所說，jsdx-disk 也有格式化硬碟的 API（注意！請小心執行下面程式，它會格式化 /dev/sdb1）：&lt;br&gt;
&lt;div&gt;&lt;pre&gt;var Disk = require(&#39;jsdx-disk&#39;);

var disk = new Disk();

disk.init(function() {

        var obj = disk.getStorageObject(&#39;Block&#39;, &#39;sdb1&#39;);

        console.log(&#39;Formating ...&#39;);
        obj.format(&#39;ext4&#39;, function(err) {

                if (err) {
                        console.log(err);
                        process.exit();
                        return;
                }

                console.log(&#39;Done&#39;);
                process.exit();
        });
});
&lt;/pre&gt;&lt;/div&gt;&lt;br&gt;
更多的功能，可以參考 jsdx-disk 原始程式碼裡的 tests 資料夾，有更多的參考用法。&lt;br&gt;
&lt;br&gt;
&lt;b&gt;後記&lt;/b&gt;&lt;br&gt;
&lt;br&gt;
在台灣創業，開發軟體相較之下比較不易出頭，但我們仍堅持不放棄新創之路，做不一樣的事。我們的團隊雖小，但人人都揮灑著熱情！請支持我們吧！:-)</description><link>https://fred-zone.blogspot.com/2013/09/jsdx-disk-javascript.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>3</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-7903322605366760725</guid><pubDate>Wed, 31 Jul 2013 08:49:00 +0000</pubDate><atom:updated>2013-07-31T16:49:05.125+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">Node.js</category><category domain="http://www.blogger.com/atom/ns#">Web</category><category domain="http://www.blogger.com/atom/ns#">Web 相關技術</category><category domain="http://www.blogger.com/atom/ns#">心得分享、講座</category><category domain="http://www.blogger.com/atom/ns#">技術新知</category><category domain="http://www.blogger.com/atom/ns#">自由軟體</category><title>COSCUP 2013 is coming!</title><description>今年 COSCUP 很有錢、很盛大，整個活動從中研院移師到了台北國際會議廳，議程多達八軌。很難想像三四年前，自己幫忙 COSCUP 辦活動時的情景，和今天相較之下，當年的活動實在迷你，大家都希望哪一天能殺入台北國際會議廳。沒想到，COSCUP 靠後起之秀多年來的努力，真的在今年達成所有人的願望！我們真的要為這些工作人員和總召柏強喝采！謝謝他們每年不辭辛勞的貢獻！&lt;br /&gt;
&lt;br /&gt;
慚愧的是，COSCUP 長越大，更顯得我小小的講者的微不足道，如今我唯一能貢獻的，大概就只剩對 Open Source 的熱情。因此，自去年在 COSCUP 2012 的國際舞台『發表退學宣言』後，這個大學讀了七年還是沒畢業的我，今年仍厚著臉皮投了稿，稿也被入取。這邊非常感謝議程審核人員的親睞，讓我贏得今年的一段宣言時間，還有開賣一分鐘內就沒有的入場門票。 :-)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
至於我這次要講的主題是：『我用 Node.js 開發自己的 Web 瀏覽器？』&lt;br /&gt;
&lt;br /&gt;
時間地點是：COSCUP 2013 第二天(8/4) 10:00，101CD 場。&lt;br /&gt;
&lt;br /&gt;
其實，正當投稿截止之際，當時熱映的『鋼鐵人3』給我了很大的啟示。不禁思考，身為工程師的我們，是否已經被工具和自己曾經所創造的東西所束縛了？如果我們今天把眼前的電腦給砸了，是否全身上下功力全失？無法有任何作為？&lt;br /&gt;
&lt;br /&gt;
這樣的概念，套在 JavaScript 這個近年來很夯的語言上，更是貼切不過。對很多人而言，失去瀏覽器為依靠的 JavaScript，令人感到不安、充滿焦慮，好像就不知道能用 JavaScript 做什麼事了。所以，為了破除迷思，就面對他吧！這是一個小小的實驗，讓我們嘗試用 JavaScript(Node.js) 打造自己的網頁瀏覽器，支援 HTML5/CSS3/WebGL 等最新標準的瀏覽器！&lt;br /&gt;
&lt;br /&gt;
如果你至今還是無法完全明白 Node.js 的能耐，期待與你見面交流。</description><link>https://fred-zone.blogspot.com/2013/07/coscup-2013-is-coming.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-7298340645157489125</guid><pubDate>Mon, 22 Jul 2013 03:57:00 +0000</pubDate><atom:updated>2013-07-22T11:57:21.780+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Cross-compile</category><category domain="http://www.blogger.com/atom/ns#">Cubieboard</category><category domain="http://www.blogger.com/atom/ns#">Embedded System</category><category domain="http://www.blogger.com/atom/ns#">Linux硬體驅動</category><category domain="http://www.blogger.com/atom/ns#">商業</category><category domain="http://www.blogger.com/atom/ns#">技術新知</category><category domain="http://www.blogger.com/atom/ns#">系統研發手札</category><category domain="http://www.blogger.com/atom/ns#">自由軟體</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><title>【玩轉 Cubieboard】安裝 u-boot 到 SD Card</title><description>當你得到 Cubieboard 後，首先要做的就是學習如何啟動你的作業系統。Cubieboard 可以支援 SD Card 啟動，這通常也是最容易入門的 Embedded System 開機方法。你只要準備一張 Micro SD Card，切割出分割區，然後把 Bootloader 和作業系統放進去即可。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;安裝 u-boot&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
Cubieboard 使用的是 u-boot 做為 bootloader，你必須先下載 Source Code 然後 Compile：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;git clone https://github.com/linux-sunxi/u-boot-sunxi.git
make Cubieboard CROSS_COMPILE=arm-linux-gnueabihf-&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
如果你的系統 toolchain 都已經安裝完成，且都沒有問題，u-boot 會順利編譯通過，然後你會在 u-boot 的目錄裡得到兩個檔案：&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;spl/sunxi-spl.bin&lt;/li&gt;
&lt;li&gt;u-boot.bin&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
有了這兩個檔案，安裝 u-boot 就不是什麼問題，只需要使用 dd 指令來把 bootloader 寫入 SD Card：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;dd if=spl/sunxi-spl.bin of=/dev/sdb bs=1024 seek=8
dd if=u-boot.bin of=/dev/sdb bs=1024 seek=32&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
註：/dev/sdb 是 SD Card 的裝置路徑，請依照你系統上的實際狀況，填入正確路徑。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;後記&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
有趣的是，很久沒有玩開發板了，自從兩年多前開始陸續投入各廠商的專案後，所碰到的 ARM Board，清一色都是客製化的產品樣板。而幫大廠商做產品，和自己玩開發板相當不一樣，同樣是驅動程式有問題，要做的是打電話罵人；如果會當機，快打電話罵人；如果缺任何東西，打電話罵人就對了！所以，在客戶的專案中，做最多的總是證明硬體廠商的問題，讓硬體廠商們心甘情願幫你把問題排除。雖然還是要熟悉 Debug 的技巧，但多半不用自己煩惱『該怎麼辦』，只需要下指導棋。因此，自己也慢慢養成很多惰性，除非是真的沒人可以幫你，才肯親自出手跳下去從底層到應用程式，深入修正問題。&lt;br /&gt;
&lt;br /&gt;
但是，玩開發板可不是這麼一回事。有問題？你自己搞定吧。搞不定？問 Google 大神吧。還是不行？慢慢看官方或是社群的 Source Code，全面理解後，再把問題解決吧。玩開發板可以更全面了解開發嵌入式系統過程中的問題，而且問題五花八門，運氣不好，你更會碰到版本大亂鬥，各種系統、程式、驅動程式都有幾十種版本，但只有少數幾種組合會是沒有問題的。光是把自己想要的系統架起來，就必須先費好一番功夫，而這還只是基礎建設而已。速度快一點，幾個小時到一個晚上，慢一點，也要研究個好幾天，或是直接放棄。這還沒談到進一步客製化，從會動的組合中，修改成你要的需求，最後還得開發你想要開發的應用程式。&lt;br /&gt;
&lt;br /&gt;
我想，既然我們要經營這片 Cubieboard，解決這類煩雜的問題，將會是我們的首要目標，讓開發者能更容易在上面開發程式。:-)</description><link>https://fred-zone.blogspot.com/2013/07/cubieboard-u-boot-sd-card.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-138252864954618277</guid><pubDate>Mon, 22 Jul 2013 03:55:00 +0000</pubDate><atom:updated>2013-07-22T11:55:59.951+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Cubieboard</category><category domain="http://www.blogger.com/atom/ns#">Embedded System</category><category domain="http://www.blogger.com/atom/ns#">商業</category><category domain="http://www.blogger.com/atom/ns#">技術新知</category><title>跟猴兒一樣精的 Cubieboard</title><description>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://cubieboard.org/wp-content/uploads/2013/01/4001-259x300.png&quot; /&gt;&lt;/div&gt;&lt;br /&gt;
我們的團隊，最近取得了『&lt;a href=&quot;http://cubieboard.org/&quot;&gt;Cubieboard&lt;/a&gt;』這開發板的代理權，負責台灣、香港等地的銷售和市場發展。這是很令人興奮的，因為我們好久沒有辦法全心全意開發自己想開發的東西、研究自己想研究的技術、打樣、以及對客戶驗證自己的想法，現在，終於可以名正言順的投入，利用這通用平台做一些不一樣的事情。&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://cubieboard.org/wp-content/uploads/2012/10/kk1.jpg&quot; /&gt;&lt;/div&gt;&lt;br /&gt;
開發板顧名思義就是可以讓你開發的硬體、讓你用軟體控制的裝置，如果你有任何想法，都可以在上面做實驗，或是 DIY 打造出外面買不到的裝置，甚至做出產品原型。如果你有興趣，無論是個人和公司團體，歡迎與我們聯絡﹝frankie@mandice.com﹞。</description><link>https://fred-zone.blogspot.com/2013/07/cubieboard.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>5</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-8113394087439398791</guid><pubDate>Thu, 23 May 2013 20:55:00 +0000</pubDate><atom:updated>2013-05-24T04:55:40.537+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Embedded System</category><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">Linux</category><category domain="http://www.blogger.com/atom/ns#">Stem OS</category><category domain="http://www.blogger.com/atom/ns#">Web</category><category domain="http://www.blogger.com/atom/ns#">Web 相關技術</category><category domain="http://www.blogger.com/atom/ns#">心得分享、講座</category><category domain="http://www.blogger.com/atom/ns#">技術新知</category><category domain="http://www.blogger.com/atom/ns#">自由軟體</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><category domain="http://www.blogger.com/atom/ns#">顧問咨詢</category><category domain="http://www.blogger.com/atom/ns#">顧問資詢</category><title>JSDC 2013 簡報釋出！NPK 與 JavaScript OS (Stem) 登場！</title><description>最近比較忙，一直拖到現在，才有點時間上傳我在今年 JSDC 的簡報。這次，給的講題是『我編譯故我在：誰說 Node.js 程式不能編成 Binary』，主要探討怎麼使用 NPK 打包你寫的 Node.js 程式，以及把 JavaScript 程式碼編譯成二進位(Binary)檔，甚至是進一步的加密設計。NPK 雖然還只是初版，目前支援了三種博君一笑的封裝等級：卸妝(Remove mock-up)、媽媽不認識(Mom doesn&#39;t recognize)、爸爸不認識(Dad doesn&#39;t recognize)，有興趣了解這三種等級不同的人，可以參考我的簡報：&lt;br /&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;&lt;iframe frameborder=&quot;0&quot; height=&quot;400&quot; marginheight=&quot;0&quot; marginwidth=&quot;0&quot; scrolling=&quot;no&quot; src=&quot;http://www.slideshare.net/slideshow/embed_code/21787393&quot; width=&quot;476&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;br /&gt;
當然，參加這種活動需要的是熱血，不在 Lightening Talk 閃電秀登場一下怎麼可以呢？於是當天毫不考慮的搶了一個名額，用短短的五分鐘介紹 Mandice 團隊最近在做的 JavaScript OS - 『&lt;a href=&quot;http://stem.mandice.org/&quot;&gt;Stem&lt;/a&gt;』。在 Stem 的平台之下，無論你是否為嵌入式系統或作業系統的高手，只需要懂 JavaScript 和純粹的 Web 相關技術，你就可以開發自己的嵌入式應用，或讓你的舊電腦或開發板有全新的用途。&lt;br /&gt;
&lt;br /&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;&lt;iframe src=&quot;http://www.slideshare.net/slideshow/embed_code/21787839&quot; width=&quot;476&quot; height=&quot;400&quot; frameborder=&quot;0&quot; marginwidth=&quot;0&quot; marginheight=&quot;0&quot; scrolling=&quot;no&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;br /&gt;
&lt;b&gt;後記&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
大家都知道，做外包工作，一向很難累積東西，唯一能累積的，就是設計工具以加速自己做案子的速度。因此，Stem OS 就是我們經歷過許多的案子後，自己設計的平台，讓我們可以盡可能完全用 JavaScript 快速開發嵌入式系統，然後結合硬體開發設計各式應用。在 Stem 裡，只需要懂 JavaScript 和 Web 相關技術，就能勝任開發工作，實現你心中的 Idea。如果說 3D Printer 是可以讓你快速打造模型實體，那 Stem OS，就是能將你心中的 Idea，化為空硬體的靈魂，Idea 的 3D Printer。&lt;br /&gt;
&lt;br /&gt;
這對創業者、創新者和非資訊產業的公司來說，都是一個可以快速切入設計產品和自家工具的機會，不妨嘗試看看。:-)&lt;br /&gt;
&lt;br /&gt;
如果有任何需求，無論是顧問、教育訓練還是合作開發，都歡迎與我們 Mandice 聯絡，或來信到 fred@mandice.com。</description><link>https://fred-zone.blogspot.com/2013/05/jsdc-2013-npk-javascript-os-stem.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>3</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-221168626139584882</guid><pubDate>Thu, 09 May 2013 15:27:00 +0000</pubDate><atom:updated>2013-05-09T23:28:33.718+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">Node.js</category><category domain="http://www.blogger.com/atom/ns#">Web 相關技術</category><category domain="http://www.blogger.com/atom/ns#">技術新知</category><category domain="http://www.blogger.com/atom/ns#">程式心得筆記</category><category domain="http://www.blogger.com/atom/ns#">系統研發手札</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><title>NPK 釋出！打包你的 Node.js 程式！</title><description>『&lt;a href=&quot;https://github.com/cfsghost/npk&quot;&gt;NPK&lt;/a&gt;』是什麼？一個打包 Node.js 程式的工具，之所以會有這個工具，完全是拜下周（5/18 - 5/19）將在中研院舉行的 JSDC 所賜。因為這次筆者也有個 Talk，將探討如何去編譯我們寫出來的 Node.js 程式，所以過程中便寫出了這樣一個工具。&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;https://github.com/cfsghost/npk&quot;&gt;https://github.com/cfsghost/npk&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
事實上，目前的 NPK 不是個新東西。JavaScript 經過多年的發展，早已經有非常多合併 JavaScript 程式碼的工具可以使用，對於前端工程師來說，是再尋常不過。不過，針對 Node.js 的程式碼，這樣的工具卻相對來說非常少。&lt;br /&gt;
&lt;br /&gt;
如果你想嘗試使用看看，可以立即用 NPM 來安裝 NPK：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;npm install npk -g&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
以 NPK 裡的範例專案為例，你可以直接下命令打包該專案：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;npk tests/purejs/&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
接著你可以在輸出的資料夾中找到打包好的檔案：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;tests/purejs/out/app.js/&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
甚至是直接執行他：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;node tests/purejs/out/app.js/app.js&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
NPK 工具定義了一個新的屬性『npk_target』在 package.json，用來讓程式人員自行決定要合併和連結哪些 JavaScript 程式（像過去為 C/C++ 等語言寫 Makefile），可以參考 NPK 內附範例專案的 package.json。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;最終目標『編譯』成二進位格式（Binary）&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
在與一些企業以及同業分享 Node.js 技術和其未來發展時，時不時會有人提出一個問題：『我們能不能編譯自己寫的 Node.js 程式？』這個問題，往往會開始引發一長串的討論，有一些人為終止這個問題，甚至會開始引用 JavaScript 語言的設計宗旨：『明碼保存，不需要預先編譯』，來強調我們不應該把 JavaScript 的優勢給閹掉。當然，這樣一廂情願的說法，是不可能消除許多人的疑慮，尤其是和 Open Source 路線不熟的企業，或是被『壞客戶欺負』的工程師。&lt;br /&gt;
&lt;br /&gt;
有些眼睛比較亮的人，或許已經發現到，這其實是個複合性的命題，會提出這個問題的人，多半想要知道或得到的是：&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;能否透過預先編譯 JavaScript 程式，讓執行效能提升。&lt;/li&gt;
&lt;li&gt;想要打包自己寫的程式，讓他合併成一個，以方便釋出或管理。&lt;/li&gt;
&lt;li&gt;保護自己的程式碼。&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
這些都將會是 NPK 的目標，但現在暫且不提 Node.js 程式碼被編譯的可行性，更多細節將會在 JSDC 的議程上有更近一步的探討。但若只是要達到第二項，打包自己的程式，將 Node.js 程式碼合而為一，你現在就已經可以用『&lt;a href=&quot;https://github.com/cfsghost/npk&quot;&gt;NPK&lt;/a&gt;』工具做到。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;後記&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
為了準備 JSDC 的 Talk，過程中筆者原本使用了一個 NPM 上現成的模組『nld』，完成合併 Node.js 程式碼的工作，但由於該模組有一些問題，而且有一年以上沒有維護。種種因素，才促使自己動手做一個新的 Node.js Linker 給 NPK。&lt;br /&gt;
&lt;br /&gt;
此外，有興趣的人可以試著用編輯器打開打包成功的 JavaScript 檔案，你除了會看到所有的 Source Code 已經被合併，而且會發現程式碼都經過 Uglify 處理過，某種程度上可以算是被加密了。</description><link>https://fred-zone.blogspot.com/2013/05/npk-nodejs.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-2431868936726960616</guid><pubDate>Sun, 21 Apr 2013 18:35:00 +0000</pubDate><atom:updated>2013-04-22T02:35:34.396+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Desktop</category><category domain="http://www.blogger.com/atom/ns#">Embedded System</category><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">Linux</category><category domain="http://www.blogger.com/atom/ns#">Node.js</category><category domain="http://www.blogger.com/atom/ns#">Stem OS</category><category domain="http://www.blogger.com/atom/ns#">Web</category><category domain="http://www.blogger.com/atom/ns#">Web 相關技術</category><category domain="http://www.blogger.com/atom/ns#">Webkit</category><category domain="http://www.blogger.com/atom/ns#">心得分享、講座</category><category domain="http://www.blogger.com/atom/ns#">有趣新構思</category><category domain="http://www.blogger.com/atom/ns#">自由軟體</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><title>OSDC.TW 2013 簡報釋出！使用 JavaScript 大搞桌面應用和嵌入式系統！</title><description>OSDC.TW 2013 順利在中央研究院國際會議廳閉幕，但一如往年，失落感接踵而來。為了這次的 Talk，可說是花了不少工夫，除了研究 Webkit 的內部實作，以及 V8 JavaScript Engine 之外，最後也發表了一個新的 JavaScript 作業系統『&lt;a href=&quot;http://stem.mandice.org/&quot;&gt;Stem&lt;/a&gt;』，一個可以完全用 JavaScript 和 Web 技術去開發應用程式的嵌入式作業系統。Stem 落實了用 JavaScript 語言開發嵌入式系統的理想，並簡化嵌入式應用開發，讓人人都能輕鬆躍過以往嵌入式系統開發的艱澀門檻。&lt;br /&gt;
&lt;br /&gt;
這邊釋出當天的簡報檔，有興趣者可自行參閱：&lt;br /&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;&lt;iframe frameborder=&quot;0&quot; height=&quot;400&quot; marginheight=&quot;0&quot; marginwidth=&quot;0&quot; scrolling=&quot;no&quot; src=&quot;http://www.slideshare.net/slideshow/embed_code/19410474&quot; width=&quot;476&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;br /&gt;
此外，對 Stem 有興趣的人，可以拜訪專案網站：&lt;br /&gt;
&lt;br /&gt;
&lt;a href=&quot;http://stem.mandice.org/&quot;&gt;http://stem.mandice.org/&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
也可以在 IRC 上交流：&lt;br /&gt;
&lt;br /&gt;
英文頻道：irc.freenode.net #stemos&lt;br /&gt;
中文頻道：irc.freenode.net #stemos-zh&lt;br /&gt;
&lt;br /&gt;
註：目前 Stem 已釋出 x86 版本的安裝鏡像檔，近期內將會釋出 ARM 的版本，敬請期待。有興趣參與開發者，歡迎來信一同交流。</description><link>https://fred-zone.blogspot.com/2013/04/osdctw-2013-javascript.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-3509587483302818972</guid><pubDate>Mon, 01 Apr 2013 14:26:00 +0000</pubDate><atom:updated>2013-04-01T22:27:18.466+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">HTML5</category><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">Node.js</category><category domain="http://www.blogger.com/atom/ns#">Web 相關技術</category><category domain="http://www.blogger.com/atom/ns#">Webkit</category><category domain="http://www.blogger.com/atom/ns#">心得分享、講座</category><category domain="http://www.blogger.com/atom/ns#">技術新知</category><category domain="http://www.blogger.com/atom/ns#">自由軟體</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><title>這年頭，你只需要懂 node-webkit ！</title><description>極為幸運，今年初的投稿被 [&lt;a href=&quot;http://osdc.tw/&quot;&gt;OSDC 2013&lt;/a&gt;] 錄取，本次主題是『node-webkit』，將於 4/20（六）早場起，全面進攻。&lt;br /&gt;
&lt;br /&gt;
你可能在問什麼是『&lt;a href=&quot;https://github.com/rogerwang/node-webkit&quot;&gt;node-webkit&lt;/a&gt;』？完全搞不清楚頭緒？身為 JavaScript 和 Node.js 的重度使用者，問問自己內心吧！不用懷疑，你最想要的東西，就在眼前。試著想像，一個沒有邊框又可獨立執行的瀏覽器，裡面跑著你用 JavaScript + HTML5 寫的應用程式，有如原生的桌面應用程式，當你想發行並釋出程式時，還能打包成一個獨立運作的執行檔，令人垂涎三尺呀！&lt;br /&gt;
&lt;br /&gt;
『node-webkit』顧名思義就是將 Node.js 整合進 webkit（Chrome/Chromium/Sarfari 所用的網頁排版引擎）之中，讓你可以在網頁程式中使用功能強大的 Node.js。這意味著，你完全可以用 Node.js + HTML5 等 Web technology 開發跨平台『桌面應用程式』。由於『node-webkit』狹著 Node.js 以及其強大的第三方模組支援，而使用者介面採用了 Chromium（Google Chrome 的開放源始碼版本）核心當做繪圖引擎，能完全支援當前最新的各種網頁技術（如：HTML5、CSS3、WebGL 等）。因此，只要身為一個前端網頁開發者，都能輕易利用它設計出優雅、炫麗的圖形化介面，然後完成一支獨立運行的桌面應用程式。&lt;br /&gt;
&lt;br /&gt;
如官方網站的範例，在前端網頁程式中使用 Node.js：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
&amp;lt;title&amp;gt;Hello World!&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
&amp;lt;h1&amp;gt;Hello World!&amp;lt;/h1&amp;gt;
We are using node.js &amp;lt;script&amp;gt;document.write(process.version)&amp;lt;/script&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
這年頭，你真的只需要懂 node-webkit！我們 OSDC 2013 中研院國際會議廳見！</description><link>https://fred-zone.blogspot.com/2013/04/node-webkit.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>2</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-1313319072849325287</guid><pubDate>Mon, 11 Mar 2013 04:42:00 +0000</pubDate><atom:updated>2013-03-11T21:36:24.773+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Embedded System</category><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">Node.js</category><title>歡迎洽詢！Node.js on Embedded System 專業技術服務！</title><description>&lt;div dir=&quot;ltr&quot;&gt;
經過一年多的孕釀，我們的團隊【Mandice】已有萬全的準備，在此正式對外宣布提供 Node.js on Embedded System 的專業技術服務，歡迎立即來信洽詢。&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
嵌入式系統開發生態已經改變，今天的 Node.js 已為嵌入式系統帶來全新的面貌，其高效開發的優勢，以及不俗的效能和數不清的現成模組，讓嵌入式系統的產品開發周期以及技術門嵌大大降低。導入使用 Node. js 能讓產品設計者，更能專注於創新和使用者體驗，因應這時代的快速發展。&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
我們的團隊在這一年間，已經深耕各類 Node.js 相關技術，更結合團隊過去的系統技術和產品經驗，提供了整套的系統軟體平台以及顧問服務給許多合作夥伴和大廠，讓其開始以 Node.js 開發自家產品。至今，已有一些面世或即將面世的產品，使用了 Node.js 的技術開發嵌入式系統，甚至已經著手準備第二代產品，成效相當不錯。&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
若您也想趕上這班列車，歡迎與我們聯繫，我們將提供您最好的服務。&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div dir=&quot;ltr&quot;&gt;
有意請來信至電子郵件信箱，我們會立即與您聯繫:&lt;br /&gt;
fred@mandice.com&lt;/div&gt;
</description><link>https://fred-zone.blogspot.com/2013/03/nodejs-on-embedded-system.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>2</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-7173440700539724618</guid><pubDate>Fri, 22 Feb 2013 12:19:00 +0000</pubDate><atom:updated>2013-02-22T20:19:59.645+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">Node.js</category><category domain="http://www.blogger.com/atom/ns#">Opens</category><category domain="http://www.blogger.com/atom/ns#">程式心得筆記</category><category domain="http://www.blogger.com/atom/ns#">自由軟體</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><title>Node.js EventEmitter 事件管理神器</title><description>如果你已經相當熟悉 JavaScript 的開發，那就肯定對其事件機制不陌生。我們等待一個物件產生事件，去觸發我們設定的 callback 函式，是最常見不過的東西了。不過，如果你想要設計一個自己的物件，並容許別人可以監聽它的事件，要怎麼做呢？&lt;br /&gt;
&lt;br /&gt;
如果你什麼都沒有，你肯定是在自己的物件中宣告一個 event handler 的表，當別人想要針對特定的事件掛上 callback function 時，就記錄在該表內。然後在事件被產生時，去呼較對應事件的所有 callback function。然後，你會設計像 .on() 、off() 和 emit() 之類的方法，讓別人可以使用。&lt;br /&gt;
&lt;br /&gt;
是的，很累。既然事件機制是我們在 JavaScript 中常用的功能，每個物件都寫一次這樣簡單的事件管理，真的很浪費時間。其實，Node.js 本來就有實作這樣的機制『EventEmitter』讓開發者直接使用。&lt;br /&gt;
&lt;br /&gt;
EventEmitter 的使用方法很簡單：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;var util = require(&#39;util&#39;);
var events = require(&#39;events&#39;);

var MyClass = function() {
};

util.inherits(MyClass, events.EventEmitter);

MyClass.prototype.ping = function() {
    this.emit(&#39;response&#39;, &#39;pong&#39;);
};&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
實際使用這個 Class 建立物件，然後監聽事件，最後觸發事件：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;var myClass = new MyClass();

myClass.on(&#39;response&#39;, function(msg) {
    console.log(msg);
});

myClass.ping();&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
&lt;b&gt;後記&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
把 EventEmitter 的繼承寫在 Class 宣告和 prototype 宣告之間很彆扭，但在 Class 宣告後立即使其繼承 EventEmitter 是正確的邏輯行為，所以，勉強接受他吧。</description><link>https://fred-zone.blogspot.com/2013/02/nodejs-eventemitter.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-8787707129051861965</guid><pubDate>Sat, 15 Dec 2012 20:58:00 +0000</pubDate><atom:updated>2012-12-16T05:04:41.697+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Glib</category><category domain="http://www.blogger.com/atom/ns#">Node.js</category><category domain="http://www.blogger.com/atom/ns#">NPM</category><category domain="http://www.blogger.com/atom/ns#">自由軟體</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><title>Release GContext Node.js Module!</title><description>How to write a native desktop application in Node.js? Perhaps you know many modules can do that easily, something&#39;s like node-gui and jsdx-toolkit. In order to develop system program, DBus module is the most important thing you must have as well. With Dbus module, you can handle all communications between programs. Furthermore, you can control low-level functions system provided via DBus, which includes hardware, networking and operation system features. Unfortunately, all things do not work at all since new version of Node.js.&lt;br /&gt;
&lt;br /&gt;
Many system libraries are based on GLib, and work with GLib event loop. For writing a node.js binding with such libraries, libev is the only way to integrate event loop between node.js event loop and GLib main context. The problem is that libev is deprecated since 0.8 node.js cause modules which depends on libev will be broken on latest and higher version. Nothing works if there is no libev.&lt;br /&gt;
&lt;br /&gt;
In fact, upstream announced that libuv is being only one way to handle all low-level operations(I/O, threads, timer and poll). However, there is no people to re-work  integration of GLib event loop with new libuv APIs, even six months have passed already. This problem must be solved if we want to write system program and native application in Node.js.&lt;br /&gt;
&lt;br /&gt;
Besides, modules which works with GLib event loop have own implementation for event loop integration in the past, conflict occurs properly between each module. Your program always crashes or hangs when normal process of event loop is interrupted by another module.&lt;br /&gt;
&lt;br /&gt;
For theses reasons, I wrote a Node.js module &quot;GContext&quot; to do GLib main context integration, used libuv APIs to support latest version of Node.js. It&#39;s an individual module, so it&#39;s possible to be used directly by other modules. As long as it uses GContext module, it can run a GLib event loop on Node.js and share this loop with other modules. There is no need to re-implement this mechanism by themselves anymore.&lt;br /&gt;
&lt;br /&gt;
You can install it from NPM directly:&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;npm install gcontext&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Initiate main context and run a GLib loop on Node.js event engine:&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;var GContext = require(&#39;gcontext&#39;);
GContext.init();&lt;/pre&gt;&lt;/div&gt;</description><link>https://fred-zone.blogspot.com/2012/12/release-gcontext-nodejs-module.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>18</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-4823560865821793195</guid><pubDate>Fri, 09 Nov 2012 23:52:00 +0000</pubDate><atom:updated>2012-11-10T07:52:58.308+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Mandice</category><category domain="http://www.blogger.com/atom/ns#">心得分享、講座</category><category domain="http://www.blogger.com/atom/ns#">技術新知</category><category domain="http://www.blogger.com/atom/ns#">自由軟體</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><category domain="http://www.blogger.com/atom/ns#">雲端(Cloud)</category><category domain="http://www.blogger.com/atom/ns#">顧問咨詢</category><title>AppHouse 簡報釋出！</title><description>昨日應邀參加『&lt;a href=&quot;http://seminar.cloud.org.tw/2012_Cloud/1011109.php&quot;&gt;企業雲端環境建置與應用技術研討會&lt;/a&gt;』，給予一場『AppHouse - 國產 PaaS』的簡報分享，聽眾相當熱情，在會後也一起相互討論了許多技術與非技術等議題。這次的分享，主要在說明 AppHouse Project 的細節，並闡述了 PaaS 在雲端產業中的定位。為了讓聽眾更能理解，也特意以小弟的淺見和觀點，用簡單明瞭的方式描述『什麼是雲端』。&lt;br /&gt;
&lt;br /&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;&lt;iframe frameborder=&quot;0&quot; height=&quot;400&quot; marginheight=&quot;0&quot; marginwidth=&quot;0&quot; scrolling=&quot;no&quot; src=&quot;http://www.slideshare.net/slideshow/embed_code/15107268&quot; width=&quot;476&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;br /&gt;
&lt;b&gt;後記&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
會後與不少人交流，發現過去很多人被『雲端』一詞搞糊塗了，不能理解。如果你也有此困擾，不妨可以看看此簡報。&lt;br /&gt;
&lt;br /&gt;
若您有任何疑問，或需要協助的地方，歡迎與我聯絡。:-)</description><link>https://fred-zone.blogspot.com/2012/11/apphouse.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-9011457082956736752</guid><pubDate>Tue, 23 Oct 2012 02:59:00 +0000</pubDate><atom:updated>2012-10-23T10:59:44.667+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Embedded System</category><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">JSDX</category><category domain="http://www.blogger.com/atom/ns#">Linux</category><category domain="http://www.blogger.com/atom/ns#">NPM</category><category domain="http://www.blogger.com/atom/ns#">技術新知</category><category domain="http://www.blogger.com/atom/ns#">程式心得筆記</category><category domain="http://www.blogger.com/atom/ns#">系統研發手札</category><category domain="http://www.blogger.com/atom/ns#">自由軟體</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><title>jsdx-power 讓你使用 JavaScript 實現電源管理</title><description>既然會成立新創公司，總有些自己的想法，也期望做一些和其他人不一樣的事，對我們而言，改變世界事小，留下長久價值更重要。也許你有點印象，筆者今年度到處給予的 Talk，多半都是與 Node.js 相關，也透漏許多正在執行的計劃。因此，有些人可能聽過筆者提及『JSDX Framework』，這是『&lt;a href=&quot;http://www.mandice.com/&quot;&gt;Mandice&lt;/a&gt;』的長期計劃，也是將 JavaScript 落實在系統、桌面和嵌入式裝置的解決方案。&lt;br /&gt;
&lt;br /&gt;
由於 JSDX 是一個開放原始碼專案，市場上也已經有一些產品和公司專案導入使用，但因為合約的關係，我不能透漏太多細節，所以本文將只討論些許技術細節。說到這，也許你還是沒什麼概念，但若是真要舉出最直接與其相關的專案，就是今年 8 月，於 COSCUP 一場議程中所公開的『JUICE Desktop Environment』（簡報檔詳見舊文：&lt;a href=&quot;http://fred-zone.blogspot.tw/2012/08/coscup-2012.html&quot;&gt;【COSCUP 2012】閉幕！簡報釋出！&lt;/a&gt;），完全以 JavaScript 實作的 3D 桌面環境。&lt;br /&gt;
&lt;br /&gt;
JSDX 包括許多元件，從 GUI Toolkit（使用者界面）、Multimedia Player（多媒體播放）、Network Manager（網路管理）到 Sound Manager（聲音管理）等，提供了各類系統控制或硬體相關的 APIs。程式開發者只需要會 JavaScript 語言，就可以使用這些 JSDX 所提供的 APIs ，開發各類應用程式或系統程式。&lt;br /&gt;
&lt;br /&gt;
回應本文標題，JSDX 如果可以拿來開發桌面環境，當然也有電源管理相關的 APIs 可使用。所以在這邊我們要討論的元件是『&lt;a href=&quot;https://github.com/cfsghost/jsdx-power&quot;&gt;jsdx-power&lt;/a&gt;』，你可以在 github 上取的他的 Source Code，亦或是直接透過 npm 安裝。&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;npm install jsdx-power&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
這邊簡單示範如何使用 JavaScript 透過『jsdx-power』命令作業系統休眠：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;var Power = require(&#39;jsdx-power&#39;);

var power = new Power;

power.init(function() {
 power.suspend();
});&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
&lt;b&gt;後記&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
JUICE DE 的 juice-panel 電量顯示，就是使用了 jsdx-power，有興趣的人可以參考其 Source Code。</description><link>https://fred-zone.blogspot.com/2012/10/jsdx-power-javascript.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-773824967034415015</guid><pubDate>Sun, 07 Oct 2012 22:04:00 +0000</pubDate><atom:updated>2012-10-08T06:45:34.534+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">Node.js</category><category domain="http://www.blogger.com/atom/ns#">NodeJS</category><category domain="http://www.blogger.com/atom/ns#">程式心得筆記</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><title>Node.js 裡的 JavaScript Array 非同步操作</title><description>如果你原本是一個專業的前端工程師，或許在 Node.js 出來之後，便開始嘗試了 Server 程式的開發。然後發現，使用 JavaScript 開發 Server 程式的過程中，有很多的細節應該注意，尤其是碰上處理大量資料和 I/O 操作時，更要對 JavaScript 引擎的運作原理有一定程度的理解。因為，絕大多數的問題，都是出在非同步（Asynchronize）與同步（Synchronize）的機制上。&lt;br /&gt;
&lt;br /&gt;
許多開發人員，都會認同在開發 Node.js 程式時，盡可能、甚至絕對不要使用『同步』的方法和 API，因為，那會打亂 JavaScript 事件引擎的運作，產生各種詭異的後疑症，而且非常難以除錯。如果你發現你的程式會莫明自己結束，或是程式運作的流程不如預期，可能就是掉入了『同步』與『非同步』的陷阱了。&lt;br /&gt;
&lt;br /&gt;
如果你稍微翻過文件，就會發現，其實 Node.js 本身對很多的 API，已經同時提供了『同步』與『非同步』兩種版本供開發者使用。不過，雖然 Node.js API 都有提供『非同步』版本，但是有些根本東西，卻欠缺『非同步』的方法，像是本文標題所提到的『Array』，就是其中之一。&lt;br /&gt;
&lt;br /&gt;
針對 Array 的諸多操作，最常見的莫過於 forEach 方法，用來處理每一個 Array item：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;var arr = [ 1, 2, 3, 4 ];

arr.forEach(function(element, index, arr) {
    // Processing
});

console.log(&#39;Completed&#39;);&lt;/pre&gt;&lt;/div&gt;然而，這個方法是『同步（Synchronize）』，也就是會讓整個程式阻塞，等所有 Array item 讀出和處理完之後，才顯示最後的『Completed』字串。&lt;br /&gt;
&lt;br /&gt;
類似的東西，不少人也會使用純 for-loop 來實作，但也都是一樣的狀況：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;var arr = [ 1, 2, 3, 4 ];

for (var i in arr) {
    // Processing
}

console.log(&#39;Completed&#39;);&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
想想看，如果 Array item 的數量相當多，又或者是每個 item 的處理時間相當長，整支程式會像死當一樣沒反應。要知道，讓一段程式跑太久，是 Event-driven 的忌諱，更是 Server 程式的大敵，因為它會讓你的網站服務反應速度極慢。可是，這樣操作 Array 資料的需求，卻又是非常常見。&lt;br /&gt;
&lt;br /&gt;
還好，多數人開始學習 Node.js 之後，因為對 Event-driven 的機制開始越來越熟悉，所以，開始能夠瞭解並利用各種非同步的方法，讓程式避免阻塞死鎖，增加效能和處理更多的連線要求。也因為如此，針對 Array 的遍歷操作，自然懂得開始使用遞迴的方式處理。但是，這樣常見的用法，時時都會出現，每次都要重新實作一次，豈不麻煩？&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;尋思，我們是否可以有個簡單的做法，以『非同步（Asynchronize）』的方法去操作 Array 呢？&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
當然，筆者自認是世界上最懶惰的人，所以，就為此開發出『&lt;a href=&quot;https://github.com/cfsghost/node-array&quot;&gt;node-array&lt;/a&gt;』，讓日後能少打點字：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;https://github.com/cfsghost/node-array&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
安裝方法相當簡單，你可以直接從 NPM 上安裝：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;npm install node-array&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
使用方法更簡單，node-array 提供了一個新的方法『forEachAsync()』，如 Array 原生的 forEach 一樣使用即可：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;var Array = require(&#39;node-array&#39;);
    
var a = [ 1, 2, 3, 4, 5 ];
    
a.forEachAsync(function(element, index, arr) {
    console.log(element);
}, function() {
    console.log(&#39;complete&#39;);
});&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
從上面的程式碼可以知道，其實 node-array 是幫原生的 JavaScript Array Object 擴充功能，繼承了原生的 Array Object，並添加了一個新的方法進去。所以，你一但在開頭將 Array 用 node-array 取代掉，之後所有建立的 Array Object 都能有 forEachAsync() 方法可使用。從此，你不用再花心力去搞定遞迴和 process.nextTick() 了！&lt;br /&gt;
&lt;br /&gt;
最後一提，node-array 初步只實作了最常用到的 forEach，日後也陸續會將其他常用的功能加入。如果您有什麼想法或 patch，歡迎提交給小弟。:-)&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;後記&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
解決了這類根本的問題後，並應用在實際開發後，意外發現了一個正面的副作用，就是程式碼漂亮很多，也『平坦』許多。讓我不禁思考，很多想把 JavaScript 程式碼平坦化的工作，是否真的必要？若是所有根本性的問題解決後，是否程式碼自然就不會太高聳？</description><link>https://fred-zone.blogspot.com/2012/10/nodejs-javascript-array.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>2</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-1431633984345869120</guid><pubDate>Thu, 27 Sep 2012 20:25:00 +0000</pubDate><atom:updated>2012-09-28T04:33:25.577+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">C/C++</category><category domain="http://www.blogger.com/atom/ns#">Glib</category><category domain="http://www.blogger.com/atom/ns#">libuv</category><category domain="http://www.blogger.com/atom/ns#">Node.js</category><category domain="http://www.blogger.com/atom/ns#">程式心得筆記</category><category domain="http://www.blogger.com/atom/ns#">自由軟體</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><title>探究如何整合 GLib Main Event Loop 和 Node.js 的 libuv</title><description>在普通情況下，整合 GLib Main Event Loop 和 libuv 不是件平常人會做的事，因為，一般人使用著 GTK+、Clutter、DBus 等等函式庫(Library)時，永遠只會使用 GLib 而不會使用到第二套事件引擎。但是，在 Node.js 中，其事件引擎並不是 GLib，而是使用自己的『libuv』，想同時運行兩套事件引擎是不可能的，所以這將注定我們無法以 Node.js 去引入 GTK+、Clutter、DBus 等函式庫來使用。不過，天下文章一大抄，世界上所有事件引擎的設計差異不大，在理解 GLib Context 的運作後，我們還是可以嘗試將兩者整合在一起，協同運行。&lt;br /&gt;
&lt;br /&gt;
簡單理解一下事件引擎，其說白了就是一個跑到天荒地老的無窮迴圈，不停的去檢查是否有事件被喚醒。所以，由此可知，兩套事件引擎不能被同時跑起來，因為任何一個事件處理的無窮迴圈，都將導致另一個事件處理的迴圈無法正常運作。所以，首要解決的課題，就是讓兩個無窮迴圈可以同時運作。&lt;br /&gt;
&lt;br /&gt;
為了解決這樣的問題，你可能會想到去使用 Thread (執行緒/線程)，只要在建立兩個 Thread，然後各自跑不同的事件引擎就可以了。但是這樣做，問題馬上就會出現。由於程式正在運作的過程中，事件數量相當多，事件被安插和喚醒的次數非常頻繁，這將導致兩個 Thread 之間很難維持穩定的資料交換，你得實作 Mutex Lock 等機制來達成 Thread-safe，更還要想辦法解決兩邊各類大小資料交換的需求。種種原因，都會造成兩個事件引擎大量的彼此等待，而效能不彰。此外，也不易同時控制兩邊的事件觸發順序，以及事件被喚醒時間的精確度，如計時器(Timer)。&lt;br /&gt;
&lt;br /&gt;
既然使用 Thread 是不好的做法，是否有其他方式可以解決我們一開始的課題？解決方法是有的，從事件引擎的運作細節，我們可以發現一些端倪。&lt;br /&gt;
&lt;br /&gt;
一般來說，大部份的事件引擎都包括了幾個部份：&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;http://developer.gnome.org/glib/2.34/mainloop-states.gif&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;292&quot; src=&quot;http://developer.gnome.org/glib/2.34/mainloop-states.gif&quot; width=&quot;504&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
從 Initial 出發， Event Loop 的迴圈每運行一次，都經歷了『prepared』、『Polling』和『Dispatching』三種狀態，若用白話來說，他們所代表的意義就是『已準備好事件清單』、『監控事件』、『喚醒、分配並處理事件』。&lt;br /&gt;
&lt;br /&gt;
如果你去參考事件引擎程式碼，會發現在三個狀態之間，分別有『prepare()』、『query()』、『check()』和『dispatch()』等四個動作，而這四個動作的行為簡單來說是『準備事件清單』、『取得需要被監控的事件』、『確認是否事件被觸發』、『喚醒並處理被觸發的事件』。&lt;br /&gt;
&lt;br /&gt;
理解了事件引擎的運作流程後，該如何讓兩個引擎協同運作便有了答案。就如同本文一開頭所說，其實所有的事件引擎都大同小異，所以 GLib 和 libuv 也有差不多的行為，同樣有『prepare()』、『check()』等動作。那麼，我們只需要將 GLib 的事件處理機制，原封不動的一一搬到 libuv 上功能對應的地方即可。&lt;br /&gt;
&lt;br /&gt;
理論上，移植 GLib 的工作並不難，我們只要參考 GLib Source Code，把 g_main_loop_run() 和 g_main_context_iterate() 內的工作拆解出來，放到 libuv 的 prepare() 和 check() 就能達到目的。&lt;br /&gt;
&lt;br /&gt;
首先，在註冊兩個 libuv 的事件（用於 prepare 和 check）：&lt;br /&gt;
&lt;div&gt;
&lt;pre&gt;/* Prepare */
uv_prepare_init(uv_default_loop(), &amp;amp;prepare_handle);
uv_prepare_start(&amp;amp;prepare_handle, prepare_cb);

/* Check */
uv_check_init(uv_default_loop(), &amp;amp;check_handle);
uv_check_start(&amp;amp;check_handle, check_cb);
&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
然後在 uv_prepare_t 的 Callback Function(check_cb)，取得 GLib main context 的事件清單並監聽：&lt;br /&gt;
&lt;div&gt;
&lt;pre&gt;void prepare_cb(uv_prepare_t *handle, int status)
{
 gint i;
 gint timeout;
 struct gcontext *ctx = &amp;amp;g_context;

 g_main_context_prepare(ctx-&amp;gt;gc, &amp;amp;ctx-&amp;gt;max_priority);

 /* Getting all sources from GLib main context */
 while(ctx-&amp;gt;allocated_nfds &amp;lt; (ctx-&amp;gt;nfds = g_main_context_query(ctx-&amp;gt;gc,
   ctx-&amp;gt;max_priority,
   &amp;amp;timeout,
   ctx-&amp;gt;fds,
   ctx-&amp;gt;allocated_nfds))) { 

  g_free(ctx-&amp;gt;fds);
  g_free(ctx-&amp;gt;poll_handles);

  ctx-&amp;gt;allocated_nfds = ctx-&amp;gt;nfds;

  ctx-&amp;gt;fds = g_new(GPollFD, ctx-&amp;gt;allocated_nfds);
  ctx-&amp;gt;poll_handles = g_new(uv_poll_t, ctx-&amp;gt;allocated_nfds);
 }

 /* Polling */
 for (i = 0; i &amp;lt; ctx-&amp;gt;nfds; ++i) {
  GPollFD *pfd = ctx-&amp;gt;fds + i;
  uv_poll_t *pt = ctx-&amp;gt;poll_handles + i;

  struct gcontext_pollfd *data = new gcontext_pollfd;
  data-&amp;gt;pfd = pfd;
  pt-&amp;gt;data = data;

  pfd-&amp;gt;revents = 0;

  uv_poll_init(uv_default_loop(), pt, pfd-&amp;gt;fd);
  uv_poll_start(pt, UV_READABLE | UV_WRITABLE, poll_cb);
 }
}
&lt;/pre&gt;
&lt;/div&gt;
註：Polling 這段在實作 GLib 內部函數 g_main_context_poll() 的工作。&lt;br /&gt;
&lt;br /&gt;
監聽的部份，若有事件被觸發則呼叫 poll_cb()，所以我們也定義：&lt;br /&gt;
&lt;div&gt;
&lt;pre&gt;void poll_cb(uv_poll_t *handle, int status, int events)
{
 struct gcontext_pollfd *_pfd = (struct gcontext_pollfd *)handle-&amp;gt;data;

 GPollFD *pfd = _pfd-&amp;gt;pfd;

 pfd-&amp;gt;revents |= pfd-&amp;gt;events &amp;amp;
  ((events &amp;amp; UV_READABLE ? G_IO_IN : 0) |
  (events &amp;amp; UV_WRITABLE ? G_IO_OUT : 0));

 uv_poll_stop(handle);
 uv_close((uv_handle_t *)handle, NULL);
 delete _pfd;
}
&lt;/pre&gt;
&lt;/div&gt;
註：revents 在監聽前會被我們歸零，在事件觸發時被我們設值，而其用途是告知 GLib main context 該事件被什麼行為（讀或寫）觸發。&lt;br /&gt;
&lt;br /&gt;
最後是 check_cb()，我們要停止所有對事件的 Polling，然後讓 GLib main context 去檢查並處理事件：&lt;br /&gt;
&lt;div&gt;
&lt;pre&gt;void check_cb(uv_check_t *handle, int status)
{
 gint i;
 struct gcontext *ctx = &amp;amp;g_context;

 /* Release all polling events which aren&#39;t finished yet. */
 for (i = 0; i &amp;lt; ctx-&amp;gt;nfds; ++i) {
  GPollFD *pfd = ctx-&amp;gt;fds + i;
  uv_poll_t *pt = ctx-&amp;gt;poll_handles + i;

  if (uv_is_active((uv_handle_t *)pt)) {
   uv_poll_stop(pt);
   uv_close((uv_handle_t *)pt, NULL);
   delete (struct gcontext_pollfd *)pt-&amp;gt;data;
  }
 }

 g_main_context_check(ctx-&amp;gt;gc, ctx-&amp;gt;max_priority, ctx-&amp;gt;fds, ctx-&amp;gt;nfds);
 g_main_context_dispatch(ctx-&amp;gt;gc);
}
&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
到目前為止，GLib 的事件引擎已經可以良好的跑在 libuv 上。&lt;br /&gt;
&lt;br /&gt;
此外，由於筆者本身開發了許多 Node.js Module 都有用到 GLib Main Event Loop，如：『dbus』、『jsdx-toolkit』等，常有這樣的需求，所以直接將這個部份寫成 C++ Class，以便日後其他專案使用。如果你有需要，可以參考兩支檔案：&lt;br /&gt;
&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/cfsghost/jsdx-toolkit/raw/master/src/context.cpp&quot;&gt;context.cpp&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/cfsghost/jsdx-toolkit/raw/master/src/context.hpp&quot;&gt;context.hpp&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
將 Header 引入你的程式碼後，就可以直接宣告並使用它：&lt;br /&gt;
&lt;div&gt;
&lt;pre&gt;Context *context = new Context;
context-&amp;gt;Init();
&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;後記&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
libuv 的歷史和說明，本文就不多提，欲知更多細節，可參閱舊文『&lt;a href=&quot;http://fred-zone.blogspot.tw/2012/07/nodejs-v08-native-module.html&quot;&gt;Node.js v0.8 大變革？！Native Module 開發者的福音！&lt;/a&gt;』。而接下來，你只需要知道，日後『libuv』將是 Node.js 內統一的事件處理機制就可以，這對撰寫 Node.js C/C++ Add-on 的人來說尤為重要，因為無論是在哪一個作業系統，都可以直接使用 libuv API 來操作事件。</description><link>https://fred-zone.blogspot.com/2012/09/glib-main-event-loop-nodejs-libuv.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-3101479426246001533</guid><pubDate>Mon, 03 Sep 2012 13:43:00 +0000</pubDate><atom:updated>2012-09-03T22:41:52.863+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">MongoDB</category><category domain="http://www.blogger.com/atom/ns#">Web 相關技術</category><category domain="http://www.blogger.com/atom/ns#">自由軟體</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><category domain="http://www.blogger.com/atom/ns#">雲端(Cloud)</category><title>MongoDB Replication 簡記</title><description>就在幾天前，MongoDB 邁入了 2.2.0 的穩定版本。我們若回頭來看，MongoDB 一直到了 2.0 前後，比起早期版本，已經有長足的進步，並且支援了相當多的功能，也對規模化和資料庫系統管理下了很多功夫。對於大多數的資料庫應用，已經非常適合。&lt;br /&gt;
&lt;br /&gt;
若你對資料庫相關技術有些了解，就會知道，當資料庫的資料發展一定規模程度，或是要確保系統不當機時，我們就需要用到 Master/Slave 的方式去備份和備援，當主要(Master)伺服器出了問題，次要(Slave)伺服器便即時補上，保持系統運作。但是，既然已經有 Master/Slave 機制，是否可以有更多台備援呢？更或者進一步，將讀寫分開在不同伺服器，以分攤流量和系統負載，並加速讀寫速度。而 Replication 就是這樣的機制，可以用來動態同步多台資料庫伺服器的資料，也可以當主要伺服器因故下線時，讓其他伺服器即時替補主要機器的位置。&lt;br /&gt;
&lt;br /&gt;
在 Debian 上設定 MongoDB 的 Replication 相當容易，首先在想要變成主要(Primary)的機器上，打開設定檔(/etc/mongodb.conf)，並為我們的 Replication 群組命名（在 MongoDB 中稱為 ReplSet，一些書籍內翻譯成『複製組』）：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;replSet = mydb&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
重新啟動 MongoDB：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;$ sudo service mongodb restart&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
使用指令 mongo，進入 MongoDB 命令模式：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;$ mongo&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
於 MongoDB 命令模式中執行：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;# 初使化 replSet
rs.initiate(null)

# 加入自己的 IP 位置
rs.add(&quot;192.168.11.1:27017&quot;)
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
若是回應成功，請先稍待數秒鐘，等伺服器偵測和初使化。然後會發現 MongoDB 的命令提示字元從『SECONDARY』變成『PRIMARY』，此時，代表這台機器已經變成 ReplSet（複製組） 中的主要機器。&lt;br /&gt;
&lt;br /&gt;
同樣的，你可以開始為 ReplSet 加入其他次要的資料庫伺服器：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;rs.add(&quot;192.168.11.2:27017&quot;)
rs.add(&quot;192.168.11.3:27017&quot;)
rs.add(&quot;192.168.11.4:27017&quot;)
rs.add(&quot;192.168.11.5:27017&quot;)&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
這邊要注意一點，如果你的 replSet 中只有兩台 MongoDB （包括 Primary 自己），單單這樣設定，會發生無法判定 Primary 該給誰的問題。因為 MongoDB 的機制，是讓眾伺服器投票決定誰當 Primary，但經測試發現，一個 RelpSet 中只有兩台機器時，常會發生問題，造成整個群組中無 Primary 機器的狀態。所以，這時要加入仲裁者(Arbiter)機器來協助排除。&lt;br /&gt;
&lt;br /&gt;
Arbiter 設定很簡單，你要先準備第三台機器，裝上 MongoDB 和設定 /etc/mongodb.conf 裡的 replSet，接著在 Primary 主機上把這台機器加入到 Arbiter 伺服器清單：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;rs.addArb(&quot;192.168.11.100:27017&quot;)&lt;/pre&gt;&lt;/div&gt;註：Arbiter 因為不處理資料，所以負載並不高，理論上也可以和原本的資料庫放在同一台機器上，但請注意，如果你要這樣做，需要另外啟動第二個 mongod 服務，並跑在不同的 Port。由於這樣的做法，在 Debian 上沒有標準安裝步驟，就不在本文詳述。此外，如果你只有兩台資料庫主機，記得兩台上面都要架設 Arbiter，這樣，當其中一台當機時，replSet 中還有一個 Arbiter 來決定 Primary 該給誰，不然也會發生整個群組中無 Primary 機器的問題，哪怕只剩一台機器還活著，MongoDB 依然憂柔寡斷的不知道該不該篡位。&lt;br /&gt;
&lt;br /&gt;
最後一步，在其他主機上都將 /etc/mongodb.conf 設定好，並進入 mongo 命令模式初使化，MongoDB 會自動同步 replSet 的設定，然後會把 Primary 上的資料同步並複製過來：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;rs.initiate(null)&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
如果一切沒問題，你可以在 Primary 主機上，進入 mongo 命令模式，然後使用指令看整個 replSet 的狀態：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;rs.status()&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
&lt;b&gt;後記&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
Replication 設定好後，只要 Secondary 的機器一上線，就會開始同步資料，如果 Primary 主機死掉，也會自動選定一台 Secondary 機器，讓它變成新的 Primary。&lt;br /&gt;
&lt;br /&gt;
過程中，主要可能發生的問題是整個 replSet 選不出 Primary 的狀況，而且在無 Primary 機器時候，你無法去修改並設定 replSet ，造成整個系統不能正常運作。由於 MongoDB 沒有提供強制清除 replSet 設定的功能（至少到目前為止我沒有找到），一個非正式的方法是使用 mongo 去將每一台裡名為『local』的 db 砍掉，然後重啟 Service 並重新設定 replSet 。</description><link>https://fred-zone.blogspot.com/2012/09/mongodb-replication.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>4</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-1532772336575856927</guid><pubDate>Sat, 01 Sep 2012 23:11:00 +0000</pubDate><atom:updated>2012-09-02T07:11:57.098+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Cloud</category><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">MongoDB</category><category domain="http://www.blogger.com/atom/ns#">Node.js</category><category domain="http://www.blogger.com/atom/ns#">NodeJS</category><category domain="http://www.blogger.com/atom/ns#">Web 相關技術</category><category domain="http://www.blogger.com/atom/ns#">技術新知</category><category domain="http://www.blogger.com/atom/ns#">自由軟體</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><category domain="http://www.blogger.com/atom/ns#">雲端(Cloud)</category><title>夢想偉大，但步伐短小的 DBHouse</title><description>數個月前開始做一個計劃『AppHouse』，實作如 Google App Engine(GAE) 般的 PaaS，其志在打造自己的 Node.js 雲端軟體平台。然後發現，除了讓雲端服務可以在平台上跑起來外，資料庫管理也必需有個便於使用的機制和規劃，仔細想想，一個沒有資料庫配合的雲端服務，可沒有什麼太大的價值，於是，『DBHouse』便應運而生。&lt;br /&gt;
&lt;br /&gt;
你可以在 github 上找到這個專案：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;https://github.com/Mandice/node-dbhouse&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
DBHouse 起初的開發目的，是讓使用 AppHouse 架設以及開發自己雲端服務的人，可以很容易存取資料庫。此外，對我們而言更便於管理資料庫資源，面對許多不同的服務，不需要特別為他們開設資料庫權限，亦或是買許多硬體和主機，建立起許多 VM 並做各種安全性規劃。其實，如果把 DBHouse 的用途，想像成 Google 在做的事，就很容易明白：『在 GAE 上你可以使用統一的 Database APIs，存取 Google 提供的資料庫系統(BigTable)。』，同理，我們也是在做同樣的事。&lt;br /&gt;
&lt;br /&gt;
只不過，學 Google 開發自己的一套資料庫太過於困難，不是一個可以達成的目標，所以我們仍然選用 MongoDB 當做 PaaS 的資料庫底層。僅管資料庫不是自己開發的，我們還是可以提供統一的 API，讓開發者存取。統一的 API 有個好處，若能做到當開發者在使用的時候，不需要知道自己在使用什麼資料庫，日後就可以在這 API 之後串接或替換不同的資料庫系統，有很大的彈性可以擴充。&lt;br /&gt;
&lt;br /&gt;
當然，更遠大的目標是希望在一個 Table(Collection) 內，因應不同的欄位需求，而交由不同資料庫處理，更進一步發揮不同資料庫的特色。但是，這夢想遠大，技術上也有很多盲點待討論，所以能不能實現那是另外一回事，至少，短期內在我們的能力範圍和經濟狀況下，暫時無法達成這一步。&lt;br /&gt;
&lt;br /&gt;
雖然 DBHouse 有這樣的初衷和夢一般的計劃，但不代表 DBHouse 一定得和 AppHouse 配合使用，更準確的說，他們本來就是獨立各自發展的專案，各自可獨立運作。說穿了，DBHouse 本身就只是一個 Database API，你可以在 Node.js 裡使用 DBHouse API 去操作自己的 MongoDB（目前只有支援 MongoDB，其他驅動還待開發），也提供了一套 ORM 供開發者使用。&lt;br /&gt;
&lt;br /&gt;
目前 DBHouse 的特色：&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;支援簡易的 ORM （包括：多層資料結構）&lt;/li&gt;
&lt;li&gt;支援 MongoDB v1.6+&lt;/li&gt;
&lt;li&gt;已實作驅動層以便支援其他資料庫&lt;/li&gt;
&lt;li&gt;盡可能貼近 SQL 語法習慣（如：select、insert、update、delete、replace）&lt;/li&gt;
&lt;li&gt;以 Mongo Query Language 為主要查詢語言&lt;/li&gt;
&lt;li&gt;根據 ORM 設定，自動轉換欄位類型（如：讀寫 MongoDB 時，自動轉換 BinObject）&lt;br /&gt;&lt;i&gt;ex, 讀出 UUID 時轉成字串，以便 JavaScript 操作或網頁間傳遞，存入或查詢時，自動轉成 MongoDB 的 BinObject&lt;/i&gt;&lt;br /&gt;
&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
目前仍缺少的功能：&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;Cursor&lt;/li&gt;
&lt;li&gt;索引(Index)管理&lt;/li&gt;
&lt;li&gt;完整的文件&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
使用 NPM 安裝：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;npm install dbhouse&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
展示如何使用 DBHouse 新增一筆資料：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;var DBHouse = require(&#39;dbhouse&#39;);

var dbHouse = new DBHouse;

/* Define schema */
var Address = new DBHouse.Schema({
        company: { type: &#39;String&#39; },
        home: { type: &#39;String&#39; },
        updated_time: { type: &#39;Date&#39; }
});

var Contact = new DBHouse.Schema({
        _id: { type: &#39;UUID&#39; },
        name: { type: &#39;String&#39; },
        email: { type: &#39;String&#39; },
        tel: { type: &#39;String&#39; },
        created: { type: &#39;Date&#39; },
        address: { type: &#39;Schema&#39;, schema: Address }
});

/* Create connection with database server */
dbHouse.connect(&#39;mongodb&#39;, { host: &#39;localhost&#39;, port: 27017 }, function() {

        /* Create a database operator */
        var db = new DBHouse.Database(dbHouse);
        db.open(&#39;dbhouse&#39;)
                .collection(&#39;contact&#39;)
                .model(Contact)
                .insert({
                        name: &#39;Fred Chien&#39;,
                        email: &#39;cfsghost@gmail.com&#39;,
                        tel: &#39;0926123456&#39;,
                        created: new Date().getTime(),
                        address: {
                                company: &#39;Taipei&#39;,
                                home: &#39;Taiwan&#39;,
                                updated_time: new Date().getTime()
                        }
                }, function(err, data) {
                        if (err)
                                throw err;

                        console.log(data);
                });
});&lt;/pre&gt;&lt;/div&gt;註：『collection()』可使用『table()』替換，兩者功能一樣，只是為了同時符合 SQL 和 NoSQL 的習慣。&lt;br /&gt;
&lt;br /&gt;
如果只是想做簡單的資料庫操作，也可以不使用 ORM 機制：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;var DBHouse = require(&#39;dbhouse&#39;);

/* Create connection with database server */
var dbHouse = new DBHouse;
dbHouse.connect(&#39;mongodb&#39;, { host: &#39;localhost&#39;, port: 27017 }, function() {

        /* Create a database operator */
        var db = new DBHouse.Database(dbHouse);
        db.open(&#39;dbhouse&#39;)
                .collection(&#39;contact&#39;)
                .select({
                        email: 0
                })
                .where({
                        &#39;$or&#39;: [ { name: &#39;Fred Chien&#39;}, { name: &#39;Stacy Li&#39; } ]
                })
                .limit(1)
                .query(function(err, data) {
                        if (err)
                                throw err;

                        console.log(data);
                });
});&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
如果你使用過 MongoDB ，也熟悉傳統 SQL 資料庫，你一定會發現兩者對於 update 的定義相當不一樣，因為在 MongoDB 中預設方法是直接取代掉整筆資料，而傳統 SQL 中只是修改欄位而已。但是在 DBHouse 中，對 update 的處理是採用 SQL 的動作，也就是修改欄位，若是要整筆取代，則另外使用 replace()。&lt;br /&gt;
&lt;br /&gt;
修改 tel 欄位的資料：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;var DBHouse = require(&#39;dbhouse&#39;);

/* Create connection with database server */
var dbHouse = new DBHouse;
dbHouse.connect(&#39;mongodb&#39;, { host: &#39;localhost&#39;, port: 27017 }, function() {

        /* Create a database operator */
        var db = new DBHouse.Database(dbHouse);
        db.open(&#39;dbhouse&#39;)
                .collection(&#39;contact&#39;)
                .where({
                        name: &#39;Fred Chien&#39;
                })
                .update({ tel: &#39;0926333572&#39; }, function(err) {
                        if (err)
                                throw err;
                });
});&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
既然 Mongo Query Language 是特色，當然可以吃特殊命令，以下程式將把 reviewed 欄位值加一：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;var DBHouse = require(&#39;dbhouse&#39;);

/* Create connection with database server */
var dbHouse = new DBHouse;
dbHouse.connect(&#39;mongodb&#39;, { host: &#39;localhost&#39;, port: 27017 }, function() {

        /* Create a database operator */
        var db = new DBHouse.Database(dbHouse);
        db.open(&#39;dbhouse&#39;)
                .collection(&#39;contact&#39;)
                .where({
                        name: &#39;Fred Chien&#39;
                })
                .update({ $inc: { reviewed: 1 } }, function(err) {
                        if (err)
                                throw err;
                });
});&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
&lt;b&gt;後記&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
大致上來說，DBHouse 已經足夠使用，但真要挑惕的話，還是有很多細節功能待補齊。而目前開發模式，是依照我們手邊專案所需，為其添加新功能，如果遲遲沒有加入您想要的功能，還請見諒。&lt;br /&gt;
&lt;br /&gt;
此外，如果有人喜歡 DBHouse，歡迎投入開發並提交 Patch。:-)</description><link>https://fred-zone.blogspot.com/2012/09/dbhouse.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-5052233270716405590</guid><pubDate>Mon, 20 Aug 2012 12:29:00 +0000</pubDate><atom:updated>2012-08-20T20:34:15.020+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">Node.js</category><category domain="http://www.blogger.com/atom/ns#">Qt</category><category domain="http://www.blogger.com/atom/ns#">心得分享、講座</category><category domain="http://www.blogger.com/atom/ns#">有趣新構思</category><category domain="http://www.blogger.com/atom/ns#">系統研發手札</category><category domain="http://www.blogger.com/atom/ns#">自由軟體</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><title>【COSCUP 2012】閉幕！簡報釋出！</title><description>國內一年一度最大的開放原始碼（Open Source）活動，再次順利落幕。很多人都意猶未盡，所以每當活動結束，便會開始思考明年要分享什麼、反省今年哪裡做的不夠，有人會在活動過後的連續幾天，電腦上開著簡報編輯器，盯著空白的內容，靜靜的發著呆。可惜的是，最後總是因為沒有任何想法而放棄。一直到了明年 COSCUP 的徵稿消息發出，一堆人又開始拾起空白了近一年的想法，擠出一個題目，逼著自己在一兩個月內完成，上台發表。就如同學校的期中、期末考，沒有死到臨頭，大多數同學都沒有唸書的動力，也不會激發自己 120% 的腦力和體力。所以，Event-Driven 的開發模式，一直是在很多開發者促使自己進步的方式。&lt;br /&gt;
&lt;br /&gt;
別以為兩個月時間很充足，大家都有外務、有工作、要過生活，所剩的時間實在有限，所以每當到了 COSCUP 的前夕，總是會聽到很多講者在拼命趕工的聲音。其實，這樣都還算是常態，頂多拼命一點，少睡一點，都還可以頂得過去。可是，今年一次給『兩個 Talk』，就真的讓人會虛脫到不行，還好，熱情能戰勝一切。:-)&lt;br /&gt;
&lt;br /&gt;
由於今年活動承辦的分工方式比較特殊，所以被選上了兩個稿，分別是：&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;JavaScript 全面逆襲！使用 Node.js 打造桌面環境！&lt;/li&gt;
&lt;li&gt;用最潮的 JavaScript 盡情開發 KDE/Qt 程式&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
前者是總結這一年來，我們團隊所開發出來的各種 Node.js APIs，並整合成為一個如同 GNOME/KDE 的桌面環境（Desktop Environment），並展示使用 JavaScript 開發出來的 Juice Desktop Environment（果汁桌面環境）。後者則是為符合 KDE 的議程方向，探討使用 Node.js 開發 Qt 程式的可能。&lt;br /&gt;
&lt;br /&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;&lt;iframe allowfullscreen=&quot;allowfullscreen&quot; frameborder=&quot;0&quot; height=&quot;356&quot; marginheight=&quot;0&quot; marginwidth=&quot;0&quot; scrolling=&quot;no&quot; src=&quot;http://www.slideshare.net/slideshow/embed_code/14005090?rel=0&quot; style=&quot;border-width: 1px 1px 0; border: 1px solid #CCC; margin-bottom: 5px;&quot; width=&quot;427&quot;&gt; &lt;/iframe&gt;&lt;br /&gt;
&lt;/div&gt;&lt;br /&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;&lt;iframe allowfullscreen=&quot;allowfullscreen&quot; frameborder=&quot;0&quot; height=&quot;356&quot; marginheight=&quot;0&quot; marginwidth=&quot;0&quot; scrolling=&quot;no&quot; src=&quot;http://www.slideshare.net/slideshow/embed_code/14011306?rel=0&quot; style=&quot;border-width: 1px 1px 0; border: 1px solid #CCC; margin-bottom: 5px;&quot; width=&quot;427&quot;&gt; &lt;/iframe&gt;&lt;br /&gt;
&lt;/div&gt;&lt;br /&gt;
&lt;b&gt;後記&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
退學不是件好的事情，請勿模仿。:-S</description><link>https://fred-zone.blogspot.com/2012/08/coscup-2012.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>4</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-8683431747603060382</guid><pubDate>Fri, 03 Aug 2012 03:21:00 +0000</pubDate><atom:updated>2012-08-03T11:24:46.936+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Express Web Framwork</category><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">NodeJS</category><title>一起來用 Courser 吧！管理 Route 的利器！</title><description>跳脫出前端應用之後的 JavaScript 語言，所遭遇的挑戰極大，至今還沒有一個非常好的開發模型和原則，讓人不免抱怨。因為沒有前人可學習，也沒有太多文獻可以參考，所以，為了讓自己能夠更快完全領會 JavaScript 在各種極端環境之下的開發情況，在這一年來，花了相當多的時間投入去做各種不同類型的 Project，從 Web service、Embedded System、System Programming 到各式雲端技術開發，嘗試從中累積更多用 JavaScript 打天下的經驗。唉，慣 C 都不慣 C 了。&lt;br /&gt;
&lt;br /&gt;
不過，做了許多東西後，就會開始有機會重覆開發相同的功能，這時就會希望能重覆利用舊的程式碼，讓同一段程式，不用每次都重寫一次而浪費時間。於是，開始慢慢把之前做過的專案整理，然後整併許多功能，獨立出來變成一個個 Node.js 模組。&lt;br /&gt;
&lt;br /&gt;
Courser 就是這樣被獨立出來的專案，而功能是為&amp;nbsp;Express Web Framework 提供一個管理 HTTP Routing 的方案。你可以從 github 上找到專案首頁：&lt;br /&gt;
&lt;br /&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;https://github.com/cfsghost/courser&quot;&gt;https://github.com/cfsghost/courser&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
Express 範例的傳統做法，是每次增加一個 API 或頁面，就要更動一次主程式檔案(app.js)，以管理 HTTP Routing，相當麻煩，尤其當有許多人協同開發時，更多不必要的程式碼衝突便出現了。此外，管理專案中的 Handler 也是件麻煩事，因為我們總不可能將所有的 Handler 寫在同一支檔案裡，必須適當分類分檔，才能便於維護。隨著專案規模越來越大，需求越來越複雜，這便是件苦差事。&lt;br /&gt;
&lt;br /&gt;
這些問題當然有解決方案，Express Source Code 裡的範例，就提供了幾種不同的方法，供開發者參考，只不過，這些方法大部份很簡陋、不通用，多半還是要靠專案開發者自行想辦法，開發一套機制，才能解決需求。因此，之前為了專案的需要，就實作了 Courser 去管理專案下的 HTTP Routing 和所有的 Handler，嘗試讓 routes 不要每次開發出來，都是這麼醜。&lt;br /&gt;
&lt;br /&gt;
Courser 目前已經釋出 v0.0.2，你可以從 NPM 上直接安裝它：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;npm install courser&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
&lt;br /&gt;
假設你已經會使用 Express Web Framework，你可以直接引入 Courser，並設定 HTTP Routing 的擺放位置：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;var express = require(&#39;express&#39;);
var Courser = require(&#39;courser&#39;);

var app = express.createServer();

// Loading and Initializing Routes from specific path
var courser = new Courser(app);
courser.addPath(__dirname + &#39;/routes&#39;);
courser.init(function() {
&amp;nbsp; &amp;nbsp; app.listen(3000);
});
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Courser 會載入所有指定目路底下的 *.js 檔案，所以在 routes 的目錄下，我們直接建立新檔（如： test.js），然後這樣定義 Routing 和 Handler：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;module.exports = {
&amp;nbsp; &#39;/&#39;: index,
&amp;nbsp; &#39;/hello&#39;: hello
};

function index(req, res) {
&amp;nbsp; &amp;nbsp; res.end(&#39;HOME&#39;);
};

function hello(req, res, next) {
&amp;nbsp; &amp;nbsp; res.end(&#39;HELLO&#39;);
};
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
你可能不喜歡這樣的型式，喜歡自己針對不同的 HTTP Method 定義不同的 Handler：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;module.exports = {
&amp;nbsp; &amp;nbsp; &#39;/method&#39;: {
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; get: get,
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; post: post
&amp;nbsp; &amp;nbsp; }
};

function get(req, res) {
&amp;nbsp; &amp;nbsp; res.end(&#39;GET Method&#39;);
};

function post(req, res) {
&amp;nbsp; &amp;nbsp; res.end(&#39;POST Method&#39;);
};&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
要使用 Express Web Framework 的 Middleware 機制，從中安插多個 callback function，也可以這樣寫（用 Array 表示）：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;module.exports = {
&amp;nbsp; &amp;nbsp; &#39;/middleware&#39;: [
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; DoSomethingBeforeHandler,
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; middleware
&amp;nbsp; &amp;nbsp; ]
};

function DoSomethingBeforeHandler(req, res, next) {
&amp;nbsp; &amp;nbsp; console.log(&#39;Do Something&#39;);
&amp;nbsp; &amp;nbsp; next();
};

function middleware(req, res) {
&amp;nbsp; &amp;nbsp; res.render(&#39;index&#39;, { title: &#39;Courser Example&#39; });
};
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
&lt;b&gt;後記&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
如果日後能加上 auto-reload 和即時抽換 Handler 的功能，就更省事了！</description><link>https://fred-zone.blogspot.com/2012/08/courser-route.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>4</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-3269829840012820711</guid><pubDate>Sun, 15 Jul 2012 19:11:00 +0000</pubDate><atom:updated>2012-07-16T03:11:05.428+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Clutter</category><category domain="http://www.blogger.com/atom/ns#">Desktop</category><category domain="http://www.blogger.com/atom/ns#">GNOME</category><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">NodeJS</category><category domain="http://www.blogger.com/atom/ns#">Web 相關技術</category><category domain="http://www.blogger.com/atom/ns#">X11</category><category domain="http://www.blogger.com/atom/ns#">心得分享、講座</category><category domain="http://www.blogger.com/atom/ns#">自由軟體</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><title>GNOME.Asia Summit 2012 香港遊之簡報釋出</title><description>最近因為忙錄，遲遲未寫下今年六月初飛去香港(Hong Kong)參加 GNOME.Asia 2012 的心得，以及將當時在活動中的簡報檔釋出。:-)&lt;br /&gt;
&lt;br /&gt;
對一個台灣來的人而言，香港不會讓人感到陌生，若不說新發展的地方，走在街頭，讓人有走在台北西門町的感覺，或是像台灣一些繁華的老市區以及夜市，站在路中央看過去，當下可以感受到早期台灣和香港之間的微妙關係，其共同點難以言喻。事實上，除了廣東話，還有那些銅板和五花八門的紙鈔外，沒有什麼會讓台灣人不習慣的地方。&lt;br /&gt;
&lt;br /&gt;
話說回來，今年的 GNOME.Asia 2012 是在香港城市大學舉行，據當地朋友所說，這是一個與大型商場（百貨公司）纏綿的學校，由於學校、商場和交通樞紐的動線很密切，走在裡面，你會感覺到商場就像學校的一部份，走沒有幾步路，就會走入了商人的魔掌，錯進了名牌服飾的專櫃。&lt;br /&gt;
&lt;br /&gt;
不過，身為一個宅宅開發者，當然不會被這些名牌服飾所迷惑，它們是嚇不倒我的！活動，當然才更重要！&lt;br /&gt;
&lt;br /&gt;
我能了解，很多人在今天看到 GNOME 幾個大字就興趣缺缺，GNOME 讓大家又愛又恨，許多人不喜歡 GNOME 3 將桌面大改，像是 Linux 之父 Linus Torvalds 前些日子跳出來劈天蓋地的砲轟；也有很多人（像是我）對於許多元件被改壞或是效能不彰而感到很不悅。但由於歷史因素，太多人無法逃脫出 GNOME 所設計出來的大框架，就算你不想用 GNOME 的桌面環境，在 Linux 等 Unix-like 系統中，你還是免不了要去使用基於 GTK+/GLib 所設計的軟體，可以說 GNOME 無處不在，已經不是單單『桌面環境』一詞可以概括。&lt;br /&gt;
&lt;br /&gt;
喜不喜歡新一代的 GNOME 桌面環境是其次，對於我而言， GNOME 是一座作業系統『操作機制』的兵工廠，他生產了太多元件讓我們可以自由使用，然後提供方法讓使用者和開發者可以用『人的角度』去控制作業系統。所以，如果你真的不滿意新的 GNOME 圖形介面設計，自己將 UI 打掉重練也不會是件難事。&lt;br /&gt;
&lt;br /&gt;
這次去香港參加活動，見到了不同地方的開發者和使用者，尤以『中文輸入法（包括輸入框架）』這個議題特別引起討論。這些同是非英語系的 CJK 開發者和使用者們，有頗多的交流。雖然有些議程全程是以廣東話進行討論，我完全聽不懂，但好在有些坐旁邊的香港朋友自動自發充當即時翻譯，還順便介紹了許多當地輸入法開發者的來歷，讓我也能聽懂他們在討論什麼東西。（由此可見中文這語言多難，雖然都是用中文字，但你頂多只能看的懂，卻聽不懂）&lt;br /&gt;
&lt;br /&gt;
至於我，在這次活動中，介紹了以 Node.js 開發現代桌面程式的解決方案，以及我正在做的開放原始碼專案『jsdx-toolkit』。簡報也在此公開供各界取用：&lt;br /&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;
&lt;object height=&quot;355&quot; id=&quot;__sse13647187&quot; width=&quot;425&quot;&gt;&lt;param name=&quot;movie&quot; value=&quot;http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=enjoywritingmoderndesktopapplicationinjavascript-120715140051-phpapp02&amp;stripped_title=enjoy-writing-modern-desktop-application-in-java-script&amp;userName=cfsghost&quot; /&gt;&lt;param name=&quot;allowFullScreen&quot; value=&quot;true&quot;/&gt;&lt;param name=&quot;allowScriptAccess&quot; value=&quot;always&quot;/&gt;&lt;param name=&quot;wmode&quot; value=&quot;transparent&quot;/&gt;&lt;embed name=&quot;__sse13647187&quot; src=&quot;http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=enjoywritingmoderndesktopapplicationinjavascript-120715140051-phpapp02&amp;stripped_title=enjoy-writing-modern-desktop-application-in-java-script&amp;userName=cfsghost&quot; type=&quot;application/x-shockwave-flash&quot; allowscriptaccess=&quot;always&quot; allowfullscreen=&quot;true&quot; wmode=&quot;transparent&quot; width=&quot;425&quot; height=&quot;355&quot;&gt;&lt;/embed&gt;&lt;/object&gt;&amp;nbsp;&lt;/div&gt;
&lt;br /&gt;
&lt;b&gt;後記&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
為了公事，此行我也順道從香港搭地鐵去了深圳一趟，其中通關、走老遠再通關，回程還錯搭了一班深圳當地的公車，花了好幾個小時繞了一大圈，也算是遊了深圳，但累垮了我的雙腿和屁股。&lt;br /&gt;
&lt;br /&gt;
此外，這圈子真的是很小，活動上也碰到了在 OpenSUSE Team 的開發者（前身是 Novell，OpenSUSE 現在獨立運作），由於我過去也在 Novell 待過，所以有許多共同的話題。:-)</description><link>https://fred-zone.blogspot.com/2012/07/gnomeasia-summit-2012.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-4872223780666300075</guid><pubDate>Fri, 06 Jul 2012 17:13:00 +0000</pubDate><atom:updated>2012-07-07T01:13:58.136+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">C/C++</category><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">NodeJS</category><title>Node.js v0.8 大變革？！Native Module 開發者的福音！</title><description>最近 Node.js 推出了 v0.8 穩定版，除了改善 I/O 的效能和一些 API 外，最大的變動就是跨平台開發環境的改進。包括了決定徹底使用 GYP 取代 node-waf(wscript)、宣布 libev 將死。或許，這些變動這對大部份的 Node.js 使用者來說不是什麼問題，但對於我們這些寫 Native API 和底層的開發者來說，關係到自己程式能不能跑在未來 Node.js 的新版本上，而且產生了很多移植性的工作。但是整體來說，好處其實大於壞處，因為以後我們的程式更容易跨平台了。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;轉用 GYP&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
GYP 是 Google 發展出來的 Build script tools，就像是傳統 Autotools、Makefile 或 CMake 這類的工具。GYP 最早被應用在廣為人知的 Google Chrome/Chromium Project ，是個跨平台的解決方案，這也是為什麼 Node.js 決定要採用的主要原因。&lt;br /&gt;
&lt;br /&gt;
在以前，不同的平台上，你需要為 C/C++ Addon 製作不同的 Build script，沒有統一的方法和途徑去設置和編譯程式，這讓跨平台的工作非常麻煩。使用 GYP 後，可以大幅減少這樣的問題。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;棄 libev ，轉用 libuv&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
首先，有一點要注意，Node.js 開發者打算在下一個穩定版（應該是 v1.0）拿掉 libev 的支援，所以請所有的開發者盡快轉用 libuv。&lt;br /&gt;
&lt;br /&gt;
libev 是 Node.js 在非 Windows 平台上所使用的 Event loop 解決方案，讓 C/C++ 開發者可以開發 Event-driven 的程式，除了可以用來監聽 FileSystem 等用途，也更容易整合 Thread 進 Event-driven 的設計模式中，要是你有印象，GLib 其實也是類似的解決方案之一，只不過 libev 更為輕量。不過， libev 主要還是被應用在 Unix-like 的系統上，若 Node.js 要支援多個平台，在其他系統上，就必需尋求別的解決方案，如 Windows 的 IOCP。&lt;br /&gt;
&lt;br /&gt;
而過去這樣的情況，對我們 Node.js Native Module 開發者來說，是個莫大的挑戰，你必需在不同系統上，採用不同的寫法，導致開發上很困難。所以 Node.js 自行開發了 libuv，包裝了 libev 和 IOCP 等機制，讓所有的開發者，只要統一使用 libuv API 即可，不用再管跨平台的問題。&lt;br /&gt;
&lt;br /&gt;
換句話說，如果你直接使用 libuv，在 Linux 等 Unix-like 的系統上，其底層依然還是使用 libev，如果你是在 Windows 上，則底層會使用 IOCP。所以，與其說是棄 libev，其實是避免開發者直接使用它。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;libuv 直接支援 Thread 操作&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
在 Node.js v0.6 以前，libuv 還並不支援直接的 Thread 操作，若你要達成這樣的需求，只有使用 uv_queue_work() 去模擬，不然就是要使用作業系統原生的 Thread API。不過，若直接使用作業系統原生的 Thread API，也同樣有跨平台的問題存在，所以 libuv 在新版中已經提供 Thread 的包裝 uv_thread_t，也提供相關的 API，讓開發者使用。</description><link>https://fred-zone.blogspot.com/2012/07/nodejs-v08-native-module.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>1</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-74759410999607178</guid><pubDate>Fri, 22 Jun 2012 20:38:00 +0000</pubDate><atom:updated>2012-06-24T06:38:48.003+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">商業</category><category domain="http://www.blogger.com/atom/ns#">心情筆記</category><title>現實中，我的創業，問題，解答，手札。</title><description>最近看到一些網友轉貼的文章，還有聽聞了一些朋友的創業故事，加上自己也是在創業，因此有感而發。其實，過去就一直想寫下『在我眼中的創業』，一方面重新整理自己在創業生涯中的思緒，另一方面鼓勵自己不能倒下。此外，如果可以，讓更多人知道創業的現實面，也不是什麼壞事。這邊要先感謝許多同是創業人的朋友，無論他們現在是否還在這條路上，都給予我很多幫助和啟發，更感激許多親身跳下去『證明』的人，讓我知曉許多悲慘的事實。&lt;br /&gt;
&lt;br /&gt;
依然強調，我尚未成功，也還年輕，我說的話可能影響不了什麼人，也沒什麼說服力，只是單純地將我所見的一切，如實的描述出來。如果你對我所說的一切不以為然，如果你仍然覺得你自己原本想法是對的，請堅持自己所想，無論最後是成功還是失敗，開心或痛苦的，你都能因此得到實質的經驗和正面的回報。&lt;br /&gt;
&lt;br /&gt;
因為我還沒成功，所以我不會告訴你怎麼成功，更不會告訴你創業一定要做到的成功守則有哪些，這些事我都沒有答案。如果你有這樣的困擾，你應該去問的是那些研究創業的部落客專家，或是書報周刊。在這我只會告訴你，我在創業中遭遇到或看到的問題。&lt;br /&gt;
&lt;br /&gt;
首先，恭喜自己。經歷過兩年的努力，好不容易還活著，這是我是創業至今最值得開心的事。相信我，這是真的，在經歷過痛苦磨難、倒閉危機和理想於現實搏鬥後，你會非常慶幸，自己還撐的下去。這一切不像是某些知名的『談創業』部落格文章中，所輕描淡寫的這麼簡單，也不像書店架上滿滿的『各類成功學』所提到的這麼有跡可循，因為你永遠可能被任何一個創業的細節所打倒，一切是這麼出乎意料，現實中的『啊！你已經死了！』天天都可能上演。所以，如果你克服了這些細節，不管落地動作好看與否，你都應該先為自己鼓掌。&lt;br /&gt;
&lt;br /&gt;
&lt;h4&gt;      &lt;b&gt;&lt;span style=&quot;font-size: large;&quot;&gt;問一下自己，什麼才叫做『成功』？&lt;/span&gt;&lt;/b&gt;&lt;/h4&gt;既然提創業，就得提大家都想要成功這件事。包括我自己，很多人不曉得&lt;b&gt;創業最中最大的問題，就是『不知道什麼才是成功』&lt;/b&gt;。對許多創業人來說，看到 Facebook 有今天的地位會感覺到相當興奮，為自己『創業的理想』注入了一劑莫大的強心針。是的，因為你認為他是成功的，所以你感覺到了『我也有機會成功！』，夢想實現也近在眼前。&lt;br /&gt;
&lt;br /&gt;
但是，我們應該先問一下自己，什麼才叫做『成功』？&lt;br /&gt;
&lt;br /&gt;
是上市上櫃？手上握有幾百億美元資產？還是不缺錢用？握有極大的權力？又或者是走到世界各地，都能有人認出你來？如果你回答不出來這個問題，那請試著去想個更簡單的問題，你認為 Facebook 算成功嗎？你覺得他從什麼時候開始算是成功？&lt;br /&gt;
&lt;br /&gt;
我相信有各種不同的回答，創業模式也有百百種，絕對沒有肯定的答案。不過，如果你才剛開始創業，就想模仿 Facebook，開始思索自己如何佔五成以上股份，開始幻想著剩下的四成如何分給其他人，甚至說的好像你就是現在的 Facebook 一般。那麼，你心中所認為的成功，應該是止於『拿到一大筆投資人的錢』。&lt;br /&gt;
&lt;br /&gt;
有太多太多人，看到了許多新創公司的成功故事，開始誤以為拿到一筆大錢，就是成功了，然後未來都將很順利。所以一堆人為了自己的 Idea 做了很多表面功夫，也練就了世界上僅次於 Steve Jobs 的說故事功力，更畫了多到了世界末日也吃不完的大餅。其目的不外乎就是想得到創投的親睞，期望這些『金主』們倒一些錢到自己的頭上。&lt;br /&gt;
&lt;br /&gt;
先不論這樣的討好的手段是對是錯，也不論你花了多少時間研究，並在過程中花了多少精力投入在『真正的核心價值』上。因為更多人沒想到的是，當幸運的拿到錢後，才是災難的開始。&lt;br /&gt;
&lt;br /&gt;
很多人心中對『創業』，都有一個美好幻想，至於敢不敢說出來或承不承認，那就是另一件事了：『公司開了，然後資金充足了，公司就能夠自己賺錢，然後自己生蛋，我可以安心掛著上流社會的 CEO(CXO) 王子名號，當個人生勝利組』&lt;br /&gt;
&lt;br /&gt;
沒錯，很多人拿到資金後，覺得成功了，便開始揮霍，租大辦公室、搞氣派，左邊有機會去左邊，右邊有機會去右邊，兩邊都有機會兩邊都去搞搞，已經忘記了自己的初衷。直到有一天，錢燒光了，收攤時才懊悔。&lt;br /&gt;
&lt;br /&gt;
比較誇張的，就會說到多個創始人為了分錢，爭個沒完，然後拆夥。&lt;br /&gt;
&lt;br /&gt;
而有些人，拿到投資人的錢後才發現，想一個怪點子容易，但經營這個點子才是最困難的。談到點子要怎麼生雞蛋，畫畫大餅很容易，但實際做下去，卻一步也走不動，有時是業務面走不動，有時是技術面跟不上。熬呀熬呀，等到錢燒光的時候還走不出自己的路時，一樣打包收攤。&lt;br /&gt;
&lt;br /&gt;
這邊還沒提到，當路快走到盡頭時，狗急跳牆花招百出的盛況，會有多麼精彩。（這留著以後出外傳 :-P）&lt;br /&gt;
&lt;br /&gt;
講到這裡，我想你會開始檢討，自己究竟花了多少時間在核心價值上？還是你只是求一筆注定要燒光的錢？&lt;br /&gt;
&lt;br /&gt;
你應該也發現了，那些知名創業部落格根本不會告訴你這些事，那些創業和成功學的書籍，永遠也只是講他們自己成功的那一面。至於細節呢？等你照著做後，倒了才會知道自己哪做錯了。&lt;br /&gt;
&lt;br /&gt;
所以，你看過多少這方面的書？多少網路上的相關文章？你以看過這些書為傲嗎？或覺得自己能因此掌握創業嗎？從許多人身上你可以發現，戰戰兢兢創業或努力一個洞挖到底的人，反而在這條路上失誤比較少，比較有成功的機會。我當然不是說讀書不好，可是如果讀了這些『聖經』，反而讓你『大意失金舟』，那你還不如不要讀了。&lt;br /&gt;
&lt;br /&gt;
還不如先想想，自己想要的成功是什麼？&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;&lt;span style=&quot;font-size: large;&quot;&gt;沒有成功的經驗？所以盡可能勤能補拙&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
知道自己要的成功是什麼後，大多數創業人缺少的是『成功的經驗』，我也不例外。若有成功經驗，你會更能判斷情勢，做出正確決定，也可以避免不必要的開銷。&lt;b&gt;但事實是，我們並沒有這樣的成功經驗。&lt;/b&gt;那麼，磨練自己的決策，修正自己的想法便時時刻刻不會少。&lt;br /&gt;
&lt;br /&gt;
所以，如果有著遠大的理想，而且有奮戰到底的心理準備，那你絕對不能放棄的是『不停強化核心價值』。核心價值一詞相當籠統，實際上就是『做盡一切打算』、『堅守領先位置』，想著有什麼機會、怎麼賺錢、怎麼發展、怎麼佈局、怎麼樣讓別人趕不上我們，並且實地並拼命去做。&lt;br /&gt;
&lt;br /&gt;
是的，你發現我在講冠冕堂皇的廢話了，小學生都會講的廢話。可是就是這樣的簡單的事，一個財迷心竅的創業人往往做不到。&lt;br /&gt;
&lt;br /&gt;
聰明人很多，每個人都有自己獨到的點子。然而，有些人以為自己的點子很獨特，認為自己只要做出來一定可以成功，根本無需多餘的努力。可是就因為實際上沒有成功經驗，所以這些人原始的想法往往經不起考驗；如果又不多做努力，在創業戰場上這樣死的人，可以說滿地都是。這就是為什麼要『強化核心價值』的原因，有做過努力，你的想法會進化，才會更經得住考驗。有努力或許不會成功，但至少可以降低失敗的機會。&lt;br /&gt;
&lt;br /&gt;
當然，這邊就不多重提前面所說，有人做盡一切是為了吸引投資客目光，強化的不是核心價值，而是『投資客討好工作』。雖然勤勞的方向可能不太正當，但就勤能補拙的角度來看，也總是比人多一些機會。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;&lt;span style=&quot;font-size: large;&quot;&gt;創業好像彩券？&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
周遭有個朋友的故事，在某次私底下閒聊時，也一五一時招了他自己的血淚史。&lt;br /&gt;
&lt;br /&gt;
他和幾個朋友自學生時代就會做做網站程式，後來也會寫寫目前當紅的手機 App，並自認有許多新奇的點子。然後覺得『只要做出來』就能找到『金主』給錢，只要能找到錢，接著就能變成 Angry Bird，然後可以賣周邊商品&amp;nbsp;。&lt;br /&gt;
&lt;br /&gt;
的確，他很幸運，點子還不錯，App 寫出來了，也找到投資人給了錢，但最後他當然卻沒變成 Angry Bird，而是錢燒光打烊，更別說有出什麼周邊商品。過程中，他只是不斷的拿著使用者數或是下載量，去找新的投資人，沒有花太多功夫對自己本來的想法不斷檢討改進。換言之，他也是糊理糊塗拿到第一桶金後，失去核心價值，不了了之的一個實例。&lt;br /&gt;
&lt;br /&gt;
他到了後期才弄懂了一件事，自己把創業當做樂透彩券了，當初以為夢到了『明牌』，找到了店家然後買了張彩券，就可以等著發財。&lt;br /&gt;
&lt;br /&gt;
事後諸葛來看 Angry Bird 的成功，並不是找一堆錢把自己充飽，更不是隨便丟個 App 放在那等三個月半年，就有大幅度成長的死忠玩家。也不是急著自己跑去做周邊商品賣商標賺錢，而是先做出絕佳的產品、經營市場，並在適當時機出擊，或交給可以發揮更大效益的人來操刀。而最重要的是，這些事，前面做不好，後面也不用做了。&lt;br /&gt;
&lt;br /&gt;
你會看到，&lt;b&gt;這一切其實都不是彩券，也沒有明牌&lt;/b&gt;，而是一步步踏出來的路。沒有人一開始就完全料到是這樣發展，都是自己慢慢開出的機會，然後時機一來就是你的。再好的想法，也要有相應的經營方法，更要不斷往前改進和擴展，就算給你搖錢樹，你也要花力氣搖它才會掉錢。&lt;br /&gt;
&lt;br /&gt;
所以，大家都聽過這麼多故事，當過無數次『諸葛』，應該都能發現，『痛苦的細節』才是不能說的秘密。不要再把自己的真心話藏起來了，想要當成大業，不是靠個一桶兩桶錢買個希望，而是你要對自己的點子負責到底。如果缺乏成功經驗，碰碰撞撞免不了，就怕你忽略了這些不起眼的碰碰撞撞，不願面對真相。那麼，這些你不願意面對的現實問題，就是擊垮整個事業的最大殺手。&lt;br /&gt;
&lt;br /&gt;
好不容易熬到有人投資，如果你以為就此解脫了，那你就真的解脫了。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;&lt;span style=&quot;font-size: large;&quot;&gt;信用破產？&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;
&lt;div&gt;&lt;br /&gt;
然後提到信用，以為投資人給你的錢是讓你隨便燒的嗎？拿到錢的人有時要自己想想，你是拿自己的信用去換錢，還是投資人真的覺得，你的東西有發展性而願意投錢？&lt;br /&gt;
&lt;br /&gt;
很多創業的是年輕人，想出來的點子或許真有點價值，但很多投資人更看重的是你『年輕』，而給你機會。這時，不要以為錢可以白燒，因為你就是以信用去換錢了，燒的太難看比燒光還慘。如果是做正確的事，錢花光了，你可以抬頭挺胸，如果你不正經的燒光了，那信用就有很大的汙點。&lt;br /&gt;
&lt;br /&gt;
此外，也有一些人，是因為能力太強或經驗豐富，所以投資人都信任他，只要他提得出點子，就給他錢去做。但這相對的，也是以信用去換取投資，也更需要僅慎。無論如何，不管你是不是以信用去換取投資，投資人給你的錢，是用來賺更多錢的，而不是進自己口袋的，不要覺得別人給你錢，你就發財了。&lt;br /&gt;
&lt;br /&gt;
你以為在投資人手上沒有黑名單嗎？有錢的投資人在業界或非業界內，人脈會不廣嗎？小心為妙吧。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;&lt;span style=&quot;font-size: large;&quot;&gt;什麼叫好的 Idea？&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
有創意、沒人做過、有錢景、有發展性...。你可以想到無數種名詞來形容好的 Idea。每個創業人手上，都會有自己的 Idea，當然，都是各自認為最好的。但有多少想法可行？多少想法能讓投資人接受，或是讓客戶買單？此外，如何避免他人來競爭？避免有財力的大財團和公司抄襲？&lt;br /&gt;
&lt;br /&gt;
在這裡，我無法評斷你的 Idea 是好是壞，所以我也無法告訴你怎樣的 Idea 是好。但既然是創業、成立公司，一個好的 Idea 一定要能夠為你帶來收入或效益（無論長期或短期，金錢還是任何種回饋），畢竟，『公司是以營利為目的』。&lt;br /&gt;
&lt;br /&gt;
我想，既然有利可圖的東西，有財力的大公司一定會想辦法進場，因為，如果唯利是圖的大公司都不想進場，這 Idea 肯定也賺不到什麼錢。那麼，如果大公司和大財團肯定會來參一腳，我們要怎麼辦？&lt;br /&gt;
&lt;br /&gt;
有人說，他想 Idea 時會去避免大公司競爭。但我覺得，不是要避免去和大公司競爭，而是&lt;b&gt;以 Operation Cost 的角度，來看待『為什麼大公司無法跟你競爭？』&lt;/b&gt;，然後找從中找出著力點。&lt;br /&gt;
&lt;br /&gt;
還記得，我有個朋友，用了一個小孩和大人的例子解釋了這件事（內容我有稍做修改，改的更為偏激了一點 :-P）：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;假設現在有一個賺錢的機會，抄一本書可以賺 1 萬元。然後抄滿 100 本，每本多給 1 萬元。此外，因為這本書比較艱澀，月薪 5 萬元的大人抄一本書的時間比較快，需要 10 個工作天（假設一個月有 30 個工作天），13 歲小孩則需要 15 個工作天。

請問，你覺得是一個月薪 5 萬元的大人，比較適合執行這任務，達成 100 本的目標，還是一個 13 歲小孩比較適合來執行這任務？（先不考慮合不合法問題）

是的，上班族抄起書來動作比較快，佔了很大的優勢，小孩抄兩本的時間，上班族可以抄三本。理論上，上班族應該更適合來執行這任務，也能更快達成目標。

但是，從成本面來看：

若抄滿 100 本書，總收入可達&amp;nbsp;2,000,000 元。

假設大人不上班，全心全意抄書，他的成本是： (10x100) x (50000 / 30)&amp;nbsp;= 1,666,666 元

假設 13 歲小孩不上學，全心全意抄書，成本是 0。（小孩根本不用自己煩惱開銷問題）

如果讓一個大人來做這件事，他只比原本收入多 333,334 元。

而對小孩而言，他則多出 2,000,000 元的收入，如果書抄不完，也還有很多收益。

這邊還沒提到期間，大人的開銷，可能需要以借貸解決，其所導致的等額外利息成本，還有可能餓死的風險成本。另外，如果沒抄到 100 本書，大人的收益根本不到 1,000,000，反而賠錢。&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
從這樣一個簡單的例子來看，若將其中『小孩』換成『新創公司』，『大人』代換成『大公司』，你就會發現，因為 Operation Cost 太高，導致很多事情大公司不可能投入，哪怕這個新事業的收益可以超過他原本的總收益，他們也只能看的到吃不到。&lt;br /&gt;
&lt;br /&gt;
所以，如果大公司真的要進入這市場，最終手段都是以買下、併購或第三方合作的方式，甚至是投資和你一樣的新創公司。當你是先驅，又領先其他人許多時，大公司們的第一順位會選擇找誰呢？&lt;br /&gt;
&lt;br /&gt;
也或許，你可能真的找到了一個沒人來搶的市場，但如果久久真的沒人來搶，請多思考一下問題在哪，因為，那可能不是藍海，而只是一灘雨後的積水。我想，真正的藍海，應該是大家都會想要來的，只是能不能來是另外一回事了。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;後記&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
創業是件很痛快的事，無論其他人支不支持你，無論投資人能不能看到你身上的光環，能在劣勢的情況下，殺下一成時，快哉！&lt;/div&gt;</description><link>https://fred-zone.blogspot.com/2012/06/blog-post_23.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>5</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-8845258714516782618</guid><pubDate>Mon, 18 Jun 2012 13:36:00 +0000</pubDate><atom:updated>2012-06-18T21:36:24.757+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">C/C++</category><category domain="http://www.blogger.com/atom/ns#">Desktop</category><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">JSDX</category><category domain="http://www.blogger.com/atom/ns#">NodeJS</category><category domain="http://www.blogger.com/atom/ns#">Window Manager</category><category domain="http://www.blogger.com/atom/ns#">X11</category><category domain="http://www.blogger.com/atom/ns#">程式心得筆記</category><category domain="http://www.blogger.com/atom/ns#">系統研發手札</category><category domain="http://www.blogger.com/atom/ns#">自由軟體</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><title>實作 X11 底下的 Popup Menu</title><description>既然要投入 JavaScript 的發展，一個很重要的目標就是讓 JavaScript 能被用來開發 Native 桌面程式。而為了達成這樣的願景，我們的團隊沒日沒夜的發展各類基礎技術和擴充底階 API，甚至是將以 JavaScript 開發整個桌面環境（Desktop Environment, 如：GNOME、KDE、LXDE 或 XFCE 這類專案）為終極目標。&lt;br /&gt;
&lt;br /&gt;
當然，跳出瀏覽器之後的 JavaScript，缺少了繪圖引擎，所以要拿來做圖型化使用者介面，會更為困難。慶幸的是，前些日子發展出的 jsdx-toolkit 已經解決了大多數的問題，除了有 3D、動畫等支援，也已經有了許多現代桌面有的UI元件（如：Entry、Label、Button... 等），以致我們完全可以放心的開發屬於自己的圖型化應用程式。&lt;br /&gt;
&lt;br /&gt;
不過，目前的情況，在手機、平板或是特定用途的嵌入式系統中完全夠用，一旦回到更為複雜的桌面環境下，就會遭遇到許多的問題。像是回到 XWindow 底下後，會遇到許多 X11 的視窗管理機制，其視窗之間錯縱複雜的交互關係，就是我們要處理的。尤其是當我們在開發桌面環境時，就會發現在一般的桌面環境下，使用者可能會同時開無數個視窗和程式，並且隨機又大量的切換使用，這迫使我們必須去修改 jsdx-toolkit 以處理 X11 底下的更多狀況，符合並更完整支援一般桌面環境下的操作習慣。&lt;br /&gt;
&lt;br /&gt;
你可能用過 GTK+/Qt 這類常見的 Toolkit，也知道在 X11 之下有很多種類型的視窗（Window），像是 Dialog、Splash、Menu、Popup Menu 等等，但你可能不知道這些現代 Toolkit 內部做了多少事。事實上，X11 本身雖定義了各類視窗的類型，但實際上 XWindow 和 Window Manager 並不會完全去照定義去處理你的視窗行為，更準確的說法是，X11 HWMH 中的基本定義和我們實際的認知是有出入的，該定義只是說希望達成的行為，但沒說是由誰（XWindow、Window Manage 還是 GUI Toolkit）去處理。&lt;br /&gt;
&lt;br /&gt;
舉例來說，我們現在寫一支程式，該程式不使用任何現代的 GUI Toolkit，然後單純使用 X11 API 把一個 Window 的類型設成 Popup Menu。但是你會發現，它的實際行為並不像我們所想的，失去焦點（Focus）後就會消失，無論你是點擊其他視窗讓他失去焦點，還是採用快速鍵讓他失去焦點。此外，他也仍然會保留 Title bar 裝飾(Decorator)，還有會在你的 Panel Taskbar 上出現一個新的 Task。就是這些種種視窗行為，和我們心中所認知的 Popup Menu 有很大的差異。&lt;br /&gt;
&lt;br /&gt;
仔細研究 X11 EWMH Spec 就會發現，對於一個 Popup Menu 的視窗，除了設定類型外，你應該要做更多屬性設定，才會讓它合乎我們預想的行為，而這些東西被分散於 Spec 文件內的各處描述，想要一次性找出來困難重重。此外，失去焦點後要關閉 Popup Menu 視窗的功能，就不僅僅是設定屬性這麼簡單，而是要使用 XGrabPointer 攔截整個畫面的輸入事件（Input event），採用特殊的做法，才能達成。&lt;br /&gt;
&lt;br /&gt;
說了這麼多，既然我們是自己開發一套新的 Toolkit，當然就要來實作一個合乎我們預期行為的 Popup Menu Window，首先在建立視窗並設定為 Popup Menu 類型後，需要做一些設定並欄截事件：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;...

    /* Grab pointer */
    int i;
    Window grabWin = -1;
    XGetInputFocus(disp, &amp;grabWin, &amp;i);
    XGrabPointer(disp, grabWin, TRUE,
        ButtonPressMask | ButtonReleaseMask,
        GrabModeAsync, GrabModeAsync,
        None, None, CurrentTime);


    /* Skip Taskbar */
    Atom wm_state = XInternAtom(disp, &quot;WM_STATE&quot;, False);
    Atom atom = XInternAtom(disp, &quot;_NET_WM_STATE_SKIP_TASKBAR&quot;, False);

    XChangeProperty(disp, win, wm_state,
        XA_ATOM, 32, PropModeAppend,
        (unsigned char *)&amp;atom, 1);


    /* Override redirect */
    XSetWindowAttributes attr;

    attr.override_redirect = True;
    XChangeWindowAttributes(disp, win, CWOverrideRedirect, &amp;attr);


...
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
接著在監聽 X Event 迴圈處，去檢查是否為 grabWin 傳來的 ButtonRelease Event（意即使用者用點擊了 Popup Menu Window 之外的區域），如果是就停止 Event 的欄截並關閉視窗。&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;...
    if (xev-&gt;type == ButtonRelease &amp;&amp; grabWin == xev-&gt;xbutton.window) {
        XUngrabPointer(disp, CurrentTime);
        grabWin = -1;

        XUnmapWindow(disp, win);

        continue;
    }
...&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
&lt;b&gt;後記&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
經過許多努力，完善各項功能，完全用 JavaScript 打造的桌面環境，指日可待。:-P</description><link>https://fred-zone.blogspot.com/2012/06/x11-popup-menu.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>6</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-6884762036520889532</guid><pubDate>Sun, 17 Jun 2012 04:09:00 +0000</pubDate><atom:updated>2012-06-17T12:10:17.988+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">心得分享、講座</category><category domain="http://www.blogger.com/atom/ns#">自由軟體</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><category domain="http://www.blogger.com/atom/ns#">雲端(Cloud)</category><title>【心得分享】軟體人甘苦談</title><description>很抱歉，因為台北大雨來襲，導致班機延誤，讓小弟無法及時從香港趕回台灣參加 FreedomHEC 2012。相當對不起主辦單位，就這樣讓當天議程開了天窗。但是，之後我有空時，仍會整理資料，依舊會公開原先預定要給予的簡報。&lt;br /&gt;
&lt;br /&gt;
不幸的是，活動第二天被客戶抓住，所以也沒辦法到場聽講。不過，雖然沒參加到 FreedomHEC 2012，為了賠罪，第三天當了司機（Be a driver, not writing driver :-S），開著車載著外國講者們一路到了宜蘭大學，給予學生機會直接面對面與 Kernel 開發者交流。&lt;br /&gt;
&lt;br /&gt;
除了面對面問答解惑，其中當然也有保留一點時間，讓講者們可以自我介紹，也分享一些經驗。我也給予了一份簡短的心得分享，由於對象是學生，時間也有限，所以就不直接討論艱澀的技術，而是以輕鬆的角度切入，分享一下創業的經驗和業界的感想，還有描述就算在面對不利軟體業的環境之下，身為一個軟體人所需要的熱情和自我要求。&lt;br /&gt;
&lt;br /&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;&lt;object height=&quot;355&quot; id=&quot;__sse13354486&quot; width=&quot;425&quot;&gt; &lt;param name=&quot;movie&quot; value=&quot;http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=random-120616222251-phpapp01&amp;stripped_title=ss-13354486&amp;userName=cfsghost&quot; /&gt;&lt;param name=&quot;allowFullScreen&quot; value=&quot;true&quot;/&gt;&lt;param name=&quot;allowScriptAccess&quot; value=&quot;always&quot;/&gt;&lt;param name=&quot;wmode&quot; value=&quot;transparent&quot;/&gt;&lt;embed name=&quot;__sse13354486&quot; src=&quot;http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=random-120616222251-phpapp01&amp;stripped_title=ss-13354486&amp;userName=cfsghost&quot; type=&quot;application/x-shockwave-flash&quot; allowscriptaccess=&quot;always&quot; allowfullscreen=&quot;true&quot; wmode=&quot;transparent&quot; width=&quot;425&quot; height=&quot;355&quot;&gt;&lt;/embed&gt;&lt;/object&gt;&lt;/div&gt;&lt;br /&gt;
衷心期盼我們能培養軟體開發的熱忱，然後才有機會做出軟體的價值。:-)</description><link>https://fred-zone.blogspot.com/2012/06/blog-post_17.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>4</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-5424401200850663148</guid><pubDate>Sun, 03 Jun 2012 21:53:00 +0000</pubDate><atom:updated>2012-06-04T05:54:04.901+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">心情筆記</category><category domain="http://www.blogger.com/atom/ns#">顧問咨詢</category><title>如何當一個優秀的救火員之打蛇打七寸</title><description>經歷過許多救火任務（這邊當然是指軟體系統專案的問題），從 Kernel、Driver、Porting 到各類系統應用程式、Web 應用程式，幾乎無所不救，有社群內的朋友嘲笑著說：『在國內大概沒有團隊，像我們一樣什麼火都救。』。有許多的感觸是肯定的，從問題發生的那一面看去，加以急迫時間的摧化之下，感受到許多程式開發者最真實的思維和邏輯，當然，還有發案老闆的著急，以及清楚看到許多關係人暗地裡的算盤。我不敢說自己是優秀的救火員，但至少一旦承諾並接下案子，有再大的困難和阻礙也總是使命必達，哪怕連續數個月每天平均只睡兩小時，三四天只睡一次，也不會倒下。我想，最少我可以談談如何救火，還有從火堆中看到的許多『人』的問題。&lt;br /&gt;
&lt;br /&gt;
其實，救火沒什麼訣竅，撲滅它是其次，主要是確保不會再燃起。俗話說『打蛇打七寸』，解問題時也要解到夠力才行。但是，或許你常聽有人說：『產品要出貨，能動最優先，好不好其次。』，似乎恰恰與我所提到的相反。沒錯，因為要確保火不會再燃起，理論上是要找出問題根源，從根源著手和解決，很多人乍聽之下，都會覺得是費工又花時間的工作。但是，每次的經驗都告訴我，實際上，找出問題根源，並解決他，往往比修補來得快很多，而且一勞永逸。尤其是碰到死線(deadline)將至，卻一直解不掉的一大坨 Bugs（指同類相關或互相影響的問題），從根本上並深入解決問題才是最快的辦法。&lt;br /&gt;
&lt;br /&gt;
這邊有個簡單的案例：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;有一個應用程式，與 Library、System API 和 Driver 都有相關，他們相依關係是：

應用程式 -&gt; Library -&gt; System API -&gt; Driver

假設現在有個問題，這支應用程式出現了一個 Bug，而這個問題最終被發現是 Driver 缺少一些功能，而造成功能不正常。那我們應該從哪裡去解決？
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
一般人的做法，若是為了因應『產品要出貨，能動最優先』，所採取的方法當然是拿掉應用程式上的某些受影響的功能，或是改寫應用程式以另循途徑的方法達成同功能。但是，若你選擇這樣的做法，最可怕問題是，如果應用程式太過複雜，修改起來會有更多的副作用(Side effect)，你要花更多時間去處理更多原先不存在的問題。&lt;br /&gt;
&lt;br /&gt;
況且，這都還沒講到，我們根本都還不知道 Driver 造成的影響範圍有多大，有哪些 System API 、Library API 和依賴他們的應用程式受影響。而今天這個應用程式僅僅只是剛好被發現到有問題而已，一切都還只是開端，之後會發現其他地方也開始到處起火了。或許這邊講的有點危言聳聽，但都是實際上常遭遇的狀況。&lt;br /&gt;
&lt;br /&gt;
不過，這種方法也不是完全不對，如果你很肯定，問題只會出現在一個地方，而且該功能可以被容許直接拿掉，這是最佳的救火方案。&lt;br /&gt;
&lt;br /&gt;
可是往往情況沒有這麼簡單，你必需保留該功能或是更多功能，不但如此，你也必需修好它。所以，在這種情況下，通常我自己會使用前後夾擠式的做法，去尋求解決方案。由上而下（從應用程式往Driver 方向）的部份，主要還是與之前一樣，尋找只出現在一個地方的問題點，我們可以直接移除以滅火。而從下而上，就是盡可能直接把根源問題點解決。因此，首先當然是盡可能下手修好 Driver 的問題，不過，如果有不能修改的理由，就依序往上找到可修改的問題點。&lt;br /&gt;
&lt;br /&gt;
要知道，修復一個 API 可以修好數十數百個使用它的應用程式，但你如果是從應用程式著手，那你就要重覆一一修改不同的應用程式，更別說還要花上每次測試找問題的時間，其數量是以指數級數來算的。所以除了直接刪除功能的做法，最好的處理位置依序為『Driver &gt; System API &gt; Library &gt; 應用程式』。&lt;br /&gt;
&lt;br /&gt;
曾經有碰過一場火，客戶軟體所在的平台本身不穩定，但他們不旦不釐清平台的問題，反而一直著手修改應用程式去避開平台本身的問題，最終不但把自己原本的程式改得亂七八糟難以維護，更是讓修改後的程式打了結，得到許多無解的副作用。更可悲的是，下次換了一個平台，這次做的所有修改，都是白做要重新來過。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;後記&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
你或許覺得本文提供的方法，並沒有什麼大不了的，那是因為已經公布了問題點所在。事實上，實際情況可能更為糟糕，你可能根本不知道是哪一層發生問題。而如何在尋求解決方案的過程中，找出問題可能的發生源，又會是另一個故事了。:-)</description><link>https://fred-zone.blogspot.com/2012/06/blog-post.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>3</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-8161461001335346397</guid><pubDate>Tue, 22 May 2012 12:44:00 +0000</pubDate><atom:updated>2012-05-22T20:52:36.392+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">NodeJS</category><category domain="http://www.blogger.com/atom/ns#">程式心得筆記</category><title>簡單理解 JavaScript 的記憶體管理機制</title><description>不像其他的語言，JavaScript 開發者永遠沒有辦法自己去釋放記憶體，頂多只能移除物件的 Reference （代表這物件已經沒有人在使用），而且這物件所佔的記憶體並不會馬上被釋放，而是 Garbage Collection 在滿足某些條件的情況下，才在背景自動去尋找沒有被使用的物件，然後釋放。若你嘗試過尋找釋放記憶體或移除物件 Reference 的方法，得到的解答，應該不外乎是使用 delete 關鍵字或是將變數設為 null，但是你真的了解它的意義嗎？事實上，有很多人都是在不瞭解的情況下使用它們，還可能因此產生 Memory leaks 的狀況。欲弄清楚這一切，我們必須先簡單理解一下 JavaScript 的記憶體管理機制，更準確的說，是物件的管理機制。&lt;br /&gt;
&lt;br /&gt;
從 JavaScript 開發者角度來看，JavaScript Engine 在運作時，記憶體使用是呈現樹狀結構，也就是所有命名或建立的變數或物件，都是存放在一個全域(global)的 Object 中。在 Node.js 中，你可以直接讀取 global 變數看到目前 Context 的環境狀態：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;console.log(global);&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
我們可以做個實驗理解一下：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;var myVar = &#39;Hello&#39;;
function myFunc() {
    return 123;
}
var myObj = {
    a: 1,
    b: 2
};

console.log(global);&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
執行以上程式，你應該可以從 global 中找到我們自己定義的變數和函式：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;{
    ...（已省略基本預設的環境變數）...
    myVar: &#39;Hello&#39;,
    myFunc: [function],
    myObj: {
        a: 1,
        b: 2
    },
    ...
}&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
從結果可以發現，所有的物件都以樹狀的形式被 global Object 保存著，無論是變數還是任何一種類型的物件，都是一組組 Key/Value 的存在。而 Value 就是各種不同形態的物件，如字串、函數、陣列、數值等。&lt;br /&gt;
&lt;br /&gt;
所以，移除某物件的 Reference，就意味著將把物件從這棵樹上拔除掉。因此，我們可以直接將該變數設為 null：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;myVar = null;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
由於該變數被設為 null，原本的字串（包含著『Hello』）物件就失去了依附的樹枝，如枯葉般從樹上掉下來，等著 Garbage Collection 來回收它。對於開發者而言，其實就是告訴 GC 我不需要這物件了，隨時可以把這個物件的記憶體釋放。&lt;br /&gt;
&lt;br /&gt;
然而，雖然變數被設為 null 後，原本的物件被釋放了，但該變數還是存在的，別忘了，他是一個在 global Object 中的 Key，現在只是沒有 Value 為 null 而已。要真正把這個變數給刪除，這時就要用到 delete 關鍵字。如果你去查一下 JavaScript 的 API 參考文獻，就會發現 delete 關鍵字其實是拿來刪除 Object 中的一組 Key/Value。因此，既然 JavaScript 所有的變數其實都只是一組存放在 global Object 的 Key/Value，我們理所當然可以用 delete 關鍵字去移除掉他：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;delete myVar;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
知曉了 JavaScript 的記憶體管理機制後，你就會了解使用 delete 關鍵字和將變數設為 null，其實並不是代表物件就會被釋放，只是砍樹枝去減少物件的 Reference。&lt;br /&gt;
&lt;br /&gt;
此外，如果一個物件有多個 Reference，只是單單刪其中一個也不會讓物件被 GC 釋放：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;var myVar = &#39;Hello&#39;;
var myVar1 = myVar;

myVar = null;
delete myVar;

console.log(myVar1);&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
以上的程式會顯示『Hello』字串，該物件並不會因為失去 myVar 這 Reference 而被 GC 移除。若想要這一個字串被釋放，必需清空物件所有的 Reference（包括 myVar 和 myVar1），才能讓物件具有被 GC 回收的條件。所以，如果你不小心讓一個不明顯的變數勾搭上了物件，然後你忘記了這個變數的存在，很有可能就會造成 Memory Leaks，讓以為已經被釋放的物件，偷偷存活在於記憶體上。&lt;br /&gt;
&lt;br /&gt;
備註：Reference 是常見於各種系統的設計，主要做法是幫物件建立一個 Reference 計數器，當有人關聯或使用到他，就會讓這計數器加一，等到關聯被移除或使用完畢後，就會讓計數器減一。所以，一旦計數器為零時，代表現在沒有任何外部的物件在使用或關聯到它，是可以被釋放掉的狀態。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;後記&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
這是很多人的誤解，在 JavaScript 中，千萬不要傻傻的以為用 delete 關鍵字就可以把物件給釋放了，需要特別注意。:-)</description><link>https://fred-zone.blogspot.com/2012/05/javascript_22.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>5</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-8083074365629800548</guid><pubDate>Sat, 19 May 2012 12:15:00 +0000</pubDate><atom:updated>2012-05-19T20:15:58.628+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">Kamalan Web Framework</category><category domain="http://www.blogger.com/atom/ns#">NodeJS</category><category domain="http://www.blogger.com/atom/ns#">RedTea Web Framework</category><category domain="http://www.blogger.com/atom/ns#">Web</category><category domain="http://www.blogger.com/atom/ns#">Web 相關技術</category><category domain="http://www.blogger.com/atom/ns#">心得分享、講座</category><category domain="http://www.blogger.com/atom/ns#">自由軟體</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><title>【JSDC.tw 2012 簡報檔釋出】Non-MVC Web Framework</title><description>由於一些因素，被徵招去救火，在 JSDC.tw 2012 (JavaScript Developer Conference 2012) 給與了一場 Talk，由於準備時間相當緊迫，就把最近在研究和試驗的半成品匆匆拿上場展示和說明，題目是『Non-MVC Web Framework』。你可能在議程上沒有看到這場 Talk，實際上是因為主辦單位的一些小疏忽，議程上仍是寫著之前的舊題目『Build Node.js Module』，而且小弟的名字也打錯了，從 Fred 變成了 Frend，差一點就變成二十世紀少年的友民黨教主。:-)&lt;br /&gt;
&lt;br /&gt;
本次議程其實和前幾天發的舊文『&lt;a href=&quot;http://fred-zone.blogspot.com/2012/05/redtea-web-framework.html&quot;&gt;前端工程師也可以淡定的開發網站應用！RedTea Web Framework！&lt;/a&gt;』息息相關，探討的是改善 MVC 的開發流程，和引入 JavaScript/Node.js 後的大膽嘗試，而且進一步討論實作細節。此外，因為 RedTea 本身只是一個實驗性的專案，目的只在於驗證可行性，還不到實際使用程度，所以藉著這次機會，公開並宣布新專案『&lt;a href=&quot;https://github.com/cfsghost/Kamalan&quot;&gt;Kamalan Web Framework（葛瑪蘭）&lt;/a&gt;』，Kamalan 引入了 Express Web Framework 為基礎，然後將結合 RedTea 的成果，並更進一步簡化其中細節和使用方法，讓前端工程師能真正無痛上手。&lt;br /&gt;
&lt;br /&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;&lt;object height=&quot;355&quot; id=&quot;__sse12991801&quot; width=&quot;425&quot;&gt;&lt;param name=&quot;movie&quot; value=&quot;http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=non-mvcwebframework-120519063445-phpapp01&amp;stripped_title=nonmvc-web-framework&amp;userName=cfsghost&quot; /&gt;&lt;param name=&quot;allowFullScreen&quot; value=&quot;true&quot;/&gt;&lt;param name=&quot;allowScriptAccess&quot; value=&quot;always&quot;/&gt;&lt;param name=&quot;wmode&quot; value=&quot;transparent&quot;/&gt;&lt;embed name=&quot;__sse12991801&quot; src=&quot;http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=non-mvcwebframework-120519063445-phpapp01&amp;stripped_title=nonmvc-web-framework&amp;userName=cfsghost&quot; type=&quot;application/x-shockwave-flash&quot; allowscriptaccess=&quot;always&quot; allowfullscreen=&quot;true&quot; wmode=&quot;transparent&quot; width=&quot;425&quot; height=&quot;355&quot;&gt;&lt;/embed&gt;&lt;/object&gt;&lt;/div&gt;&lt;br /&gt;
雖然還沒完全將 RedTea 的機制完全整合進 Kamalan，但目前 Kamalan 最少可以和 Express 一樣使用，你如果有用 Express 寫的程式，直接將 require() 引入的模組名改成 Kamalan 即可。由於 Kamalan 在根本上就是 Express，甚至是可以使用 Express 的所有 Middleware。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;後記&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
這場 Talk 後，引起了一些 JavaScript Developer 趕的迴響，一些建議和聽眾的期待相當令人開心，討論過程也非常愉快。感謝大家的參與。:-)</description><link>https://fred-zone.blogspot.com/2012/05/jsdctw-2012-non-mvc-web-framework.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>1</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-5819619038414316166</guid><pubDate>Fri, 18 May 2012 04:07:00 +0000</pubDate><atom:updated>2012-05-18T12:07:28.042+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Desktop</category><category domain="http://www.blogger.com/atom/ns#">GNOME</category><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">心得分享、講座</category><category domain="http://www.blogger.com/atom/ns#">自由軟體</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><title>【GNOME.Asia 2012】Enjoy Writing a Modern Desktop Application in JavaScript</title><description>依稀記得，當年去越南(Vietnam)參加 GNOME.Asia Summit 2009 的情景，當時甚至寫了一篇[&lt;a href=&quot;http://fred-zone.blogspot.com/2009/11/our-happy-travel-for-gnome-summit-in.html&quot;&gt;遊記心得&lt;/a&gt;]。因為那是首次以講者身份參加國外的 Open Source 研討會活動，意義非凡。更重要的是，那也是當時和 Penk 組成的 ULLab(/usr/local/lab) 第一次出征，如今這樣的組合雖不復存在，但仍在心中藏有很多回憶。&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://2012.gnome.asia/static/img/logo.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left:1em; margin-right:1em&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;90&quot; width=&quot;200&quot; src=&quot;http://2012.gnome.asia/static/img/logo.jpg&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
今年度的『&lt;a href=&quot;http://2012.gnome.asia/&quot;&gt;GNOME.Asia Summit 2012&lt;/a&gt;』將在 6/9 - 6/10  於香港舉行，很感謝 GNOME Foundation 再次贊助，讓小弟幸運的又能以講者身份飛去參加這次活動，而這次要給予的主題是『Enjoy Writing a Modern Desktop Application in JavaScript』。&lt;br /&gt;
&lt;br /&gt;
今日科技變革相當快速，但桌面應用的開發一直跟不上節奏，原因是『開發效率差』和『入門門檻高』一直都是 C/C++ 這類中低階的語言的痛處，不足因應這個時代對應用的大量需求。因此使用有高生產力的腳本(Script)語言來開發桌面應用程式，便是目前可行的解決之道。&lt;br /&gt;
&lt;br /&gt;
JavaScript 自 World Wide Web 澎渤發展以來，一直處於相當重要的地位，用於處理 User Interface 的工作，已經有快要二十年的時間，可說是非常成熟。事實上，GLib/GTK+/Clutter 等底層的事件模型，就與 JavaScript Engine 本身的機制大同小異，在同樣解決使用者介面的工作上，可說是殊途同歸。因此，有這樣豐富的前線作戰經驗當後盾，我們可以確信，JavaScript 非常適合被用於開發桌面應用程式。若是與 GTK+/Clutter 這類底層 Toolkit 相結合，跳脫出瀏覽器，寫原生的桌面應用程式也未嘗不可。&lt;br /&gt;
&lt;br /&gt;
這次的議程，小弟將提及：&lt;br /&gt;
&lt;ol&gt;&lt;li&gt;回顧 JavaScript 歷史和發展&lt;/li&gt;
&lt;li&gt;探討如何使用 JavaScript 開發原生的桌面應用程式（搭配 GTK+/Clutter/Mx）&lt;/li&gt;
&lt;/ol&gt;&lt;br /&gt;
既然這次活動的主角是 GNOME，主要將以探討原生的桌面程式為主，當然也會因應未來發展，提及 Web Application 的部份。&lt;br /&gt;
&lt;br /&gt;
期待與香港和世界各地前來的朋友相見。:-)</description><link>https://fred-zone.blogspot.com/2012/05/gnomeasia-2012enjoy-writing-modern.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>1</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-3089389334953924083</guid><pubDate>Mon, 14 May 2012 12:59:00 +0000</pubDate><atom:updated>2012-05-14T20:59:08.973+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">NodeJS</category><category domain="http://www.blogger.com/atom/ns#">RedTea Web Framework</category><category domain="http://www.blogger.com/atom/ns#">Web</category><category domain="http://www.blogger.com/atom/ns#">Web 相關技術</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><category domain="http://www.blogger.com/atom/ns#">雲端(Cloud)</category><title>前端工程師也可以淡定的開發網站應用！RedTea Web Framework！</title><description>還記得，尚未投入 Node.js 前，一直覺得 Node.js 帶來了未來，讓我們可以用 JavaScript 同時開發 Web 前端(Front-end)和後端(Back-end)，等到真的投入 Node.js 後，發現雖然事實的確是如此，但由於前端和後端應用所需要的背景知識不盡相同，開發模式和概念更是大異其趣，所以，雖然同樣是使用大家熟悉的 JavaScript 語言，但前端開發者仍然不見得能夠如願地來開發後端應用。&lt;br /&gt;
&lt;br /&gt;
這樣的情況讓我想起有位來台灣工作的外國朋友，曾告訴過我一個多年前發生在他身上的笑話。故事是：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;他原本是個美術方面的設計師，然後，但有一天老闆對他說：『你從現在起，去做開發程式的工作吧。』

他問：『為什麼？』

老闆回答：『寫程式是用英文寫，而你又會英文，不就可以寫嗎？』&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
是的，同樣道理，雖然對前端工程師而言，JavaScript 是最熟悉的程式語言，而 Node.js 又可以讓你使用 JavaScript 寫整個 Web 應用，但這不代表對這些人而言，就可以輕易上手 Web service 的後端開發。要真正讓前後端開發合而為一，不只是語言要統一，開發經驗也要相同才是，這才是所有 Web 開發者最期盼的事。&lt;br /&gt;
&lt;br /&gt;
於是筆者基於 Node.js，開發了『&lt;a href=&quot;https://github.com/cfsghost/redtea&quot;&gt;紅茶(RedTea)&lt;/a&gt;』，這是一個和現有的網站框架(Web Framework)所不一樣的全新的嘗試。以前端開發者視角和經驗為出發點，專門設計給前端工程師上手使用的網站框架，讓前端工程師也可以『淡定』的開發網站應用。由於 RedTea 不是傳統 MVC 模型的 Framework（至於 RedTea 採用的是哪種開發模型，筆者一時也說不上來。），所以，如果你是一個已經被 MVC 涂毒已深的開發者，可能要先花點時間重新理解一下。:-)&lt;br /&gt;
&lt;br /&gt;
此外，RedTea 有一個最大的特點，就是支援了在瀏覽器環境下，呼叫後端 JavaScript Class/Function 的功能，就像在使用本地端的 JavaScript 物件般。因此，前後台交換資料，不用再以 GET/POST、URL Path Routing 或 Ajax 相關的方法實作，只要學會怎麼使用 JavaScript Class 即可。重點是，即使你沒有任何 HTTP 通訊協定的知識，或後前後端資料處理的經驗，依然可以開發出網站程式。&lt;br /&gt;
&lt;br /&gt;
當然，設計 RedTea 的目的，除了是為前端工程師著想之外，也是因為長久以來在思考 MVC 模型的問題後，所嘗試提出的解決方案。至於是什麼問題，可以從專案開發的流程探討，一個基於 MVC 模型的 Web 專案開發流程大致上如下（問題點也將補充在後面）：&lt;br /&gt;
*註：『程式架構和演算法：Model』、『視覺部份和 UI：View』、『控制機制：Controller』&lt;br /&gt;
&lt;ol&gt;&lt;li&gt;設計 Model（問題：當你一開始根本不完全清楚功能需求時，你如何能設計一個完善的架構去容下一切？）&lt;/li&gt;
&lt;li&gt;設計 View&lt;/li&gt;
&lt;li&gt;設計 Controller，用來連接程式邏輯架構和前端 UI (問題：有太多種交換方式，GET/POST Routing stuffs, Ajax APIs 等)&lt;/li&gt;
&lt;li&gt;為了更完整的功能需求，一次又一次修改和重構 Model&lt;/li&gt;
&lt;li&gt;為了配合程式架構和演算法的修改，一次又一次修改和重構 View&lt;/li&gt;
&lt;li&gt;Controller 零亂又噁心不已&lt;/li&gt;
&lt;/ol&gt;&lt;br /&gt;
說穿了，這種來來回回式的開發方法，若是在前後端是不同語言的情況下，將會是不得已的情況。畢竟，前後端需要共同定出一個雙方可以接受的資料交換格式，然後在開發過程中逐漸磨合。不過，當前後端都是 JavaScript 的情況下，是否可以簡化這部份的開發流程，就是一個可以思考的地方。&lt;br /&gt;
&lt;br /&gt;
所以，RedTea 的設計，主要是為了達成這樣的 Web 開發流程：&lt;br /&gt;
&lt;ol&gt;&lt;li&gt;設計你眼前第一時間想看到的東西&lt;/li&gt;
&lt;li&gt;在所看到的 UI 上，實作功能需求&lt;/li&gt;
&lt;li&gt;在後端(Server-side)處理使用者因需求產生的資料&lt;/li&gt;
&lt;/ol&gt;&lt;br /&gt;
由於前後端都是 JavaScript，很容易做到前後端邏輯上的同步，再加上簡單的轉換器和 RPC 設計，就可以讓前端程式直接呼叫後端的 Class 和 Function。後端的 API，就彷彿是你前端程式的一部份。&lt;br /&gt;
&lt;br /&gt;
說了這麼多，到底如何使用 RedTea 開發出一個網站呢？如果你參考 Github 上的 RedTea 範例，會發現主要有四個目錄，分別說明如下：&lt;br /&gt;
&lt;ol&gt;&lt;li&gt;ui - Layout Template&lt;/li&gt;
&lt;li&gt;runner - Browser-side Script&lt;/li&gt;
&lt;li&gt;routes - URL Path Routing&lt;/li&gt;
&lt;li&gt;apis - Server-side APIs&lt;/li&gt;
&lt;/ol&gt;&lt;br /&gt;
若你有摸過其他的 MVC Web Framework，對 routes 的功用應該不陌生，而 RedTea 當然也可以像其他框架一樣，讓你隨意自定 URL 的橋接。但若要善用 RedTea 的優勢，routes 的主要目的應該只是決定 UI 和 Runner 的組合，如下：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;module.exports = {
    &#39;/&#39;: index
};

function index(app, req, res) {
    /* Using index.jade (UI) and index.js (Runner) */
    res.render(&#39;index&#39;, { title: &#39;RedTea&#39; });
};&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
RedTea 在回應瀏覽器的要求時，會合併指定的 UI 和 Runner，並自動代入 RedTea Caller 的機制，讓 runner 被輸出到前端時，有能力呼叫 Server-side APIs。&lt;br /&gt;
&lt;br /&gt;
一個 runner 程式大概長的是這個樣子（examples/runner/index.js）：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;RedTea.import();

RedTea.main(function() {
    var chat = new RedTea.API.Chat;

    chat.say(&#39;Fred&#39;, &#39;Hello World!&#39;);
    chat.getConversation(function(err, data) {
        for (var i in data) {
            var lineObj = data[0];
            var dom = document.getElementById(&#39;conversation&#39;);
            dom.innerHTML += lineObj.name + &#39;:&#39; + lineObj.content + &#39;&amp;lt;br&amp;gt;&#39;;
        }
    });
});&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
從範例中，你會看到瀏覽器上才有的 document 物件出現，事實上 runner 就是一支跑在瀏覽器上的 JavaScript 程式。 RedTea.import() 會去 Server 載入目前已經被定義的 API，等初始化完成，會以 RedTea.main() 為程式進入點開始跑。這時，你就可以用 RedTea.API 去建立 Server API 定義的物件，然後呼叫在 Server-side 的函數方法。進一步，你也可以用得到的資料，去更新網頁上的 DOM。&lt;br /&gt;
&lt;br /&gt;
至於 Server-side APIs 的定義與寫一般 JavaScript 無異，你可以定義 Class、Function 或是變數，RedTea 會自動將這些 API 轉換成前端可以跑的形式，而原始的 API 定義長這個樣子（examples/apis/chat.js）：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;module.exports = {
    Chat: Chat
};

function Chat(externalData) {
    this.externalData = externalData;
    this.publicData = externalData.userdata;
}

Chat.prototype.getUserList = function(callback) {

    callback(null, this.publicData.users);
};

Chat.prototype.getConversation = function(callback) {

    callback(null, this.publicData.conversation);
};

Chat.prototype.say = function(name, content) {
    var line = {
        name: name,
        content: content
    };

    this.publicData.conversation.push(line);
};&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
然後和一般的 Node.js 程式一樣，你會需要一支主程式（examples/app.js）：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;var RedTea = require(&#39;redtea&#39;);

var app = new RedTea;

app.routeDirs.push(__dirname + &#39;/routes&#39;);
app.uiDirs.push(__dirname + &#39;/ui&#39;);
app.runnerDirs.push(__dirname + &#39;/runner&#39;);
app.apiDirs.push(__dirname + &#39;/apis&#39;);

var publicData = {
    users: [],
    conversation: []
};

app
    .initRoute()
    .initRender()
    .initAPI(publicData)
    .listen(9876);&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
&lt;br /&gt;
只會 JavaScript 嗎？開始享受單純用 JavaScript 開發網站應用程式的人生吧！卡關了？就喝杯紅茶淡定一下！ :-D&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;後記&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
RedTea 目前只是原型，還不是相當完整，像是 static file 的功能都尚未實作，很多功能也還只是堪用，歡迎各界一同補齊它。 :-)</description><link>https://fred-zone.blogspot.com/2012/05/redtea-web-framework.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>11</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-4434066918386952875</guid><pubDate>Fri, 04 May 2012 21:57:00 +0000</pubDate><atom:updated>2012-05-05T06:05:35.245+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">NodeJS</category><category domain="http://www.blogger.com/atom/ns#">程式心得筆記</category><category domain="http://www.blogger.com/atom/ns#">自由軟體</category><title>想寫好 JavaScript，遞迴技能要練好</title><description>V8 JavaScript Engine 的運作，主要是以事件驅動(Event-driven)來執行所有的程式碼，如果你有開發過 GTK+ 程式的經驗，使用上應該不陌生。不過這對一般人其實相當不好理解，因為事件驅動會讓你的程式看起來像是多工在運作（如使用執行緒），所以有些人對於 JavaScript 的理解是，有些東西可以丟背景執行。&lt;br /&gt;
&lt;br /&gt;
要了解事件驅動，可以先從 JavaScript 的行為說起。如果你正在使用 Node.js，你就會發現 Node.js 通常對同樣功能提供了兩個 API，分為『同步(Synchronize)』和『非同步(Asynchronize)』兩種類型。例如下面這刪除特定檔案的例子：&lt;br /&gt;
&lt;br /&gt;
同步(Synchronize) API：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;fs.unlinkSync(&#39;/home/fred/badfile&#39;);
...&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
非同步(Asynchronize) API：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;fs.unlink(&#39;/home/fred/badfile&#39;, function(err) {
    if (err) {
        console.log(&#39;Failed to delete file.&#39;);
        return;
    }

    console.log(&#39;Successfully delete a file.&#39;);
});
...&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
簡單觀察同步與非同步的執行結果，可以知道前者（同步）會停住並阻塞在此 API，等檔案刪除成功後才繼續下一行程式；而後者（非同步）有如將刪除檔案的工作丟到背景執行，並繼續往下一行程式執行，完全不會像同步 API 一樣停住。等刪除檔案的工作完成後，便觸發事件去呼叫 Callback function。而這樣非同步的機制就全靠事件驅動(Event-driven)來達成，雖看起來像是把程式丟到背景跑，實際上是在 JavaScript Engine 上註冊了一個事件，等到工作完成以後，事件被觸發時才接續執行 callback function。&lt;br /&gt;
&lt;br /&gt;
非同步的做法通常會用在需要花大量時間的 API 上，如檔案系統、資料庫系統的操作等，以避免因為單一 API 造成整個程式鎖死或卡住不會動的問題，尤以前端和需要即時反應的工作上，特別需要。&lt;br /&gt;
&lt;br /&gt;
至於這樣的事件驅動是怎麼達成的？簡單舉例，像多工作業系統一樣，如果你以微觀的視角來看，電腦根本仍只是單工在執行程式，只是每支程式因為分配到的執行時間相當小，對人來說，一晃眼就已經輪流執行了在作業系統『排程器』上的每一支程式，所以看似同時多工。而在 JavaScript Engine 中，其實也是類似的情形，但不太一樣的是，它是以『事件(Event)』而非『時間』為單位，也就是等到當下事件所對應的程式執行結束後，才會切換到下一個被觸發的事件。&lt;br /&gt;
&lt;br /&gt;
就因為 JavaScript Engine 有這樣的特性，所以我們可以肯定的知道有一種問題存在：『如果某個事件處理太久或陷入無窮迴圈，會讓整個 JavaScript Engine 動彈不得』，當在這問題發生時，我們的 JavaScript 程式就會像當機一樣，卡死在那不會動。&lt;br /&gt;
&lt;br /&gt;
所以，在了解 JavaScript Engine 的運作原理後，就知道在開發 JavaScript 程式時，有一些要點必須注意：&lt;br /&gt;
&lt;ol&gt;&lt;li&gt;盡可能『完全不使用』同步(Synchronize)類型的 API&lt;/li&gt;
&lt;li&gt;避免使用『大量次數』或『執行期長』的『迴圈(Loop)』和『遞迴(Recursive)』方法&lt;/li&gt;
&lt;li&gt;盡可能讓自己的程式，執行時不阻塞，以免影響到其他事件&lt;/li&gt;
&lt;/ol&gt;&lt;br /&gt;
不過寫程式難免會碰到處理大量資料，無法避免要花大量時間去處理，所以需要一些手段，利用 JavaScript 的事件引擎來分散消化這些運算。通常的做法有：&lt;br /&gt;
&lt;ol&gt;&lt;li&gt;切割資料，採遞回方式分批處理&lt;/li&gt;
&lt;li&gt;將迴圈改成使用遞迴方法&lt;/li&gt;
&lt;li&gt;使用 setTimeout() 或 process.nextTick() 改善遞迴呼叫&lt;/li&gt;
&lt;/ol&gt;&lt;br /&gt;
你可以看到，其實最終的解決方案都是改成遞迴方法，然後使用 setTimout() 或 process.nextTick() 去註冊事件，讓 JavaScript Engine 在下一次事件觸發時，才繼續處理資料。如此，便可避免事件阻塞，卡死整個程式。&lt;br /&gt;
&lt;br /&gt;
改善後的遞迴方法大致上如下：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;function goodRecursive() {
    /* Do Something... */

    /* setTimeout(goodRecursive, 0) */
    process.nextTick(goodRecursive);
}


goodRecusive();&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
所以說，想寫好 JavaScript ，遞迴技能要練好。 :-)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;後記&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
個人認為，不只是要『盡可能』避免使用同步(Synchronize)的 API，而是『根本不要』去使用它，因為在 JavaScript Engine 中，阻塞所造成的不良效應會比想像中的還要嚴重，你會打亂掉很多其他事件應有的運作。據過去經驗，阻塞效應所導致的周邊問題，非常難以除錯(Debug)，甚至是無法追蹤問題所在。</description><link>https://fred-zone.blogspot.com/2012/05/javascript.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>2</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-823182544516736006</guid><pubDate>Sun, 22 Apr 2012 16:12:00 +0000</pubDate><atom:updated>2012-04-23T00:25:46.903+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Hacking 心得筆記</category><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">JSDX</category><category domain="http://www.blogger.com/atom/ns#">NodeJS</category><category domain="http://www.blogger.com/atom/ns#">oFono</category><category domain="http://www.blogger.com/atom/ns#">有趣新構思</category><category domain="http://www.blogger.com/atom/ns#">自由軟體</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><title>【OSDC.tw 2012 Hackathon 成果分享】用 JavaScript 打電話囉！</title><description>以為 OSDC.tw 2012 在 4/15 號時就結束了嗎？其實並沒有。在活動的一個星期後，於 4/21 星期六，OSDC.tw 接著舉行了 Hackathon 的活動。很多人可能不知道 Hackathon 是什麼，有人叫他『駭客鬆』、『駭客松』或是『駭客爽』，其實總歸來說，就是讓開發人員盡情開發程式的活動。&lt;br /&gt;
&lt;br /&gt;
這類活動的規則是，參加的開發者可以自由組隊，提交想法，然後當場實作。最後，在活動結束前，讓各隊伍一一上台展示成果。由於開發者的創意常常天馬行空，又能看到實際成果，所以這類活動都相當有意思。這次，OSDC.tw 提供了無限量供應的食物還有場地，活動時間是從當天早上 9:00 到下午 6:00，成果發表是從下午 4:30 開始，每個隊伍約有七個半小時實作自己的提案。&lt;br /&gt;
&lt;br /&gt;
這次 Hackathon 活動，我與『魏藥』組隊，選的主題是『使用 JavaScript 打電話』，意即使用 JavaScript 開發出一支 Linux 應用程式，可以像手機一樣撥出電話並與對方通話。選這主題是因為我們的機器上剛好有 3G 數據卡(Modem)，所以這個提案的目的，主要是撰寫一個 Node.js 模組『jsdx-ofono』，去提供控制 Modem 的 JavaScript API，讓開發者可以透過這組 APIs 撥打甚至是傳接 SMS/MMS 之類的訊息。最後，為了驗證並展示，運用了之前開發的『jsdx-toolkit』，用 JavaScript 撰寫了一個簡單的撥號介面『jsdx-app-voicecall』。&lt;br /&gt;
&lt;br /&gt;
註：因為時間太趕，後來才發現，撥號 UI 忘了放倒退按鈕，所以撥錯號時要把程式重啟。:-P&lt;br /&gt;
&lt;br /&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;
&lt;iframe allowfullscreen=&quot;&quot; frameborder=&quot;0&quot; height=&quot;315&quot; src=&quot;http://www.youtube.com/embed/pzH1yxL5Tn8&quot; width=&quot;560&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;
&lt;br /&gt;
如專案名，其主要整合了 oFone ，應用了之前開發的 node-dbus，所以省下了不少時間，最後再以 jsdx-toolkit 快速實作了一個 UI。原本希望，如果時間再充足一些，我們計劃幫 jsdx-ofono 加上 SMS/MMS 和讀寫 SIM 卡電話簿的功能，可惜最後沒來得及。&lt;br /&gt;
&lt;br /&gt;
註：因為時間也不足，我們還來不及將 Linux 上的麥克風接上 oFone，所以目前接通後，只能聽到對方手機傳來的聲音。&lt;br /&gt;
&lt;br /&gt;
此外，這次展示的兩個元件『jsdx-ofono』和『jsdx-app-voicecall』都有在 github 釋出，歡迎取用：&lt;br /&gt;
&lt;a href=&quot;https://github.com/cfsghost/jsdx-ofono&quot;&gt;https://github.com/cfsghost/jsdx-ofono&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://github.com/cfsghost/jsdx-app-voicecall&quot;&gt;https://github.com/cfsghost/jsdx-app-voicecall&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;後記&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
這次的成果其實不限於使用在 PC 和 3G 網卡上，理論上，oFono 實作了完整的 Modem 管理，同樣一份程式，在手機裝置上同樣可以運作。這意味著使用 Node.js 自己開發一套 Android Framework 或 WebOS 其實並不是件難事。:-)</description><link>https://fred-zone.blogspot.com/2012/04/osdctw-2012-hackathon-javascript.html</link><author>noreply@blogger.com (Fred Chien)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://img.youtube.com/vi/pzH1yxL5Tn8/default.jpg" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-797211961310388409</guid><pubDate>Fri, 20 Apr 2012 17:11:00 +0000</pubDate><atom:updated>2012-04-21T01:20:12.775+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">心情筆記</category><category domain="http://www.blogger.com/atom/ns#">顧問咨詢</category><title>我所慣用的討論問題之方法</title><description>在處理技術問題時，我喜歡和他人討論，因為這是有助於大家提升技術的機會，也是種讓自己覺得不孤獨的方式，互相放下身段然後交換意見，每次都能不亦樂乎。這無關乎誰技術高低，只要大家能夠共同解決問題，除了是種樂趣也是種團隊集思廣益的機會。&lt;br /&gt;
&lt;br /&gt;
由於有些人可以接受反證的方式，而有一些人比較能接受列舉推導的證明方式。所以，難免會碰到種情況，就是有人會大玩文字遊戲，讓你分不清他到底是真心想要找出問題，還是為辯而辯。其實，他們常常只是因為無法接受其他人的證明手段而已，所以互挑對方的骨頭。造成公說公有理，婆說婆有理，沒有交集點的局面。&lt;br /&gt;
&lt;br /&gt;
一旦碰到這種情況，為避免文字遊戲混淆主題，我總是會請在乎『文詞精確』的人，先自己定義一些文字和用詞，然後再行討論。或是請有一些想法的人，針對問題先列舉出他們自己認為的所有可能性，然後接著追加自己認為的可能性，最後再行討論。這樣做的目的是，試圖採用他們們各自喜歡的證明方法，正反舉證，然後以交集去找出問題，除了讓兩邊都沒有話說之外，也能更快讓大家有『同樣的語言』可以溝通。此外，有時候為使討論順利進行，我也會一項項以詢問的方式進行，有時問題相當愚蠢，甚至會穿插基本面的問題。&lt;br /&gt;
&lt;br /&gt;
很幸運的，使用這種方法後，在多數情況下，在大家列舉和回答問題之後，通常都能透過排除矛盾的方式，將問題給釐清，找到可能的疑點或是直接得到問題的解答。&lt;br /&gt;
&lt;br /&gt;
不過世界不是這麼美好，既然過程中有愚蠢或基本面的問題，有些自認有本事的人被問到時，難免會覺得你在污辱他或是瞎扯蛋，有些人會回嗆：『自己去看 Spec』 、『連這麼基本的問題都不會，還問、還想討論？』。一旦扯到人的問題，就會讓整個討論進行不下去。&lt;br /&gt;
&lt;br /&gt;
而有些人，在討論不下去後，會搬出多年來死背活背的聖經，在你面前推導了無數公式，繞了一大圈，最後『告訴你無解』或『證明自己沒有錯』。好一點的狀況，他們最後會『猜測問題』所在，回到討論的主題；不好一點的情況，他們就會告訴你『絕對不會有問題，是個無法解釋的靈異事件』，終結討論。&lt;br /&gt;
&lt;br /&gt;
值得注意的是，如果問題的原因是：『執行那本聖經的理論時出現了 Bug』，你會更容易從他們口中得到『無解』和『不可能』的回答，因為這是『完美的聖經理論』內不會記載的東西。&lt;br /&gt;
&lt;br /&gt;
不過，當碰到這種人時就沒辦法了，只能等他們自己想通後給你解答，亦或是另請高明。所以，如果你是他們的主管，又不想得罪他們，你需要給他們更多時間讓他們獨立作業。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;後記&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
有方法且順利的進行討論，可以加速找到問題解答。不過討論是人和人之間的對談，難免會有人的因素參雜進來，這也是最麻煩的部份。:-)&lt;br /&gt;
&lt;br /&gt;
另外，在過往的經驗中，成功的討論一個問題並得到解答，靠的不是攻防戰，而是靠理性分析。既然是討論，就要先接受任何可能，再去一一收斂並交互探討。如果一開始就把所有可能性都拔除了，那最後會證明不出任何東西，此次的討論便沒有任何意義。當然，如果此次討論目的是你要證明自己很行，那你就真的做到了。</description><link>https://fred-zone.blogspot.com/2012/04/blog-post.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-8446169475925702072</guid><pubDate>Mon, 16 Apr 2012 12:41:00 +0000</pubDate><atom:updated>2012-04-16T20:41:50.327+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">C/C++</category><category domain="http://www.blogger.com/atom/ns#">Embedded System</category><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">NodeJS</category><category domain="http://www.blogger.com/atom/ns#">Web</category><category domain="http://www.blogger.com/atom/ns#">Web 相關技術</category><category domain="http://www.blogger.com/atom/ns#">心得分享、講座</category><category domain="http://www.blogger.com/atom/ns#">技術新知</category><category domain="http://www.blogger.com/atom/ns#">系統研發手札</category><category domain="http://www.blogger.com/atom/ns#">自由軟體</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><category domain="http://www.blogger.com/atom/ns#">雲端(Cloud)</category><title>【OSDC.TW 2012】Let&#39;s Enjoy Node.js - All Development in JavaScript 簡報檔釋出</title><description>感謝各位捧場，上周六在『&lt;a href=&quot;http://osdc.tw/&quot;&gt;OSDC.TW 2012&lt;/a&gt;』所分享的『Let&#39;s Enjoy Node.js - All Development in JavaScript』，旨在介紹『&lt;a href=&quot;http://nodejs.org/&quot;&gt;Node.js&lt;/a&gt;』這新興的 Open Source 專案，我們可以預見，其將帶來軟體業相當重大的變革，並為 JavaScript 程式語言開拓了嶄新的局面。目前簡報檔釋出，歡迎各界取用和參閱。&lt;br /&gt;
&lt;br /&gt;
自 2009 年 Node.js 公開發表以來，備受世界各地開發者囑目，在 2011 年 12 月更一度榮登 github 關注排行的第一位，超越了 Ruby on rails。Node.js 的出現意味著 JavaScript 語言從前端應用走進了 Server 的殿堂，除了效能表現不俗之外，使用其開發應用也相當迅速，未來 Web 開發者可以完全使用 JavaScript 語言打造網站服務，甚至是可大規模括展的雲端應用，堪稱 Web 開發者的夢想。&lt;br /&gt;
&lt;br /&gt;
值得一提的是，Node.js 其實不僅限於 Web 應用，甚至可以用於各類系統程式開發，比較廣為人知的例子是 HP WebOS（前身是 Palm WebOS）。其採用了 Node.js ，做為系統服務的開發途徑之一。此外，Node.js 有模組化的設計，讓開發人員可以輕易使用各類其他語言(C/C++/Python... etc)為其擴充功能，彈性相當高，更讓 Node.js 不受限於任何開發領域。&lt;br /&gt;
&lt;br /&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;
&lt;iframe frameborder=&quot;0&quot; height=&quot;355&quot; marginheight=&quot;0&quot; marginwidth=&quot;0&quot; scrolling=&quot;no&quot; src=&quot;http://www.slideshare.net/slideshow/embed_code/12555723&quot; width=&quot;425&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;</description><link>https://fred-zone.blogspot.com/2012/04/osdctw-2012lets-enjoy-nodejs-all.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-5155820343983696609</guid><pubDate>Sun, 15 Apr 2012 15:54:00 +0000</pubDate><atom:updated>2012-04-16T00:01:21.460+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">C/C++</category><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">NodeJS</category><category domain="http://www.blogger.com/atom/ns#">心得分享、講座</category><category domain="http://www.blogger.com/atom/ns#">程式心得筆記</category><title>【Node.js Taiwan 心得分享】How to Write Node.js Module 簡報檔釋出</title><description>於本週四所分享的『&lt;a href=&quot;http://fred-zone.blogspot.com/2012/04/nodejs-taiwan-how-to-write-nodejs.html&quot;&gt;Node.js Taiwan 社群聚會 - How to Write Node.js Module 心得分享&lt;/a&gt;』，簡報檔經整理後釋出，對 Node.js Module 開發有興趣的人可以自行參閱。若有不瞭解之處，可與小弟聯絡和討論。如有發現問題，請不吝賜教。&lt;br /&gt;
&lt;br /&gt;
本次分享主要有三大部份：&lt;br /&gt;
&lt;ol&gt;&lt;li&gt;使用 JavaScript 開發 Node.js Module&lt;/li&gt;
&lt;li&gt;學習使用 NPM 註冊帳號和上傳模組(Module)&amp;nbsp;&lt;/li&gt;
&lt;li&gt;進階議題：開發 Node.js 的 C/C++ Addon&lt;/li&gt;
&lt;/ol&gt;&lt;br /&gt;
簡報展示：&lt;br /&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;&lt;iframe frameborder=&quot;0&quot; height=&quot;355&quot; marginheight=&quot;0&quot; marginwidth=&quot;0&quot; scrolling=&quot;no&quot; src=&quot;http://www.slideshare.net/slideshow/embed_code/12547089&quot; width=&quot;425&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;後記&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
由於小弟於 people.linux.org.tw 上的 SSH Key 遺失了，短期內沒空處理，所以無法像過去一樣直接上傳 PDF 供大家取用。因此，日後會使用 slideshare 來分享簡報。</description><link>https://fred-zone.blogspot.com/2012/04/nodejs-taiwan-how-to-write-nodejs_15.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-6720069646780602930</guid><pubDate>Mon, 09 Apr 2012 23:52:00 +0000</pubDate><atom:updated>2012-04-10T09:02:59.422+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">NodeJS</category><category domain="http://www.blogger.com/atom/ns#">Web</category><category domain="http://www.blogger.com/atom/ns#">Web 相關技術</category><category domain="http://www.blogger.com/atom/ns#">程式心得筆記</category><title>各式 JavaScript Class 的效能比較</title><description>我們在做 JavaScript 開發時，常會使用到類別(Class) 的設計方法，但事實上，JavaScript 根本沒有所謂的類別(Class) 概念，此功能完全是靠動態物件(Object) 所模擬出來。因此，在 JavaScript 中，有數種方法可以定義和設計我們自己的類別(Class) 。可是哪一種方法的效能比較好呢？這便是本文的主題。&lt;br /&gt;
&lt;br /&gt;
為免空口白話，筆者在 jsperf.com 建立了一個 Test Case『&lt;a href=&quot;http://jsperf.com/prototype-operator-performance&quot;&gt;Prototype Operator Performance&lt;/a&gt;』以便實驗，並邀請各方網友使用瀏覽器前來測試，比較使用和未使用&amp;nbsp;Prototype 兩種情況，以及不同寫法的速度。（若你有興趣幫忙做實驗，進入該網站後， 請點選 Run Tests 開始測試）&lt;br /&gt;
&lt;br /&gt;
註：在測試過程中，請確定你的瀏覽器，沒去跑其他的吃重的網頁，以確保實驗結果的準確性。&lt;br /&gt;
&lt;br /&gt;
你可以在該網站上，觀察其他網友們的測試結果。但是，由於本次目的，主要測試的是同一種 JavaScript 引擎，處理不同建構方法的速度，所以，大家只要專注於在同一個瀏覽器下，其測試結果就可以。不同電腦，不同系統和不同瀏覽器的速度，並不是我們這次比較的重點。此外，因為 Node.js 使用的是 V8 JavaScript Engine，在本文中主要會拿 Chrome （因為 Chrome 使用的是 V8 JavaScript Engine）的結果討論。&lt;br /&gt;
&lt;br /&gt;
這個測試有兩組實驗，第一組是測試呼叫類別(Class) 中方法(Method) 的速度，我們分別測試三種，用不同方法建構出來的類別(Class)：&lt;br /&gt;
&lt;ol&gt;&lt;li&gt;未使用原型(Prototype)，直接在 Constructor 內定義 方法(Method)&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;A = function() {
  this.message = function(s) {
    var msg = s + &#39;&#39;;
  };
  this.addition = function(i, j) {
    return (i * 2 + j * 2) / 2;
  };
};
A_instance = new A();&lt;/pre&gt;&lt;/div&gt;&lt;div&gt;&lt;pre&gt;A_instance.message(&#39;Hi&#39;);
A_instance.addition(i, 2);&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;使用原型(Prototype)，在 Constructor 外定義&amp;nbsp;方法(Method)&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;B = function() {};
B.prototype.message = function(s) {
  var msg = s + &#39;&#39;;
};
B.prototype.addition = function(i, j) {
  return (i * 2 + j * 2) / 2;
};
B_instance = new B();&lt;/pre&gt;&lt;/div&gt;&lt;div&gt;&lt;pre&gt;B_instance.message(&#39;Hi&#39;);
B_instance.addition(i, 2);&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;使用原型(Prototype)，直接在 Constructor 內定義&amp;nbsp;方法(Method)&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;C = function() {
  C.prototype.message = function(s) {
    var msg = s + &#39;&#39;;
  };
  C.prototype.addition = function(i, j) {
    return (i * 2 + j * 2) / 2;
  };
};
C_instance = new C();&lt;/pre&gt;&lt;/div&gt;&lt;div&gt;&lt;pre&gt;C_instance.message(&#39;Hi&#39;);
C_instance.addition(i, 2);&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;&lt;br /&gt;
第二組則是運用 A、B、C 三種類別(Class)，測試建立實例(Instance) 的速度：&lt;br /&gt;
&lt;ol&gt;&lt;li&gt;A Class &lt;br /&gt;
&lt;div&gt;&lt;pre&gt;A_instance = new A();
A_instance.message(&#39;Hi&#39;);
A_instance.addition(i, 2);&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;B Class &lt;br /&gt;
&lt;div&gt;&lt;pre&gt;B_instance = new B();
B_instance.message(&#39;Hi&#39;);
B_instance.addition(i, 2);&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;C Class &lt;br /&gt;
&lt;div&gt;&lt;pre&gt;C_instance = new C();
C_instance.message(&#39;Hi&#39;);
C_instance.addition(i, 2);&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;/ol&gt;&lt;br /&gt;
截至目前為止，V8 JavaScript Engine 的測試結果：&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgEdhOUIpGl1wWJ2w9xFnv4C3YnbWErQieQGSfzWSl7I_jCD7iWSeiMd2c-nxSxj1vLnbgAs29EfCTbpZOcuxahK-fcIp398rJrnbRkkuzIhkiJ2T1H0QBwK_tnWy3IuAmFs_ibtAzTNwA/s1600/prototype-operator-performance.jpg&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;374&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgEdhOUIpGl1wWJ2w9xFnv4C3YnbWErQieQGSfzWSl7I_jCD7iWSeiMd2c-nxSxj1vLnbgAs29EfCTbpZOcuxahK-fcIp398rJrnbRkkuzIhkiJ2T1H0QBwK_tnWy3IuAmFs_ibtAzTNwA/s640/prototype-operator-performance.jpg&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;顏色各代表：&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;藍：Call Methods of A Class&lt;/li&gt;
&lt;li&gt;紅：Call Methods of B Class&lt;/li&gt;
&lt;li&gt;橘：Call Methods of C Class&lt;/li&gt;
&lt;li&gt;綠：Create A Instance&lt;/li&gt;
&lt;li&gt;紫：Create B Instance&lt;/li&gt;
&lt;li&gt;藍綠：Create C Instance&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;單純比較 呼叫方法(Call Method) ，不使用 Prototype 略勝一疇&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
雖然不同瀏覽器上不盡相同，但從測試結果來看，通常呼叫 A Class，不使用原型(Prototype) 所定義出來的方法(Method)，速度最快，這通常是因為執行原型(Prototype) 的方法(Method)之前，會先檢查是否有一般的方法(Method)，所以 JavaScript Engine 多做了一道檢查拖慢了速度。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;比較建立實例(Instance) 的速度，使用 Prototype 快過其他實作近 20 倍&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
如果是比較建立實例(Instance) 的速度，B Class 『在類別(Class) 外使用原型(Prototype) 去定義方法(Method) 』的方式，速度快過其他方法將近 20 倍，效能差距相當大。這造成效能低落的主要是因為 Constructor 的複雜度，JavaScript 在建立 實例(Instance) 時，每次都會執行 Constructor，所以 Constructor 一旦工作很多，效能理所當然會降低。&lt;br /&gt;
&lt;br /&gt;
而且，從測試結果來看，在 Constructor 外事先使用 原型(Prototype) 的情況下，所有的 實例(Instance) 會共用方法(Method)，而不是自己建立自己的，所以除了省記憶體外，理所當然也減少每次建立 方法(Method) 的時間。更重要的是，如果節省記憶體，也意味著 Garbage Collection 的工作越少，效能也會多少有些提升。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;總結&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
整體上來說，使用 Prototype 最好的方式，如果在需要重覆建立多個 實例(Instance) 的情況下，效能可以提升 20 倍，記憶體也節省不少。</description><link>https://fred-zone.blogspot.com/2012/04/javascript-class.html</link><author>noreply@blogger.com (Fred Chien)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgEdhOUIpGl1wWJ2w9xFnv4C3YnbWErQieQGSfzWSl7I_jCD7iWSeiMd2c-nxSxj1vLnbgAs29EfCTbpZOcuxahK-fcIp398rJrnbRkkuzIhkiJ2T1H0QBwK_tnWy3IuAmFs_ibtAzTNwA/s72-c/prototype-operator-performance.jpg" height="72" width="72"/><thr:total>1</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-6292109865790324942</guid><pubDate>Sat, 07 Apr 2012 07:07:00 +0000</pubDate><atom:updated>2012-04-07T15:12:20.293+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">C/C++</category><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">NodeJS</category><category domain="http://www.blogger.com/atom/ns#">心得分享、講座</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><title>Node.js Taiwan 社群聚會 - How to Write Node.js Module 心得分享</title><description>無論你是否已經相當熟悉，還是想要嘗試入門&amp;nbsp;Node.js，應該都想要尋找興趣同好，找機會共同研究和分享彼此心得。已經辦了多次聚會和心得分享的『&lt;a href=&quot;http://nodejs.tw/&quot;&gt;Node.js Taiwan 社群&lt;/a&gt;』，將於 4/12 （星期四）舉辦第五次的 Node.js 聚會，邀請廣大朋友們參加。&lt;br /&gt;
&lt;br /&gt;
小弟有幸受邀，將於這次聚會交流心得分享，主題是『How to Write Node.js Module』。&lt;br /&gt;
&lt;br /&gt;
在不久的文章『&lt;a href=&quot;http://fred-zone.blogspot.com/2012/03/javascript.html&quot;&gt;我為什麼大舉投入 JavaScript 的相關開發&lt;/a&gt;』內有提到，今天的 JavaScript 已經不再只是網頁前端的專利，除了能開發網站後端，也可以撰寫系統程式，甚至是嵌入式系統和跨平台的各類應用。而 Node.js 因為實作了許多基本的 JavaScript 函式庫，讓 JavaScript 可勝任許多的應用，所以近年來受到不少關注。此外，Node.js 的模組(Module)機制，更是讓我們能輕易擴充各種不同的功能，以及使用 C/C++ 為 Node.js 實作低階的 API。所以，想要完全不受限制的運用 JavaScript 開發各類應用，學會寫 Node.js Modules 是個必需要經歷的過程。&lt;br /&gt;
&lt;br /&gt;
有鑑於此，本次的分享內容會提及：&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;Node.js Ecosystem&lt;/li&gt;
&lt;li&gt;Pure JavaScript Module Development&lt;/li&gt;
&lt;li&gt;NPM(Node.js Package Manager)&lt;/li&gt;
&lt;li&gt;C/C++ Addon Development&lt;/li&gt;
&lt;/ul&gt;
若時間允許，會進一步探討 C/C++ Addon 所衍生的應用範例：&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;System Programming in JavaScript&lt;/li&gt;
&lt;li&gt;Desktop Programming in JavaScript&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
詳細&amp;nbsp;Node.js Taiwan Party&amp;nbsp;聚會訊息如下：&lt;br /&gt;
&lt;br /&gt;
官方公告：&lt;a href=&quot;http://nodejs.tw/post/20631976641/nodeparty20120412&quot;&gt;http://nodejs.tw/post/20631976641/nodeparty20120412&lt;/a&gt;&lt;br /&gt;
時間：2012/04/12 (星期四) 19:30 – 22:00&lt;br /&gt;
地點：台北市大安區金華街 122 號正對面 d.cafe 地下室 W-B05 室（創立方）&lt;br /&gt;
（政治大學城區部，可從旁邊的小門直接進到地下室，找不到的人『&lt;a href=&quot;http://maps.google.com.tw/maps?q=%E9%87%91%E8%8F%AF%E8%A1%97&amp;amp;hl=zh-TW&amp;amp;ie=UTF8&amp;amp;ll=25.03005,121.527371&amp;amp;spn=0.004204,0.00479&amp;amp;sll=23.63446,120.970459&amp;amp;sspn=8.698964,9.810791&amp;amp;brcurrent=3,0x3442a99bd1adbcc7:0xc5ab69bb7491162a,0,0x3442ac6b61dbbd9d:0xc0c243da98cba64b&amp;amp;hnear=106%E5%8F%B0%E5%8C%97%E5%B8%82%E5%A4%A7%E5%AE%89%E5%8D%80%E9%87%91%E8%8F%AF%E8%A1%97&amp;amp;t=m&amp;amp;z=18&amp;amp;layer=c&amp;amp;cbll=25.030066,121.527272&amp;amp;panoid=EzwYe_3ZuWiwEeU7uuVdtA&amp;amp;cbp=12,25.2,,0,17.33&quot;&gt;看圖&lt;/a&gt;』）&lt;br /&gt;
&lt;br /&gt;
有興趣的朋友們，歡迎前來參加，不吝指教。:-)</description><link>https://fred-zone.blogspot.com/2012/04/nodejs-taiwan-how-to-write-nodejs.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>2</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-5787154677718556155</guid><pubDate>Thu, 05 Apr 2012 22:57:00 +0000</pubDate><atom:updated>2012-04-06T07:04:35.574+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Express Web Framwork</category><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">NodeJS</category><category domain="http://www.blogger.com/atom/ns#">Web</category><category domain="http://www.blogger.com/atom/ns#">Web 相關技術</category><category domain="http://www.blogger.com/atom/ns#">程式心得筆記</category><title>用 Node.js 實作多個 Process 監聽並處理同一個 Port</title><description>Node.js 實作了一個內建的模組『cluster』，目的在於提供開發者一個容易的方式，針對多核心機器進行支援。可惜的是，截至今天 Node.js v0.6.14 版本，『cluster』功能還是不夠完整，從官方文件上可以得知，其 API 仍處於實驗(Experimental)階段，在日後會有極大的變動(Drastic changes in future versions)。&lt;br /&gt;
&lt;br /&gt;
而就 git repository 上最新的 Node.js 開發版本來看，『cluster』模組已經大幅度更改，也從簡單的 function call 變成了物件的設計。所以，目前非常不建議在自家產品中大量使用 『cluster』，至少短期內，他都不是穩定可用的，會有很大的改動空間。&lt;br /&gt;
&lt;br /&gt;
不過，你如果是要實作 Web Service，會想要使用『cluster』的目的就顯而易見：『透過多個 Process 分擔流量和工作量』。若只是單單要做到這一點，我們可以自己來實作。&lt;br /&gt;
&lt;br /&gt;
通常我們有兩種方式達成這個需求：&lt;br /&gt;
&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;反向代理(Reverse Proxy) 技術&lt;/li&gt;
&lt;li&gt;使用多個程序(Process) 監聽並處理同一個 Port&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
前者的做法，是建立一個常駐程式或機器，統一處理所有連線的需求，然後將這些連線做流量平衡(Load Balance)，轉送並分配給後端多個程序或機器處理，待處理完成後，再將後端的回應送回給使用者。這樣的 Proxy 機制，你可以用已經相當成熟的解決方案達成，像是 Nginx 或 Apache。亦或是自己使用 Node.js 撰寫，但由於這不是本文的重點，日後再來討論這個實作的細節。&lt;br /&gt;
&lt;br /&gt;
而後者，使用多個程序(Process)&amp;nbsp;監聽並處理同一個 Port，是本文的主題，也是『cluster』模組正在做的事。如果你有看過『cluster』的程式碼，就會發現它其實主要運用了兩個技術：『取得 net._handle』和『利用 process.send() 傳送 Handle 給子行程(child process)』。&lt;br /&gt;
&lt;br /&gt;
知道了關鍵後，實作上就不是什麼大問題，主要流程是：&lt;br /&gt;
&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;運用『net』建立一個 Server，目的在於監聽(listen) 80 Port&lt;/li&gt;
&lt;li&gt;建立多個子行程(child process) 準備處理連線&lt;/li&gt;
&lt;li&gt;將『net』的內建 Handle 傳給子行程(child process)&lt;/li&gt;
&lt;li&gt;在子行程(child process)&amp;nbsp;接收主程序傳來的 Handle&lt;/li&gt;
&lt;li&gt;在建立&amp;nbsp;HTTP Server 時，直接使用主程序傳來的 Handle&lt;/li&gt;
&lt;li&gt;關閉只用來初使化 Handle 的 Server&lt;/li&gt;
&lt;/ol&gt;
&lt;br /&gt;
&lt;br /&gt;
主程式的範例如下：&lt;br /&gt;
&lt;div&gt;
&lt;pre&gt;var child_process = require(&#39;child_process&#39;);
var net = require(&#39;net&#39;);

var tcpSrv = net.createServer();
tcpSrv.listen(80, function() {
&amp;nbsp; &amp;nbsp; for (var i = 1; i &amp;lt;= 4; i++) {
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; var worker = child_process.fork(&#39;worker.js&#39;);
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; worker.send(i, tcpSrv._handle);
&amp;nbsp; &amp;nbsp; }

&amp;nbsp; &amp;nbsp; tcpSrv.close();
});&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
worker.js 的程式：&lt;br /&gt;
&lt;div&gt;
&lt;pre&gt;var http = require(&#39;http&#39;);

process.on(&#39;message&#39;, function(id, handle) {
&amp;nbsp; &amp;nbsp; http.createServer(function(req, res) {
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;res.writeHead(200, {&#39;Content-Type&#39;: &#39;text/plain&#39;});
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;res.end(&#39;Worker &#39; + id);
&amp;nbsp; &amp;nbsp; }).listen(handle);
});&lt;/pre&gt;
&lt;/div&gt;
&lt;br /&gt;
如此，就可以讓所有子行程共同監聽 80 port，透過瀏覽器來看，你應該可以隨機看到『Worker 1』、『Worker 2』、『Worker 3』或『Worker 4』的字樣，分屬不同 Process 回傳的網頁內容。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;後記&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
能這樣使用的原因是，在 &amp;nbsp;Node.js 中，『http』是繼承『net』的衍生實作，所以他們的底層 handle 可以通用。此外，此做法不限於 HTTP Server，也可以在用於一般 TCP Server。&lt;br /&gt;
&lt;br /&gt;
差點忘了提到，Express Web Framework 這類的框架，都是繼承『http』，所以都可以使用相同的做法。</description><link>https://fred-zone.blogspot.com/2012/04/nodejs-process-port.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>3</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-5708147213587635166</guid><pubDate>Tue, 03 Apr 2012 20:44:00 +0000</pubDate><atom:updated>2012-04-04T04:48:18.169+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">NodeJS</category><category domain="http://www.blogger.com/atom/ns#">Web</category><category domain="http://www.blogger.com/atom/ns#">Web 相關技術</category><category domain="http://www.blogger.com/atom/ns#">技術新知</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><category domain="http://www.blogger.com/atom/ns#">雲端(Cloud)</category><title>發佈 AppHouse-Manager 圖型化管理介面！</title><description>繼前文『&lt;a href=&quot;http://fred-zone.blogspot.com/2012/04/apphouse.html&quot;&gt;不靠別人！親手佈建自家的雲端服務平台 AppHouse&lt;/a&gt;』所提到，我們以當今主流的雲端平台的佈建習慣為參考，開發了一個自己的 Hosting Platform，做為投入雲端產業的基礎建設。雖然與許多雲端業界的老前輩（如：Google、Amazon 等等）比起來，仍然不夠看，但研發仍逐步朝完整的解決方案前進。&lt;br /&gt;
&lt;br /&gt;
在使用『&lt;a href=&quot;https://github.com/cfsghost/AppHouse&quot;&gt;AppHouse&lt;/a&gt;』時發現，網站應用程式的佈建雖然相當快速，但若是要直接在 AppHouse 上開發應用程式，會有相當多的麻煩，也不夠便捷。於是，撰寫並釋出了一套圖型化的管理程式『&lt;a href=&quot;https://github.com/cfsghost/AppHouse-Manager&quot;&gt;AppHouse-Manager&lt;/a&gt;』，用來管理 AppHouse 上所有的應用程式(Application)。&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjwSBFwSPsROj1v15AgKKwJoewoARKZqqxh6ozYUQkLokjBXAw8eEpAkpgPowPnb2YPfDODk3CbBl8AdAU_PISHkhfmwJbqcDCBjNVJQ_EDm01eM_7EeOklWqkJCXp3K8Q-CjON1R2dODc/s1600/apphouse-manager_addstatus.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;424&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjwSBFwSPsROj1v15AgKKwJoewoARKZqqxh6ozYUQkLokjBXAw8eEpAkpgPowPnb2YPfDODk3CbBl8AdAU_PISHkhfmwJbqcDCBjNVJQ_EDm01eM_7EeOklWqkJCXp3K8Q-CjON1R2dODc/s640/apphouse-manager_addstatus.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
目前提供了功能，可以重啟特定的應用程式，讓開發者可以在撰寫和測試服務時，依自己的需要重新啟動自己的程式。&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjjughFvsq_kLZ1WE3NGTSfuOT5QceY4Jp_Y74wGiH6jQLzQvoqYcG90nkpG8g1mPTLoSMErH_i7dsg8WaG-HOeF-WJxrkVrFwFv-dhLeq6BavZ6F2yiUcDlrE5WJ4mpwmHUWEooPvruos/s1600/apphouse-manager_login.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;424&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjjughFvsq_kLZ1WE3NGTSfuOT5QceY4Jp_Y74wGiH6jQLzQvoqYcG90nkpG8g1mPTLoSMErH_i7dsg8WaG-HOeF-WJxrkVrFwFv-dhLeq6BavZ6F2yiUcDlrE5WJ4mpwmHUWEooPvruos/s640/apphouse-manager_login.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgcFUhp6QSyYdGymdTkiRZob07iW6aVf1PMcHc-_Hc1F4bRQHVQMC1F7YGVWNPC3w4CKRPxQGFQfvCZw7r00Rw0ddUYHq-YPOiIHN-JENWC_CpnvsbTI6_8ZjJr2fQ_vIYrPGvRyai2OMQ/s1600/apphouse_console.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;424&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgcFUhp6QSyYdGymdTkiRZob07iW6aVf1PMcHc-_Hc1F4bRQHVQMC1F7YGVWNPC3w4CKRPxQGFQfvCZw7r00Rw0ddUYHq-YPOiIHN-JENWC_CpnvsbTI6_8ZjJr2fQ_vIYrPGvRyai2OMQ/s640/apphouse_console.png&quot; width=&quot;640&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
&lt;br /&gt;
也實作了即時監控 Console ，我們可以透過瀏覽器即時偵錯(Debug)。&lt;br /&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
目前 AppHouse-Manager 還相當不足，只能說堪用而已，並還有許多欠缺改進的地方。不過，已經足夠讓工程師在 AppHouse 上進行開發工作。&lt;/div&gt;</description><link>https://fred-zone.blogspot.com/2012/04/apphouse-manager.html</link><author>noreply@blogger.com (Fred Chien)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjwSBFwSPsROj1v15AgKKwJoewoARKZqqxh6ozYUQkLokjBXAw8eEpAkpgPowPnb2YPfDODk3CbBl8AdAU_PISHkhfmwJbqcDCBjNVJQ_EDm01eM_7EeOklWqkJCXp3K8Q-CjON1R2dODc/s72-c/apphouse-manager_addstatus.png" height="72" width="72"/><thr:total>2</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-3945763186679231566</guid><pubDate>Mon, 02 Apr 2012 02:59:00 +0000</pubDate><atom:updated>2012-04-02T13:41:41.160+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Hosting Platform</category><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">NodeJS</category><category domain="http://www.blogger.com/atom/ns#">商業</category><category domain="http://www.blogger.com/atom/ns#">技術新知</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><category domain="http://www.blogger.com/atom/ns#">雲端(Cloud)</category><title>不靠別人！親手佈建自家的雲端服務平台 AppHouse</title><description>現在，凡事只要扯上『雲端』兩字，價值就上漲好多倍，所以，任誰都想狹著『軟體奈米』之勢，大做文章。此外，在大多數人的印象中，雲端就是 Google App Engine(GAE) 、 Amazon E2 或 Microsoft Windows Azure 的代名詞，無論是誰，只要是能將服務放在上面，或是將公司搬到了台北 101 的 Google 樓上，就叫做上了雲端。而那些所謂專家說的 IaaS 、 PaaS  、 SaaS 專有名詞，其實都是舊瓶新酒，只是商業界幫雲端和網路生態的各個部份做了劃分，方便於解釋給投資人或政府單位的『難懂標題』而已。&lt;br /&gt;
&lt;br /&gt;
簡單來說，雲端對一般的企業，最重要的是發展自家應用，提供更多的『加值』且『不需大量人力』的服務，然後對客戶進行『綁樁』；對中間維護伺服器的廠商，就是『租出更多的伺服器』，得到更『大規模的需求和機會』，進一步『降低機房建設和維護的成本』；而對電信單位，就是賣出更多『通信產品』和『頻寬線路』，也能換到更多『國家預算』。&lt;br /&gt;
&lt;br /&gt;
雲端對不同的企業有不同的意義，但對終端的一般企業和使用者來說，建立一個不用擔心擴展性和極限的網路服務，是他的真正價值所在。所以講穿了，雲端並不是什麼新東西，多企業會將服務放在雲端上，求的不過就是為了量化的『擴展性(Scalability)』，如果你很熟悉網路產業，就會發現其實這就是以前，當一個網路服務長大到一定規模，才開始要考慮的事。只是現在這個時代，網路服務搭上了終端實體產品的『量產列車』，許多服務的第一時間規模就相當龐大，所以直接跳過網路服務的發展期，馬上得考量第二步的事。這也是為什麼現在雲端熱哄哄的原因。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;假設業界使用雲端的真正原因，主要是為了追求擴展性，我們不能親手打造嗎？我們是否可以自己建構一個像 Google App Engine(GAE) 的平台，將自己的 Web App 輕鬆佈建在上面？也保留日後擴展的可能性？&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
於是『&lt;a href=&quot;https://github.com/cfsghost/AppHouse&quot;&gt;AppHouse&lt;/a&gt;』應運而生，我們開始投入開發這樣的專案，目前開放原始碼，以 GPL 做為授權而公開。雖然初期是自己使用為主，但這對在國內還未投入實際行動的雲端市場來說，算是一種相當特別的嘗試。如果可以，也希望能找到機會，在國內能建置公開的平台，供業界或軟體開發者使用。&lt;br /&gt;
&lt;br /&gt;
『AppHouse』最終目標是提供一個 Hosting Platform，就如&amp;nbsp;Google App Engine(GAE) 所做的事一般，讓我們可以自己搭建自家的雲端服務平台，並輕鬆將過去使用 Node.js 寫的網站服務或 Web App 佈建在上面。當然，也像 GAE 一樣，能藉由增加 Instance ，達成擴展服務規模的需求。如果你有注意，其實對岸也有人發起類似的專案『&lt;a href=&quot;http://cnodejs.net/&quot;&gt;NAE&lt;/a&gt;』。&lt;br /&gt;
&lt;br /&gt;
相對於許多國際知名的雲端平台，AppHouse 算是新專案，所以各方面功能也許不是這麼完整，初期能達到的功能就只有：&lt;br /&gt;
&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;支援多個 App 同時運行。&lt;/li&gt;
&lt;li&gt;支援沙箱(Sandbox)設計，讓不同 App 可在互相不干擾的情況下獨立運作。&lt;/li&gt;
&lt;li&gt;自動偵側並重起 App。&lt;/li&gt;
&lt;li&gt;支援 Virtual Host 和網域名稱的設定。&lt;/li&gt;
&lt;/ul&gt;
&lt;br /&gt;
&lt;br /&gt;
如果不考慮開放給其他人佈建 App 的情況下，只考慮建構自己的平台，目前 AppHouse 的狀況算是夠用的，由於有沙箱(Sandbox)的設計，也不需要透過虛擬化(Virtualization)技術，就可以搭建多個 App，效能也相當不錯。&lt;br /&gt;
&lt;br /&gt;
日後的方向，會嘗試整合規模化資料庫(Database)的支援和 CDN，讓 AppHouse 能夠有更完整的功能。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;後記&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
無論如何，希望在有心人的資助下，能夠建立起國內第一個&amp;nbsp;Node.js 雲端平台供各界使用。&lt;br /&gt;
&lt;br /&gt;
如果您願意當有心人，麻煩請與我們聯絡！</description><link>https://fred-zone.blogspot.com/2012/04/apphouse.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>5</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-2936740826084322368</guid><pubDate>Thu, 29 Mar 2012 08:45:00 +0000</pubDate><atom:updated>2012-03-29T17:19:10.300+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">JSDX</category><category domain="http://www.blogger.com/atom/ns#">NodeJS</category><category domain="http://www.blogger.com/atom/ns#">系統研發手札</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><title>使用 Node.js 控制網路連線管理員</title><description>既然以『國內首屈一指的 JavaScript 專家』為目標，使用 JavaScript 打造作業系統就是我們的終極目標。未來會將先前的 Flat Project（可參考舊文 &lt;a href=&quot;http://fred-zone.blogspot.com/2011/09/flat-project-demo-os-for-tablets.html&quot;&gt;Flat Project Demo - An OS for Tablets 平板作業系統&lt;/a&gt; 和 &lt;a href=&quot;http://fred-zone.blogspot.com/2011/08/flat-project.html&quot;&gt;Flat Project - 從山寨做起，親手打造炫麗的平板系統&lt;/a&gt;）以 JavaScript 重新架構，實作並提供一個更易於開發應用的環境。當然，就像過去所說，無論有多困難，產品化一直是我們的目標。&lt;br /&gt;
&lt;br /&gt;
既然要開發作業系統，就要讓 JavaScript 能直接觸碰到系統層級或硬體裝置的控制，我們必須實作各種 JavaScript API 去達成這個目的，而前些日子所提及的『&lt;a href=&quot;https://github.com/Shouqun/node-dbus&quot;&gt;node-dbus&lt;/a&gt;』和『&lt;a href=&quot;https://github.com/cfsghost/jsdx-connman&quot;&gt;jsdx-toolkit&lt;/a&gt;』就是在做這類的工作。現在，基於 node-dbus，我們進一步和『&lt;a href=&quot;http://connman.net/&quot;&gt;connman(Connection Manager)&lt;/a&gt;』連接，實作出『&lt;a href=&quot;https://github.com/cfsghost/jsdx-connman&quot;&gt;jsdx-connman&lt;/a&gt;』，提供 JavaScript 網路管理機制的 API。這代表我們可以藉由 jsdx-connman 去控制無線、有線網路，甚至是WiMax、藍芽等各種連線介面。&lt;br /&gt;
&lt;br /&gt;
如果想要嘗試 jsdx-connman，可以直接使用 NPM 安裝：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;npm install jsdx-connman&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
這邊有個範例，讓我們可以使用 jsdx-connman API 去得知無線裝置的狀態和掃描當前環境的 Wifi 無線基地台：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;var ConnMan = require(&#39;jsdx-connman&#39;);

var connman = new ConnMan();
connman.init(function() {
 if (connman.Wifi.Powered)
  console.log(&#39;Wifi is powered&#39;);
 else
  console.log(&#39;Wifi is not powered&#39;);

 if (connman.Wifi.Connected)
  console.log(&#39;Wifi is connected&#39;);
 else
  console.log(&#39;Wifi is not connected&#39;);

 console.log(&#39;Scanning Access Point...&#39;);
 connman.Wifi.Scan(function() {
  connman.Wifi.ListAPs(function(list) {
   console.log(&#39;Got &#39; + list.length + &#39; Access Point(s)&#39;);
   for (var index in list) {
    var ap = list[index];
    if (ap.Name)
     console.log(&#39;[&#39; + ap.Name + &#39;]&#39;);
    else
     console.log(&#39;[*hidden*]&#39;);

    console.log(&#39;Strength: &#39; + ap.Strength + &#39;%&#39;);
    console.log(&#39;Security: &#39; + ap.Security);
    console.log(&#39;&#39;);
   }
  });
 });
});&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;後記&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
身為國內的小軟體公司，我們沒有太多的資源，也沒有太多的設備和環境。所以開發出來的東西不可能盡善盡美，更不可能在所有機器上都能運作良好。如果運作上有遭遇到任何問題，歡迎回報。:-)</description><link>https://fred-zone.blogspot.com/2012/03/nodejs_29.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-2808938601215312463</guid><pubDate>Mon, 26 Mar 2012 10:49:00 +0000</pubDate><atom:updated>2012-03-27T08:51:57.069+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">心情筆記</category><category domain="http://www.blogger.com/atom/ns#">顧問咨詢</category><title>這是兩碼子事！『犛清問題』與『不是我的問題』！</title><description>前一陣子有人寫了文章，以硬體工程師的角度，跳出來罵軟體工程師，因而吵得沸沸揚揚，在各社群網站上也被人重覆轉貼了好一段時間，掀起不少戰文。雖然整篇文章看下來情緒化字眼相當多，在某些部份也有些偏頗甚至太超過。但是不可否認，他的確有提到一個長久以來，許多工程師的通病，無論是軟體工程師還是硬體工程師。&lt;br /&gt;
&lt;br /&gt;
身為工程師或與工程師一同工作的人，應該常聽到或自己本身常說這句話：『不是我的問題。』，就是這經典名句，總是搞得老闆慌、主管氣、工程師們更怒。而對這句話，我特別有感觸，因為過去我身為一個科技業救火員，常跳入火坑滅火，發現很多時候，就是這句話讓什麼問題都解決不了，導致起了大火，又沒人要解決，最後還需要我這外人進去犛清問題。&lt;br /&gt;
&lt;br /&gt;
而在這些救火經歷中，我深刻體會到，證明『不是我的問題』是非常困難的。&lt;br /&gt;
&lt;br /&gt;
一方面，有原罪加身，別人會把你當做在推卸責任。另一方面，因為非常難用『列舉法』去證明自己沒錯，又加上當局者迷，所以，才會有這類『列舉不全』的自我辯解：『我的程式在我的機器上測一點都正常，所以沒問題』、『我測量的電壓和波形都很正常，所以不是我硬體的問題』。其癥結在於，你有在其他電腦環境上測過嗎？你有測過所有工作狀況之下的電壓和波形嗎？大家似乎都忘了高中數學就學過，要找出『向量』以指出方向，要有兩個或更多個點嗎？就算你要用列舉法證明問題不在於你，你也要取樣更多更完整的證據。&lt;br /&gt;
&lt;br /&gt;
所以，很多工程師想盡辦法證明自己沒錯，以為自己是在犛清問題，根本不然。因為『犛清問題』與『不是我的問題』是兩碼子事啊！&lt;br /&gt;
&lt;br /&gt;
我認為，如果你要犛清問題，又證明自己沒錯，『證明是別人的錯』才是最好的方法，並且有助於專案前進和實質意義。&lt;br /&gt;
&lt;br /&gt;
這邊有個自己團隊最近遭遇的小故事與大家分享：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;我們與硬體廠 X 合作開發一個產品，其中發生了一個問題，播放影片時常會鈍鈍卡卡的，此外，因為該產品有觸控面板，如果影片播放中，用手去碰觸觸控面板，影片會立即變很鈍。

由於是 ARM 平台，播放影片都藉助晶片解碼(Decode)，所以起初是找晶片供應商 Y 來查明真相，但是，晶片商『並不認為是自己的問題』，他們認定自己已經出了不少貨，平台夠穩定，所以應該也不可能會有問題。此外，因為觸控面板也會影響影片播放，所以供應商 Y 認定，問題肯定出在 X 廠商因自己動了手腳，加了其他模組或軟體所導致。所以，廠商 X 只好摸摸鼻子，請自己的 RD 找出問題。當然，X 廠商的軟體工程師也不認為是自己的問題，而硬體的人則測了老半天，也不覺得是自己的問題。互相推拖拉的下場，就是最終還是找不出問題所在。

而事實上，這個嚴重問題從去年底我們還沒合作時，他們就已經發現了，直到不久前都還沒解決，時間最少拖了一季。

重點是，既然都不是大家的問題，那問題到底是誰的呢？

為了解決這個問題，最近有個軟體工程師 A 提議，請硬體工程師 B 花幾個小時幫忙，盡可能把所有與影片播放不相干的線路都手動跳線『閹割掉』，想藉由除去硬體設計，反過來證明可能是工程師 B 的問題。而 B 為了真正徹底去掉所有干擾的因素，甚至連各類 Input 裝置（Key button, Touch panel）也都不放過。如此一來，根本沒有操作介面可以控制軟體，讓 A 必須自己想辦法改軟體，去掉一切軟體干擾的因素，使機器啟動後自動只播影片做測試。此外，為了保險起見，A 還特地去要了一塊公板再做一次做同樣的測試。

經過 A B 兩人的共同合作（或是你可以說是互相證明）後，所得到的結果是：『影片播放依然會鈍。』，所以可以推論問題肯定是出在驅動程式或是晶片上。

這時當所有證據指向同一個人，晶片供應商 Y 才願意承認是自己的問題，願意去試著找出問題。然後在深入了解後，才發現是驅動程式給錯了。而 X 廠商因此當了好多個月的冤大頭。&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
從這個小故事可以知道，軟體工程師 A 想出方法要證明是 B 的問題，而 B 不但做得相當徹底，也不給軟體後路，讓軟體必需修改成單純的自動播放。A 與 B 相互證明是對方的問題，結果得到了真正的解答。其中過程，雖然晶片供應商 Y，一直不肯承認錯誤，但被 A B 兩人共同證明並指出後，就不得不低頭。&lt;br /&gt;
&lt;br /&gt;
奇妙的是，最後你會發現，當有爭議的雙方，都共同想證明是對方的問題時，兩方居然會提出同樣類似的意見和方法。其實，大家都知道怎麼去找出問題並解決，只是都不願跨出那一步而已。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;後記&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
說來慚愧，過去我自己也常說：『不是我的問題』，也常看到很多自認聰潁的工程師，總是可以講的一口頭頭是道，撇清自己的問題。可是，一旦發生問題時，這些置身事外的人，都不能幫助專案進展，一點都比不上那些看似笨笨不高明，但努力去證明和犛清問題的工程師們。&lt;br /&gt;
&lt;br /&gt;
最後，願大家都能成為一個用『證明是別人問題』來證明自己清白的工程師。共勉之。</description><link>https://fred-zone.blogspot.com/2012/03/blog-post_26.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>4</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-9151018484485602861</guid><pubDate>Thu, 22 Mar 2012 23:16:00 +0000</pubDate><atom:updated>2012-03-23T22:26:39.573+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">NodeJS</category><category domain="http://www.blogger.com/atom/ns#">程式心得筆記</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><title>能處理 Binary Data 的 Node.js Buffer Class</title><description>從一開始 JavaScript 就被設計成易於處理 Unicode 的語言，其所有的動作，都無法直接操作 Binary 類型的資料。也就是說，JavaScript 對處裡一個個位元組(byte)的資料是沒輒的，因此使用範圍大幅受限。為了解決這個問題，Node.js另外設計了一個 Buffer class，讓開發者可以處理 Binary data。&lt;br /&gt;
&lt;br /&gt;
為了方便理解，其實我們可以將 buffer class 看做為 C 語言中的 malloc()，使用它就像是和直接系統要一塊原始記憶體來使用。此外，因為 buffer class 被設計成在 V8 heap 之外配置記憶體，因此不受限於 V8 Engine heap 的 1.6GB 大小限制。&lt;br /&gt;
&lt;br /&gt;
你可以用下列三種方式建立 Buffer 物件：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;/* 建立 16 Bytes 的記憶體空間 */
var buffer = new Buffer(16);

/* 直接代入資料陣列 */
var buffer = new Buffer([ 8, 8, 6, 9, 2, 6, 3, 3, 3, 5, 7, 2, 1, 1, 1 ]);

/* 直接代入字串 */
var buffer = new Buffer(&#39;String!&#39;, &#39;utf-8&#39;);
var buffer = new Buffer(&#39;String!&#39;, &#39;ascii&#39;);&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
你可以寫入資料：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;var buffer = new Buffer(32);

/* 寫入一段字串 */
buffer.write(&#39;Write something&#39;, &#39;utf-8&#39;);

/* 只寫入兩個字元 */
buffer.write(&#39;Write something&#39;, 2, &#39;ascii&#39;);&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
以操作陣列(Array)的方法，單獨存取每個 Byte 的資料：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;var buffer = new Buffer(32);

/* 將每個 byte 歸零 */
for (var i; i &amp;lt; buffer.length; i++) {
    buffer[i] = 0;
}&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
當然也有像 memcpy() 的資料複製：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;var buffer1 = new Buffer(&#39;Source!&#39;);
var buffer2 = new Buffer(32);

/* buf.copy(targetBuffer, [targetStart], [sourceStart], [sourceEnd]) */
buffer1.copy(buffer2, 0, 0, 6);&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;後記&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
更多的功能，可以參考官方的 API 文件： &lt;a href=&quot;http://nodejs.org/api/buffer.html&quot;&gt;http://nodejs.org/api/buffer.html&lt;/a&gt;</description><link>https://fred-zone.blogspot.com/2012/03/binary-data-nodejs-buffer-class.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>7</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-2671987577243417290</guid><pubDate>Wed, 21 Mar 2012 23:40:00 +0000</pubDate><atom:updated>2012-03-22T07:53:14.246+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">DBus</category><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">NodeJS</category><category domain="http://www.blogger.com/atom/ns#">NPM</category><category domain="http://www.blogger.com/atom/ns#">自由軟體</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><title>讓我們用 Node.js 與 DBus 打交道</title><description>一個多工的作業系統，最不可或缺的機制就是 IPC，應用程式互相溝通的管道。但在比較複雜的桌面應用，系統核心本身的 IPC 就顯得太過簡單，不敷使用。而『DBus』是一個被廣泛使用的 IPC 機制，擁有權限控管、標準的 Interface 甚至是可跨網路溝通等特性，讓應用程式只要遵循標準的資料結構，就可以將資訊傳遞到另一支程式手上。DBus&amp;nbsp;尤其是在桌面環境下被使用最多，從網路管理員(Network Manager)、藍芽(Bluetooth)連線管理、亮度/音量變更等各式更新訊息通知(Notification)機制，無一不用到他。但由於 DBus 有眾多複雜功能，撰寫程式去使用他其實不是這麼容易，所以若是能用 JavaScript 來做這項工作，肯定能加快許多。&lt;br /&gt;
&lt;br /&gt;
對 JavaScript 而言，打通了 DBus，意謂著可以控制網路管理員，去建立所有類型的網路連線（無論是有線、無線、需要撥號或 VPN），亦可以連接 iBus（當前主流的輸入法框架）以支援並控制輸入法，甚至是可以控制絕大多數的系統服務，說是打通任督二脈可是一點也不為過。&lt;br /&gt;
&lt;br /&gt;
上網尋找，其實已經有一些人嘗試在為 Node.js/V8 Engine 寫 DBus 的支援，只是多半都不夠完整或 API 的使用上過於複雜，這讓筆者萌生了要自己開發一個新的 Dbus 模組的念頭。但重新開發實在是太累人，於是在網路上眾多的現成品中，挑選了一個 API 比較易於使用的專案『&lt;a href=&quot;https://github.com/Shouqun/node-dbus&quot;&gt;node-dbus&lt;/a&gt;』，然後為他加上更完整的功能，目前已經小有成果，除了提交回 upstream 外，也已經發佈到 NPM 上。&lt;br /&gt;
&lt;br /&gt;
如果想要使用，你可以直接以 NPM 安裝（註：NPM上註冊的名稱和github上不太一樣）：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;$ npm install dbus&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
其使用上相當簡單，建立一個自己的 DBus Service 範例如下：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;var dbus = require(&quot;dbus&quot;);

dbus.start(function() {

 var service_path = &#39;org.freedesktop.DBus.TestSuitePythonService&#39;;
 var object_path = &#39;/org/freedesktop/DBus/TestSuitePythonObject&#39;;
 var interface_path = &#39;org.freedesktop.DBus.TestSuiteInterface&#39;;

 session = dbus.session_bus();

 var dbusRegister = new dbus.DBusRegister(dbus, session);

 /* Export Methods */
 var Methods = {
  WhoAreYou: function () {
   return &#39;I AM Spiderman!&#39;;
  }
 };

 /* Register a Bus Name */
 dbus.requestName(session, service_path);

 /* Register Methods */
 dbusRegister.addMethods(object_path, interface_path, Methods);
 dbusRegister.addMethods(object_path, &#39;org.freedesktop.DBus.Introspectable&#39;, {
  Introspect: function() {
   return &#39;&amp;lt;?xml version=\&quot;1.0\&quot; encoding=\&quot;UTF-8\&quot; ?&amp;gt;&#39; +
    &#39;&amp;lt;node name=\&quot;&#39; + object_path + &#39;\&quot;&amp;gt;&#39; +
    &#39;&amp;lt;interface name=\&quot;&#39; + interface_path + &#39;\&quot;&amp;gt;&#39; +
    &#39;&amp;lt;method name=\&quot;WhoAreYou\&quot;&amp;gt;&#39; +
    &#39;&amp;lt;arg name=\&quot;data\&quot; type=\&quot;s\&quot; direction=\&quot;out\&quot; /&amp;gt;&#39; +
    &#39;&amp;lt;/method&amp;gt;&#39; + 
    &#39;&amp;lt;/interface&amp;gt;&#39; +
    &#39;&amp;lt;interface name=\&quot;org.freedesktop.DBus.Introspectable\&quot;&amp;gt;&#39; +
    &#39;&amp;lt;method name=\&quot;Introspect\&quot;&amp;gt;&#39; +
    &#39;&amp;lt;arg name=\&quot;data\&quot; type=\&quot;s\&quot; direction=\&quot;out\&quot; /&amp;gt;&#39; +
    &#39;&amp;lt;/method&amp;gt;&#39; + 
    &#39;&amp;lt;/interface&amp;gt;&#39; +
    &#39;&amp;lt;/node&amp;gt;&#39;;
  }
 });

 /* Loop to waiting for somebody to call */
 dbus.runListener();
});&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
寫一支 Client 程式，透過 DBus 去使用前一支程式所提供的 WhoAreYou() 函式：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;var dbus = require(&quot;dbus&quot;);

dbus.start(function() {

 session = dbus.session_bus();

 interface = dbus.get_interface(session, 
  &quot;org.freedesktop.DBus.TestSuitePythonService&quot;, 
  &quot;/org/freedesktop/DBus/TestSuitePythonObject&quot;, 
  &quot;org.freedesktop.DBus.TestSuiteInterface&quot;);

 console.log(interface.WhoAreYou());
});&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
相較於用 C 寫 DBus 程式，這樣應該更簡單吧。當然，還有更簡化的空間，會在後續版本中改進。:-)&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;後記&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
最近一直有人問我，花這麼多時間去開發這麼多模組，只為了銜接各種 Library，那為何不直接使用 gir？&lt;br /&gt;
&lt;br /&gt;
我的回答是，JavaScript 有自己的特性和慣用邏輯，如果只是將各個原本用 C/C++ 語言習慣所設計出來的 API 引入到 JavaScript，那和直接寫 C/C++ 語言去使用那些 Library，在開發難度上有何不同？雖然可以讓 JavaScript 得到很多支援，但對實際應用上卻沒有太大幫助。別忘了，使用 JavaScript 就是要享受無痛開發的樂趣。:-D</description><link>https://fred-zone.blogspot.com/2012/03/nodejs-dbus.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>4</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-4201998206301740718</guid><pubDate>Tue, 20 Mar 2012 02:17:00 +0000</pubDate><atom:updated>2012-03-20T10:29:00.445+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">C/C++</category><category domain="http://www.blogger.com/atom/ns#">Desktop</category><category domain="http://www.blogger.com/atom/ns#">Embedded System</category><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">NodeJS</category><category domain="http://www.blogger.com/atom/ns#">Web</category><category domain="http://www.blogger.com/atom/ns#">Web 相關技術</category><category domain="http://www.blogger.com/atom/ns#">商業</category><category domain="http://www.blogger.com/atom/ns#">心得分享、講座</category><category domain="http://www.blogger.com/atom/ns#">心情筆記</category><category domain="http://www.blogger.com/atom/ns#">手機平台</category><category domain="http://www.blogger.com/atom/ns#">有趣新構思</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><category domain="http://www.blogger.com/atom/ns#">顧問咨詢</category><title>我為什麼大舉投入 JavaScript 的相關開發</title><description>和我比較熟的人，最近應該都知道，目前我專注於 JavaScript 和 Google V8 engine 的相關研究上，甚至決定從今年開始，公司營運以及技術團隊的方向，是企圖提供『最專業的 Node.js 和 JavaScript 相關服務』。（所以，如果您有需要，請聯絡我們&amp;nbsp;:-)&amp;nbsp;）我非常清楚知道，有些人聽到 JavaScript 就歡天喜地，而有些人則不將他當一回事。更有多到數不清的技術人員，熟悉並使用 JavaScript 非常多年，聽到我說要提供『最專業的 JavaScript 相關服務』，會不屑一顧甚至以為我在瘋言瘋語。不過，如果你願意聽，接下來我會說明這是怎麼樣的瘋狂服務。&lt;br /&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
我不能阻止你對 JavaScript&amp;nbsp;有預設立場，因為這是無奈的歷史包袱，但請暫時放下過去成見，聽我靡靡道來。&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;b&gt;我所遭遇的矛盾&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
過去，絕大多數人都只將 JavaScript 視為 Web 開發的一環，在大家的印象和認知中，JavaScript 充其量只是一個在瀏覽器中控制著 HTML DOM 的腳本語言。雖然近年來出現了 Node.js，過去微軟也支援使用者使用 JavaScript 撰寫 ASP 和系統腳本程式，但也不改這樣的看法：『JavaScript 是身處於末端的語言，與網頁、網站密不可分的東西』。所以，對許多硬底子的企業和研發人員來說，JavaScript 象徵了 Web/HTML 與不彰的效能，一種幻想的存在；而對很多 Web 開發者和前端工程師來說，JavaScript 卻是一種當今資訊產業的萬靈丹。如此天差地遠的矛盾，一直存在於這個產業界。&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
曾經當過 Web 開發者一段不少的時間，我承認也理解，使用 Web 技術來實作各類使用者界面(User Interface)和資料庫應用，非常容易和快速，不用考慮到太多的事。這一切要歸功於 JavaScript 和 HTML 的合作無間。所以，不時能聽到一些 Web 開發者，嘲笑作業系統程式開發者和嵌入式裝置的技術人員的辛苦，認為許多功能和特效，使用 Web 技術一下就達成了。&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
由於我過去也是『慣C』一族，做過很多包羅萬象的硬碰硬開發，小從一般電腦到嵌入式裝置、桌面系統到手機平版，大到各類特殊需求的系統研發，都有所涉獵。所以，對於系統資源和效能，一直有著莫明的堅持。也因為長期的磨練，深知所有的軟體開發，都在找尋與硬體拉距戰的平衡點。如何在有限的系統資源上，呈現最好的效果，一直都是我所在追求的事。&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
這兩種極端的軟體開發，開發效率和執行效能互為優缺點，讓兩方人馬各持己見。但因為同時身為這兩種人，產業界的矛盾，卻直接在我心中打架。&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;b&gt;尋求同時兼顧開發效率和軟體效能的解決方案&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
其實一直以來，非常希望尋找一個語言，能快速又無痛的去開發各種應用，又不失效能、彈性和硬體、系統的耦合度。我知道，這無疑是天方夜談，但是若是一種語言沒辦法達成，如果用兩種語言配合，只要能達到這需求，又有何不可？&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
因此，Node.js 和 Libjs 這類的開放源始碼專案，給我很大的啟發。藉由輕量化，分離 JavaScript Engine 和其效能最大瓶頸 - 『瀏覽器』，使 JavaScript 非常適用於各種領域。許多非關效能的事，可以交給 JavaScript 來做，而需要效能、與硬體和系統溝通的工作，可以交給 C/C++ 來處理。兩者只要之間只要定好模組規範，即可兼顧開發效率和彈性及效能。JavaScript 因此可得到以往完全無法想像的發展空間，又能避免使用 C/C++ 辛苦的開發過程，加速程式應用的研發。&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
如舊文『&lt;a href=&quot;http://fred-zone.blogspot.com/2011/11/nodejs.html&quot;&gt;我如何看待 NodeJS&lt;/a&gt;』所提及：『JavaScript 已經可以和 Python 相提並論』，基本上，我們已經可以用 JavaScript 開發各式應用，類似的模組機制已經與 Python 趨於相同，但所佔的系統資源則相對少很多，效能甚至更勝一籌。（效能可以參考國外有人做的比較：&lt;a href=&quot;http://blog.famzah.net/2010/07/01/cpp-vs-python-vs-perl-vs-php-performance-benchmark/&quot;&gt;C++ vs. JavaScript vs. Java vs. Python vs. Perl vs. PHP performance benchmark&lt;/a&gt;）。&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;b&gt;對於 JavaScript 來說，瀏覽器不再是束縛而是選項&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
或許有人說，JavaScript 若捨棄了瀏覽器即捨棄掉了最大的優點。但在我的觀點來看，瀏覽器不是被捨棄了，而只是成為了『用 JavaScript 開發使用者介面』的選項之一，若你願意，仍可繼續使用。但為了讓研發使用者介面有更輕量節省資源的選擇，前些日子，我才開發了『node-clutter』（現已改名成為 jsdx-toolkit），做為沒有瀏覽器後，使用 JavaScript 開發 OpenGL/GLES 程式和使用者介面的替代方案。&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;b&gt;適用領域&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
如今的 JavaScript&amp;nbsp;已和過去不可同日而語，可以用其開發的領域幾乎已經沒有受限。&lt;/div&gt;
&lt;div&gt;
&lt;ol&gt;
&lt;li&gt;Web 後端和資料庫系統應用（可取代 Python/Perl/PHP/JSP/ASP/.NET 等技術）&lt;/li&gt;
&lt;li&gt;桌面和手機應用程式（Desktop/Mobile Application）&lt;/li&gt;
&lt;li&gt;手機或平板系統（如取代 JVM 層，並撰寫像 Android Framework 一般的系統框架）&lt;/li&gt;
&lt;li&gt;嵌入式系統（如同手機系統一般，除驅動程式之外的任何研發）&lt;/li&gt;
&lt;li&gt;雲端服務（有 Scalable 的需求）&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;b&gt;我相信的新一代&amp;nbsp;JavaScript 解決方案&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
這樣新一代的 JavaScript 解決方案，有數不清的優點可以一一詳列，但最重要的有三點：&lt;/div&gt;
&lt;div&gt;
&lt;ol&gt;
&lt;li&gt;使各類嵌入式系統開發更為快速，尤其與使用者介面(User Interface)相關的。&lt;/li&gt;
&lt;li&gt;滿街數不清的 JavaScript 開發者都可以晉升成後端甚至系統工程師， 不再只是末端應用的開發人員。&lt;/li&gt;
&lt;li&gt;統一前後端的開發經驗，讓研發人員在這雲端時代，可以更專注於應用的設計。&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div&gt;
同時也打破了過去業界中存在的矛盾與迷思：&lt;/div&gt;
&lt;div&gt;
&lt;ol&gt;
&lt;li&gt;JavaScript 和瀏覽器的肥大是共存。&lt;/li&gt;
&lt;li&gt;JavaScript 除了用來做 Web 和資料庫系統相關應用之外，不能變成產品化的軟體。&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div&gt;
換言之：&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;對企業來說：加速開發，省錢，再省錢。&lt;/li&gt;
&lt;li&gt;對傳統 Web 開發者：更有發展空間，更有實際價值。&lt;/li&gt;
&lt;li&gt;對硬底子開發者：讓自己的底層成果能更快被看的見。&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;b&gt;那麼，瘋狂服務？&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
既然 &amp;nbsp;JavaScript 的發展是大勢所趨，未來我們會延續過去的各種經驗，再全面引入 JavaScript，做為自身所有軟體開發的基礎，以 JavaScript 整合並加速產品開發的效率和品質，凡舉雲端服務、Web 、資料庫應用、桌面和手機應用程式、作業系統和嵌入式系統，都是我們將提供的專業服務範圍。&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
目前，我們已經對 Node.js 和 V8 Engine 有相當程度的瞭解和深入。除了著墨在雲端相關技術的發展，也有不少在作業系統底層和嵌入式裝置，引入 JavaScript 軟體架構的開發經驗。身為 Open Source 開發者，當然也會不定期貢獻成果給上游或釋出相關原始程式碼。&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
嚴格來說，成為『JavaScript&amp;nbsp;軟體平台的專家』才是我們的目標，所以我們才說，希望提供『國內最專業的 JavaScript 相關服務』。&lt;/div&gt;</description><link>https://fred-zone.blogspot.com/2012/03/javascript.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>13</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-8864287673484647520</guid><pubDate>Fri, 16 Mar 2012 12:40:00 +0000</pubDate><atom:updated>2012-03-16T20:40:20.565+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">NodeJS</category><category domain="http://www.blogger.com/atom/ns#">程式心得筆記</category><title>Node.js Callback Function 不成文的習慣</title><description>我們常會在一些 Node.js 或是第三方模組的 API 上，看到 callback function 的第一個參數是 err 的情況，雖然官方並沒有明文規定（或許有，只是我沒有看到？），但這樣的習慣已經隨處可見。從官方 API 文件中的範例，就能看到許多例子：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;fs.rename(&#39;/tmp/hello&#39;, &#39;/tmp/world&#39;, &lt;b&gt;function (err)&lt;/b&gt; {
  if (err) throw err;
  console.log(&#39;renamed complete&#39;);
});

fs.stat(&#39;/tmp/world&#39;, &lt;b&gt;function (err, stats)&lt;/b&gt; {
  if (err) throw err;
  console.log(&#39;stats: &#39; + JSON.stringify(stats));
});&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
這樣的情況，最常出現在非同步(asynchronous)執行的函式中。因為這類的函式呼叫，做法是將工作丟到背景等待完成，所以會立即回傳(return)，並繼續執行下面的程式。而該函式的工作，在真正完成或有出錯時，會呼叫 callback function。所以，在 callback function 內，我們需要藉由判斷 err 存在與否，去得知該函式是否完成工作或出錯。&lt;br /&gt;
&lt;br /&gt;
至於 err 是什麼格式，對大多開發者來說，他只是個純字串的存在，也就是你可以直接印出它的內容：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;console.log(err)&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
但事實上，err 是 Error 物件，這是 ECMA 有定義的標準物件，對一些有經驗的 JavaScript 開發者來說，應該不陌生，甚至除了 Node.js 之外，在其他的瀏覽器上都有支援。如果想知道他是什麼東西，也可以自己嘗試建立一個 Error 物件來觀察：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;var err = new Error(&#39;Hello Error!&#39;);&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
比較有趣的是，Error 物件被建立時，會包括 JavaScript Engine Stack 的資訊，這有利於更進一步的 Debug 工作。&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;console.log(err.stack)

Error: Hello Error!
    at repl:1:6
    at REPLServer.eval (repl.js:80:21)
    at Interface.&lt;anonymous&gt; (repl.js:182:12)
    at Interface.emit (events.js:67:17)
    at Interface._onLine (readline.js:162:10)
    at Interface._line (readline.js:426:8)
    at Interface._ttyWrite (readline.js:603:14)
    at ReadStream.&lt;anonymous&gt; (readline.js:82:12)
    at ReadStream.emit (events.js:88:20)
    at ReadStream._emitKey (tty.js:327:10)&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
所以，如果你正在設計一個擁有 callback 的函式，比較好的做法就是遵循這樣有 Error 物件的設計模式。&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;function waitingforyou(callback) {
    /* Do something */
    data = doSomething();

    if (!data)
        callback(new Error(&#39;Something\&#39;s Wrong&#39;));
    else
        callback(null, data);
}&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
&lt;b&gt;後記&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
JavaScript 有太多彈性可以讓我們把他搞的亂七八糟，如果不是故意要惡搞，建立一些設計習慣，才是好的選擇。</description><link>https://fred-zone.blogspot.com/2012/03/nodejs-callback-function.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-2845639893900177361</guid><pubDate>Tue, 06 Mar 2012 06:27:00 +0000</pubDate><atom:updated>2012-03-06T14:27:01.286+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">NodeJS</category><category domain="http://www.blogger.com/atom/ns#">程式心得筆記</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><title>探討 Node.js 的非同步機制</title><description>Node.js 標榜著事件驅動(Event Drive)的設計，也因為如此，它在網站應用程式的領域上，通常強調有最快速的即時反應。其原理是利用 libev 去實作事件輪詢，不斷的檢查是否有事件需要被處理，一旦發現有事件在待命，就去執行並觸發相應的 Handler。但無可避免的，總會有程式和工作需要佔用大量的 CPU 時間，因此獨佔目前的事件處理程序，造成整個程式被單一事件卡死。這樣的問題，有機會讓原本期望的即時反應機制崩潰。&lt;br /&gt;
&lt;br /&gt;
同樣的問題，也出現在其它 non-blocking 設計的 Framework。Facebook&amp;nbsp;所開發，也紅過一陣子的 Tornado Web Framework，針對這個問題，就提供了一個 Decorator &amp;nbsp;-『@tornado.web.asynchronous』，這可以讓 Handler 處於非同步的模式下執行，意味著該段程式可以先丟到背景，而不會讓整個程式為了等待該 Handler 結束，而耗時太久或卡死。&lt;br /&gt;
&lt;br /&gt;
相同的，Node.js 也提供擁有類似的設計，只不過對於語言的使用者來說，概念和用法上不太一樣，因為 Node.js 所提供的 API 目標並不只是單純處理 Web Server 的應用，更像是低階的工作排程器的控制，如作業系統上的 yield() 或 sched_yield()。&lt;br /&gt;
&lt;br /&gt;
我們可以很快的透過一個簡單的例子，去理解怎麼使用他，假設我們開一個檔案讀資料，一般邏輯上的做法（這裡所用的 file 物件是假的，在真實的 Node.js API 中並不存在，只是為了說明方便）：&lt;div&gt;&lt;pre&gt;file.open();
while(true) {
&amp;nbsp; &amp;nbsp;&amp;nbsp;result = file.read();
&amp;nbsp; &amp;nbsp; if (!result)
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; break;

&amp;nbsp; &amp;nbsp; /* Do something... */
};

console.log(&#39;blah blah blah...&#39;);&lt;/pre&gt;&lt;/div&gt;若是這個檔案很大，需要花三分鐘才能讀完，那這個 while 迴圈肯定會鎖死，等讀完後才顯示『blah blah blah...』字樣。想想看，若是這樣的程式被放在 Web Framework 的 Handler 中，肯定會因為這一個使用者，讓其他人三分鐘之內，都無法使用這個網站服務。而實際上，這樣的問題最常出現於檔案上傳的工作上。&lt;br /&gt;
&lt;br /&gt;
若是我們可以用process.nextTick()來改寫，則情況會大不同：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;function readLoop() {
&amp;nbsp; &amp;nbsp;&amp;nbsp;result = file.read();
&amp;nbsp; &amp;nbsp; if (!result)
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; return;

&amp;nbsp; &amp;nbsp; /* Do something... */
&amp;nbsp; &amp;nbsp;&amp;nbsp;process.nextTick(readLoop);
}

file.open();
process.nextTick(readLoop);
console.log(&#39;blah blah blah...&#39;);&lt;/pre&gt;&lt;/div&gt;結果是『blah blah blah...』字樣會立即出現，檔案讀取工作都會被分段排在每次的排程，這讓事件引擎得以喘口氣，處理其他的工作和事件，整個服務也不會因為單一工作而受阻中斷。&lt;br /&gt;
&lt;br /&gt;
或許你已經發現到了，這樣的做法，其實與過去在瀏覽器製作動畫效果的方法雷同，運用 setTimeout() 去重覆一個會佔用大量時間的工作。為了便於理解，你也可以將它想像成&amp;nbsp;setTimeout(fn, 0) 的取代，只不過&amp;nbsp;process.nextTick() 的執行上會更加有效率，這點在 Node.js 的官方文件裡也有特別說明。&lt;br /&gt;
&lt;br /&gt;
如果你有興趣要瞭解&amp;nbsp;process.nextTick() 是怎麼被實現的，就必需從Node.js 的設計根本面來著手。前面提到，Node.js事件驅動(Event Drive)主要實做於 libev 的基礎之上，Node.js 實作了一個佇列(Queue) 存放事件，而所有的事件觸發，都是由一個輪詢事件的引擎所驅動&amp;nbsp;。process.nextTick() 就是讓我們可以把程式事件放到佇列(Queue)，使其在下一次事件輪詢時被驅動。&lt;br /&gt;
&lt;br /&gt;
你可以參考 Node.js 原始程式碼中的 src/node.js，得到此 API 的實作：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;&amp;nbsp; &amp;nbsp; process._tickCallback = function() {
&amp;nbsp; &amp;nbsp; &amp;nbsp; var l = nextTickQueue.length;
&amp;nbsp; &amp;nbsp; &amp;nbsp; if (l === 0) return;

&amp;nbsp; &amp;nbsp; &amp;nbsp; var q = nextTickQueue;
&amp;nbsp; &amp;nbsp; &amp;nbsp; nextTickQueue = [];

&amp;nbsp; &amp;nbsp; &amp;nbsp; try {
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; for (var i = 0; i &amp;lt; l; i++) q[i]();
&amp;nbsp; &amp;nbsp; &amp;nbsp; }
&amp;nbsp; &amp;nbsp; &amp;nbsp; catch (e) {
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if (i + 1 &amp;lt; l) {&lt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; nextTickQueue = q.slice(i + 1).concat(nextTickQueue);
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; if (nextTickQueue.length) {
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; process._needTickCallback();
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; throw e; // process.nextTick error, or &#39;error&#39; event on first tick
&amp;nbsp; &amp;nbsp; &amp;nbsp; }
&amp;nbsp; &amp;nbsp; };
&amp;nbsp; &amp;nbsp; process.nextTick = function(callback) {
&amp;nbsp; &amp;nbsp; &amp;nbsp; nextTickQueue.push(callback);
&amp;nbsp; &amp;nbsp; &amp;nbsp; process._needTickCallback();
&amp;nbsp; &amp;nbsp; };&lt;/pre&gt;&lt;/div&gt;更多細節事件引擎的細節，讀者可以自行參考 Node.js 的原始程式，就不在本文討論了。</description><link>https://fred-zone.blogspot.com/2012/03/nodejs.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-3732504496558729307</guid><pubDate>Sun, 04 Mar 2012 03:25:00 +0000</pubDate><atom:updated>2012-03-04T13:51:00.290+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Cloud</category><category domain="http://www.blogger.com/atom/ns#">心情筆記</category><title>玩什麼雲端？</title><description>雲端這個詞已經吵得鬧哄哄，也有更多人為了這個『形容詞』下了太多的定義，到底什麼是雲端已經變得不重要，也無法三言兩語說的清楚。身為一個資訊產業的創業小老弟，其實並不期待雲端能帶給資訊業如何的革命和新生態，只視為這是一次新契機，讓政府和台灣科技業的老大們，能靜下心傾聽。&lt;br /&gt;
&lt;br /&gt;
我們暫且不需要討論雲端的格局觀，因為這類的畫餅，大從國家政策，小到百姓家常，已經太多了。我們有預算，有政策，有土地，有銀行，有股票，什麼都不缺，只缺少夠多的實際作為而已。其實，回顧過去，可以看到許多事我們現在可以做，但是大多數人，多半用美國矽谷的角度探討當今的資訊產業，我認為，那無法幫助台灣人省視情勢，因為有太多經驗不是在台灣的業界可以仿效或改進的。所以，既然人在台灣，當以台灣出發的角度，省視這些年來的發展過程，因此，筆者將以自己在台灣的經驗，嘗試省視這個產業，雖可能不算準確，卻是一種不同角度的嘗試。&lt;br /&gt;
&lt;br /&gt;
的確，筆者剛滿 26 歲，年紀不足，在商業上，經驗也不算多。但在上一個世紀末，卻曾趕上過十幾年前 .COM 的最後一班列車。至今，看著當年遺留下來的無價紀念品，那些重量級的網域名稱，是花再多錢都無法獲得的一種經驗，令筆者有著無限的感慨和體會。說實在，如果沒這樣做過，永遠不會理解，不受金錢誘惑，堅持自己認為正確的事，然後去惡搞並使用那些當年叫價上億美元的網域名稱，是多麼痛快的事。&lt;br /&gt;
&lt;br /&gt;
很多人會說我是忌妒，眼紅看著在十多年前還是小學生時，曾經發想和完成過的東西，諸如網路相簿、Blog、購物車、集體購物等，還有更多當時翹課躲書店而寫出來的服務，在今天一一出現或成功於網路上。事實上，比起心中的不平衡，我開心自己從中學到更多當時沒有看清楚的事，讓今天的視野可以更開闊。&lt;br /&gt;
&lt;br /&gt;
還記得當時，所有人對網際網路(Internet)都有一種特別的期待，就像今天的雲端(Cloud)魔力一樣。全世界的人也都像現在一樣，砸進了無數的人力物力，在商業界，更是起了許多不得了的名目，打算大賺一筆。然而，絕大部份獲得勝利的人是誰？不是那些有錢有勢的企業和組織，而是真正做事和成本相對低的車庫公司。在某方面來說，這些大企業意外吃了不少悶棍。&lt;br /&gt;
&lt;br /&gt;
從國內的發展來看，雖然在網路發達的今天，網路速度已經不再是大問題，架設網站的成本也不是太高。你可能很難想像當年，要是你有著一條&amp;nbsp;64K 專線，都可以成為台灣 ISP 公司的情況。也因為如此，當時國內許多想做網站服務的人，都因線路和機房成本，發展受限，或敗陣下來。能夠挺下來的，很多都是後臺夠硬。所以，絕大部份的台灣人都無法想像，後來GMail/Youtube那樣吃大頻寬和大儲存容量的服務，怎麼有人敢想敢做。&lt;br /&gt;
&lt;br /&gt;
不過在這樣的困境下，一些利用學校網路資源的人，一一起家並做大的例子，時有所聞，更常從國外突然掘起的網站服務公司身上看到。從過去的新聞媒體中，也可以找到很多的實例。從這樣的現象不難發現，其實人人都有想法，只是門檻過高而缺少舞台發揮，不只如此，若是在網路環境慢人家幾年的台灣，更是難以有未來。&lt;br /&gt;
&lt;br /&gt;
反觀今天的雲端產業，雖然炒作聲響大，為何大多數有想法的人，依然選擇租一般主機，實作自己的想法，以碰運氣的心態去等待成就，直到真的有些回饋並找到錢，才去打掉重練，開始準備放到大家稱為『雲端』的伺服器上？甚至，根本不考慮『雲端』的解決方案。相同的道理，因為門檻太高，尤其是價格門檻。我們根本不能奢望新創公司或是年輕的學生，可以負擔這樣昂貴的費用，去使用所謂的雲端主機。既然沒有人會去用，政府花再多錢協助企業蓋再多『蚊子機房』也是沒有意義。&lt;br /&gt;
&lt;br /&gt;
同樣的門檻問題，至今還是沒有解決，當年身為沒有經濟能力的小學生是那樣，現在長大後自己創業後也是這樣，我們正在步上過去同樣的路，身為『雲端機房』的需求者，看著『蚊子機房』放在那只能痛心。&lt;br /&gt;
&lt;br /&gt;
至少個人認為，想要在台灣發展雲端重鎮，就要先將過去我們輸掉的時間先追回來。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;後記&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
問題當然不如此單一，只是讓真正需要雲端來發展的人，可以如願使用，比什麼都還來得重要。</description><link>https://fred-zone.blogspot.com/2012/03/blog-post.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-8586598271295488010</guid><pubDate>Sat, 25 Feb 2012 06:54:00 +0000</pubDate><atom:updated>2012-03-22T17:12:33.666+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">C/C++</category><category domain="http://www.blogger.com/atom/ns#">Clutter</category><category domain="http://www.blogger.com/atom/ns#">Desktop</category><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">JSDX</category><category domain="http://www.blogger.com/atom/ns#">Linux</category><category domain="http://www.blogger.com/atom/ns#">NodeJS</category><category domain="http://www.blogger.com/atom/ns#">OpenGL</category><category domain="http://www.blogger.com/atom/ns#">User Interface</category><category domain="http://www.blogger.com/atom/ns#">技術新知</category><category domain="http://www.blogger.com/atom/ns#">自由軟體</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><title>那一年，我們一起用Node.js開發3D使用者介面！</title><description>曾經想要用 Node.js 開發一些原生的應用程式或是 OpenGL 程式，但無論怎麼問，無所不答的 Google 大神都給我千篇一律的答案：『使用 HTML5 和 WebGL』。這答案其實不令人意外，因為截至目前為止，大多數學習使用 Node.js 的人都是 Web 開發者，其所提出的解決方案，理所當然都是與『瀏覽器』有關。想來有趣，Node.js 就是『與瀏覽器剛分手的 JavaScript Engine』，為何還要回頭與瀏覽器糾纏不清呢？於是，筆者開發並於 GitHub 釋出了一個 Node.js module - 『&lt;a href=&quot;https://github.com/cfsghost/jsdx-toolkit&quot;&gt;jsdx-toolkit&lt;/a&gt;』，讓 JavaScript 開發人員，可以輕易使用 Node.js 開發原生的桌面應用程式，更重要的是，不但可以寫炫麗的 3D UI，而且完全不需要靠『傻大個瀏覽器』。&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjHGhId70jQ1Yy2ZEhG4VTWOtZd9VWg6Pi4LyAPsfW-jmIuVmEuZZBserO4kdSSsLKXyr1pKqQ7gjfR29a-V6LABYe5BbddSbDW1ckJv9yVqw2nuLN032E97Mmzuj4PdS23ap8ZE3_6a4A/s1600/node-clutter-demo.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;320&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjHGhId70jQ1Yy2ZEhG4VTWOtZd9VWg6Pi4LyAPsfW-jmIuVmEuZZBserO4kdSSsLKXyr1pKqQ7gjfR29a-V6LABYe5BbddSbDW1ckJv9yVqw2nuLN032E97Mmzuj4PdS23ap8ZE3_6a4A/s320/node-clutter-demo.png&quot; width=&quot;300&quot; /&gt;&lt;/a&gt;&lt;/div&gt;如果想要載入圖片，並把物件 3D 旋轉，只需要這樣做：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;var toolkit = require(&#39;jsdx-toolkit&#39;);


if (toolkit.init() != toolkit.INIT_SUCCESS) {
&lt;span class=&quot;Apple-tab-span&quot; style=&quot;white-space: pre;&quot;&gt; &lt;/span&gt;console.log(&quot;Failed to initialize jsdx-toolkit.&quot;);
&lt;span class=&quot;Apple-tab-span&quot; style=&quot;white-space: pre;&quot;&gt; &lt;/span&gt;process.exit();
}

/* Create a new stage */
var stage = new toolkit.Stage();
stage.title = &#39;Rotate&#39;;
stage.resize(500, 500);
stage.setColor(0, 0, 0, 255);
stage.on(toolkit.EVENT_DESTROY, function() {
&lt;span class=&quot;Apple-tab-span&quot; style=&quot;white-space: pre;&quot;&gt; &lt;/span&gt;toolkit.quit();
});
stage.show();

/* Load a image */
var texture1 = new toolkit.Texture;
texture1.loadFile(&#39;fred.jpg&#39;);
texture1.setPosition(100, 100);
texture1.rotate(toolkit.Y_AXIS, 30, 0, 0, 0);
stage.add(texture1);

toolkit.main();&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
當然，這樣還不夠看，目前也已經支援了 GStreamer&amp;nbsp;，所以影音播放也不是問題，並能將影片渲染在 3D 物件上。&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgDNJkf-tvGLZW1k90Uga_Oz9a73r576z-hk52loZ_GjrIldxhdjT4MysyEZAyx6QJ1_0y_u0oMGCSLYoUkCp81VCu3m2jZGMPBaRrsyYQwGOsgu4ulyYOFZuj1jxkxZ5I22G6elkOCXqc/s1600/video-texture-demo.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;260&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgDNJkf-tvGLZW1k90Uga_Oz9a73r576z-hk52loZ_GjrIldxhdjT4MysyEZAyx6QJ1_0y_u0oMGCSLYoUkCp81VCu3m2jZGMPBaRrsyYQwGOsgu4ulyYOFZuj1jxkxZ5I22G6elkOCXqc/s320/video-texture-demo.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
達成這樣需求的程式一點都不難，短短不到 30 行就可以搞定：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;var toolkit = require(&#39;jsdx-toolkit&#39;);

if (toolkit.gst_init() != toolkit.INIT_SUCCESS) {
&lt;span class=&quot;Apple-tab-span&quot; style=&quot;white-space: pre;&quot;&gt; &lt;/span&gt;console.log(&quot;Failed to initialize jsdx-toolkit.&quot;);
&lt;span class=&quot;Apple-tab-span&quot; style=&quot;white-space: pre;&quot;&gt; &lt;/span&gt;process.exit();
}

/* Create a new stage */
var stage = new toolkit.Stage();
stage.title = &#39;VideoTexture&#39;;
stage.resize(400, 300);
stage.setColor(0, 0, 0, 255);
stage.on(toolkit.EVENT_DESTROY, function() {
&lt;span class=&quot;Apple-tab-span&quot; style=&quot;white-space: pre;&quot;&gt; &lt;/span&gt;toolkit.quit();
});
stage.show();

/* Create a video texture */
var video = new toolkit.GstVideoTexture;
video.setPosition(50, 50);
video.loadFile(&#39;jushelf-demo.ogv&#39;);
video.rotate(toolkit.Y_AXIS, 30, 0, 0, 0);
stage.add(video);

video.play();

toolkit.main();&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
此外，多數 JavaScript 開發者對 jQuery 應該相當熟悉，更喜愛其鏈狀（Chain）的寫法，同樣習慣也可以延用：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;stage.setTitle(&#39;VideoTexture&#39;).resize(400, 300).setColor(0, 0, 0, 255);&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
假設你已經對於這樣的開發有興趣，那你應該會更感興趣『動畫支援』：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;texture1.animate(toolkit.EASE_IN_QUINT, 1000, {
&lt;span class=&quot;Apple-tab-span&quot; style=&quot;white-space: pre;&quot;&gt; &lt;/span&gt;&#39;x&#39;: 5,
&lt;span class=&quot;Apple-tab-span&quot; style=&quot;white-space: pre;&quot;&gt; &lt;/span&gt;&#39;y&#39;: 5,
&lt;span class=&quot;Apple-tab-span&quot; style=&quot;white-space: pre;&quot;&gt; &lt;/span&gt;&#39;scale-x&#39;: 2.0,
&lt;span class=&quot;Apple-tab-span&quot; style=&quot;white-space: pre;&quot;&gt; &lt;/span&gt;&#39;opacity&#39;: 0
});&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
還不滿足嗎？可以去研究 tests 目錄下的程式碼，有更多功能已經被實作。像是方便 UI 觸發反應效果的&amp;nbsp;State Machine，以及更多的小功能。開始動手玩吧！&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhsagIK1UJENxT5ie2Mt8mNdFB0cXSlpEP2T9wWLhQPmHE8dsbn9nlil2erWljXH6z7ScvjdDspANkpz6NTGBYDI9QHn2902CSzyi2qvv0VO4LHZZhJZyLaT7HpPOPBu4bPSA_N_t087t4/s1600/texture-demo.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;320&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhsagIK1UJENxT5ie2Mt8mNdFB0cXSlpEP2T9wWLhQPmHE8dsbn9nlil2erWljXH6z7ScvjdDspANkpz6NTGBYDI9QHn2902CSzyi2qvv0VO4LHZZhJZyLaT7HpPOPBu4bPSA_N_t087t4/s320/texture-demo.png&quot; width=&quot;308&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;後記&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
仍有許多功能待未來加入，而且目前難免會有 Bug，還請來踢館的人們，多多包涵，不吝指教。</description><link>https://fred-zone.blogspot.com/2012/02/nodejs3d.html</link><author>noreply@blogger.com (Fred Chien)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjHGhId70jQ1Yy2ZEhG4VTWOtZd9VWg6Pi4LyAPsfW-jmIuVmEuZZBserO4kdSSsLKXyr1pKqQ7gjfR29a-V6LABYe5BbddSbDW1ckJv9yVqw2nuLN032E97Mmzuj4PdS23ap8ZE3_6a4A/s72-c/node-clutter-demo.png" height="72" width="72"/><thr:total>6</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-1207000197762600387</guid><pubDate>Sun, 19 Feb 2012 01:33:00 +0000</pubDate><atom:updated>2012-02-19T09:33:55.554+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Hacking 心得筆記</category><category domain="http://www.blogger.com/atom/ns#">Kinect</category><category domain="http://www.blogger.com/atom/ns#">Linux</category><category domain="http://www.blogger.com/atom/ns#">OpenCV</category><category domain="http://www.blogger.com/atom/ns#">技術新知</category><category domain="http://www.blogger.com/atom/ns#">系統研發手札</category><title>我的 Kinect 應用程式開發記錄</title><description>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;話說微軟(Microsoft) 這兩年為了 XBox 360 所推出的 Kinect 造成一股風潮，一夕之間，把 Wii 打的七零八落，取代了其體感遊戲機王者的地位，相比之下，Sony PS3 同一時期所推出的體感裝置，就顯得乏人問津&amp;nbsp;。&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;最近，筆者雖然忙錄，被很多麻煩的公事私事綁住，但每當睡前，仍抽空研究一些新技術。一方面是為了促使腦筋轉得更快，一方面是想加強許多 Idea 和 Business Model 的強度和完整性。於是，撿起丟在一旁生灰塵的 Kinect，嘗試著在上面開發一些應用。&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;由於『&lt;a href=&quot;http://openkinect.org/wiki/Main_Page&quot;&gt;OpenKinect&lt;/a&gt;』眾高手的努力，所以驅動程式不是太大的問題，很久以前就已經能分別在 Linux、Windows 和 Mac 上驅動 Kinect。不過雖說如此，最困難的還是在於辨識演算法的相關設計，對於開發人員來說，你不過就是能得到攝影機的影像，就如同從 Webcam 上得到一樣，其他的圖學運算以及應用還是要自己動手。&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjIqnDmkiR3C2L3fpuAUpdFqLVY1xmDnCTQZRW9-CSRR6IQ5xqySyPgfcTlWONTx96U9GhGqtpyHRkwV19ZdFqdWDunFbKP1YPevpOsEv1LIxAuSjW6Gbq1RWZjC_jtQ74jZmAE7nhx9z4/s1600/hand_detection.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;125&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjIqnDmkiR3C2L3fpuAUpdFqLVY1xmDnCTQZRW9-CSRR6IQ5xqySyPgfcTlWONTx96U9GhGqtpyHRkwV19ZdFqdWDunFbKP1YPevpOsEv1LIxAuSjW6Gbq1RWZjC_jtQ74jZmAE7nhx9z4/s320/hand_detection.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;當然，如果 Kinect 只是和一般 Webcam 一樣，那新聞媒體和許多人對他的誇讚，就太言過其實。其最大的不同在於，Kinect 有深度感測，這意味著我們可以得到實際的空間資訊，以工程的方式，大大減少辨識演算法的難度，並提升了辨識系統的精準度。&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: left;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div class=&quot;&quot; style=&quot;clear: both; text-align: left;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiYJGZf1lONTh20s6ZUXNMhDkcXnoRzVBtgOezYQ3Pfc-tOp9L_gOBwwLAVp2zwkuP17Wmc1IwNkPxg-gk-IWzxuCpot64G-PtIis-nez0KnE1A0SSJlmCPcm31xsXmYilEXhobnH4tYGw/s1600/gothand.png&quot; imageanchor=&quot;1&quot; style=&quot;clear: left; float: left; margin-bottom: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;246&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiYJGZf1lONTh20s6ZUXNMhDkcXnoRzVBtgOezYQ3Pfc-tOp9L_gOBwwLAVp2zwkuP17Wmc1IwNkPxg-gk-IWzxuCpot64G-PtIis-nez0KnE1A0SSJlmCPcm31xsXmYilEXhobnH4tYGw/s320/gothand.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;這次的嘗試，焦點放在辨識演算法之上，便開始思索各種方案。過程中，試了數種別人已經開發好的函式庫，都不甚滿意。倒不是效果不好，而是系統需求太大，開發語言大多採用 &amp;nbsp;Java ，所以需要安裝一卡車的模組程式以及 JVM。有些更是對 Linux 的開發者不夠友善，或開發支援不夠完整。無論哪一種現有方案，都不足以讓筆者可以安心建構在上面然後開發下去，因為產品化和易移植性，對筆者來說是相當重要的考量，不是只要學術驗證而已。所以最後決定，自己手動設計演算法，採用 C/C++。&lt;/div&gt;&lt;div class=&quot;&quot; style=&quot;clear: both; text-align: left;&quot;&gt;&lt;br /&gt;
&lt;/div&gt;經過這幾天睡前的努力，可以看到開發過程中的截圖，演算法初步已經能辨識手掌，並抓取指尖位置，精準度已達可用的程度。接著，只要多做些簡單的開發，撰寫 X11 輸入裝置驅動程式，就能實際以多點操控的型式，操作螢幕上的所有應用程式。&lt;br /&gt;
&lt;br /&gt;
此外，這裡有一些心得，Kinect 深度感測的數值範圍是 0 ~ 2047，至於得到的深度數值如何換算成實體距離，這邊有個公式可以換算成公尺(Meter)的型式：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;Real Distance = 1.0 / (raw_depth * -0.0030711016 + 3.3309495161)&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
如果你有興趣，國外可以找到許多大學實驗室利用 Kinect 和機器人整合的應用，也有為數不少的相關論文。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;後記&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
雖然相較國外研究機構的成果，筆者所開發的程式實在小兒科，但已經能解決於一般應用的需求，讓人興奮不已。如果有足夠時間，下一步，將嘗試開發人體骨架探測的演算法，抓取使用者的肢體動作，以達到更精確的判斷和支援更多的動作指令，也避免有人用假手呼弄電腦。:-D&lt;br /&gt;</description><link>https://fred-zone.blogspot.com/2012/02/kinect.html</link><author>noreply@blogger.com (Fred Chien)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjIqnDmkiR3C2L3fpuAUpdFqLVY1xmDnCTQZRW9-CSRR6IQ5xqySyPgfcTlWONTx96U9GhGqtpyHRkwV19ZdFqdWDunFbKP1YPevpOsEv1LIxAuSjW6Gbq1RWZjC_jtQ74jZmAE7nhx9z4/s72-c/hand_detection.png" height="72" width="72"/><thr:total>2</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-5381004581825698582</guid><pubDate>Thu, 16 Feb 2012 09:44:00 +0000</pubDate><atom:updated>2012-02-16T17:44:06.004+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">NodeJS</category><category domain="http://www.blogger.com/atom/ns#">程式心得筆記</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><title>用 NodeJS 打造守護神常註程式</title><description>寫系統程式很常會需要碰到實作常駐程式，這意味著你必需要讓程式在背景執行，然後提供服務或是等待事件觸發，如『&lt;a href=&quot;http://fred-zone.blogspot.com/2007/09/daemon.html&quot;&gt;親手打造一個背景執行的Daemon程式&lt;/a&gt;』所提到，若用 C 語言，做法大致如此：&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;void main()
{
    pid_t pid;
    pid = fork();

    if (pid&gt;0) {
        exit(0);
    }
}&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
而在 node.js 上可以使用『&lt;a href=&quot;https://github.com/indexzero/daemon.node&quot;&gt;daemon&lt;/a&gt;』模組，輕易打造常註程式。&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;$ npm install daemon&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
使用方法如下：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;var daemon = require(&#39;daemon&#39;);

/* Become a daemon */
daemon.start();

/* Loop to do something here in background */&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
執行後，程式應該會立即返回並結束，但是若用 ps 等系統工具程式去看目前正在執行的 Process，會發現我們的程式已經運行在背景了。</description><link>https://fred-zone.blogspot.com/2012/02/nodejs.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-6191940626805951637</guid><pubDate>Wed, 15 Feb 2012 14:25:00 +0000</pubDate><atom:updated>2012-02-15T22:26:42.351+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Desktop</category><category domain="http://www.blogger.com/atom/ns#">GNOME</category><category domain="http://www.blogger.com/atom/ns#">Juice</category><category domain="http://www.blogger.com/atom/ns#">Linux</category><category domain="http://www.blogger.com/atom/ns#">X11</category><category domain="http://www.blogger.com/atom/ns#">自由軟體</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><title>JuDaemon 完全取代 GNOME 的最後一根稻草</title><description>我們不得不承認，GNOME 為了整合桌面環境，曾經開發了很多好用的整合性的元件，像是桌面背景控制、圖型化元件樣式、螢幕設定監控和更多桌面功能的設定程式。對於我們這些不愛 新版 GNOME 的使用者來說，常常是改用其他桌面環境（如：LXDE、E17 或單純用 Awesome window manager），再配上 GNOME 的這些整合性元件。&lt;br /&gt;
&lt;br /&gt;
不久前，雖然 GNOME 進入了 3.0 時代，但這些整合性元件依然存在著，並不隨著版本上升而有所太大改變，這對我們這些『不肯升級的使用者』是很好的消息，沒有太大的影響。不過，在最近幾個月，GNOME 已經將這些元件一一消滅，整併進去其他的專屬程式當中。在 Debian 上，gnome-control-center 更是已經毀損（如果你沒有裝新版的 GNOME 3.0），更進一步，許多重要的系統常駐程式一一失效（如：gnome-settings-daemon 和 gnome-power-manager 等），不再如同過去可以輕易單獨使用。&lt;br /&gt;
&lt;br /&gt;
GNOME 所帶來的毀滅，已經嚴重影響到筆者現有的使用，所以打算再次繼續原本停滯一段時間的 Juice Desktop Environment&amp;nbsp;（詳情可參考『&lt;a href=&quot;http://fred-zone.blogspot.com/2011/08/juice-desktop-environment.html&quot;&gt;新鮮果汁吧！果汁桌面環境(Juice Desktop Environment)！&lt;/a&gt;』），過去曾開發過『&lt;a href=&quot;http://code.google.com/p/jushelf/&quot;&gt;JuShelf&lt;/a&gt;』，就屬於這計劃的元件之一。&lt;br /&gt;
&lt;br /&gt;
大概評估了工作，主要就是系統監控程式的實現，也就是取代舊有 gnome-settings-daemon 的功能。對筆者個人而言，目前只有幾種簡單需求：&lt;br /&gt;
&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;電源管理：螢幕休眠、電腦蓋上後自動休眠、AC 電源切換通知和低電壓通知。&lt;/li&gt;
&lt;li&gt;螢幕設定：插上外接螢幕後自動偵測，拔除時也自動恢復成單一螢幕（現在是一片黑）。&lt;/li&gt;
&lt;li&gt;OSD支援：調整螢幕亮度、音量時，可以跳出畫面顯示目前亮度和音亮。&lt;/li&gt;
&lt;/ol&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
日後當然還需要有圖型化程式，可以微調這些細部設定，不過不是初期主要的工作（相信會選擇不用 GNOME 的人，應該都有能力自己改設定檔）。&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
筆者成立了一個新的 Project -『&lt;a href=&quot;http://code.google.com/p/judaemon/&quot;&gt;JuDaemon&lt;/a&gt;』，就是為了解決這樣的需求，目前只完成了螢幕自動休眠的部份，尚有其他工作待繼續。如果您有興趣，可以提交 patch 或是加入這個專案一同開發。:-)&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
&lt;b&gt;後記&lt;/b&gt;&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
考慮到筆者近來比較常用 github ，而且 github 的開發者相對踴躍交流，過一段時間會將 Juice 的 Project 都搬到 github 上。&lt;/div&gt;</description><link>https://fred-zone.blogspot.com/2012/02/judaemon-gnome.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-6330630724708356274</guid><pubDate>Fri, 10 Feb 2012 22:32:00 +0000</pubDate><atom:updated>2012-02-11T06:48:02.253+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">LXDE</category><category domain="http://www.blogger.com/atom/ns#">心得分享、講座</category><title>那一年，自由軟體變成統獨論戰的題材</title><description>對於我們這種『宅宅水電工』，唯一的樂趣就是經營技術，閒暇招三五同好，玩玩新東西。但怎麼也沒想到，我們這種人做出來的東西，卻變成有心人『統獨論』的撰文題材，令人又好氣又好笑。原始文章出自這篇『&lt;a href=&quot;http://www.taiwanonline.cc/blog/%E7%BE%85%E8%B3%B4%E5%B7%B4/5350.html&quot;&gt;台灣人缺乏自信之實例 &lt;/a&gt;』，是筆者在閒逛 Google 時搜尋到的，因為有提到自己的名字，就特別點進去看了一下。被說成缺乏自信心，甚至要被除去，百般無奈。&lt;br /&gt;
&lt;br /&gt;
還記得 &lt;a href=&quot;http://lxde.org/zh-tw&quot;&gt;LXDE&lt;/a&gt; 剛開始會為人所知，是因為 PCMAN 於一些台灣社群活動中，首度公開設計理念，當時雖然許多人叫好，但真正參與開發或專案的確寥寥無幾，一段時間折騰下來，包括我在內的開發者和參與貢獻者，不過五六人，甚至 PCMAN 寫了大量的程式，一度荒廢了自己的學業和醫生本業。&lt;br /&gt;
&lt;br /&gt;
說起來心酸，為了得到台灣社群的認同，我們無不熱衷於在台灣各個大小場合，推廣和號招開發者以及有興趣的人加入。當然， 我們不是外國人，都是使用『中文』與其他人說明和討論技術問題，也極力確保軟體都有中文的介面。甚至成天掛在 IRC（相當古老的網路聊天系統，目前仍被全世界的駭客和開放源始碼廣泛使用） 上，與網友以『中文』討論諸多相關問題。當時許多文件和簡報，同時存在著中文和英文版，甚至很多只有中文版本。&lt;br /&gt;
&lt;br /&gt;
LXDE 的成員身體力行在台灣走透透，卻沒想到也能招來不少外國人。在推廣過程中，開始接觸到許多國外來的開發者和自由軟體愛好者，也開始在各國都有人自動自發要幫忙翻譯成各種語言，然後台灣以外的許多愛好者也開始大量使用 LXDE 並回報問題，甚至提交他們修改過後的程式 patch，參與貢獻。我只能說一開始， LXDE 只有一堆一般人不易讀懂的程式碼，後來的許多非中文的文件和說明， 很多是出自於這些外國貢獻者之手。&lt;br /&gt;
&lt;br /&gt;
註：差點忘了提，LXDE 的網域名稱，還是一位印度人所贊助的。&lt;br /&gt;
&lt;br /&gt;
『自由軟體和開放源始碼』會為我們這些宅宅水電工所喜愛，就是能與所有的同好者，一同完成這些程式和專案。但是，這些自由軟體本來就只是花費我們的課餘時間和下班閒暇時間，不可能什麼東西都能親力親為，我們唯一能做的，就是將自己的初衷做好 。所以，LXDE 後來在國外會有一些知名度，也不是一開始所能預期的，更別提到是不是以台灣為榮，那都是不重要的後話。&lt;br /&gt;
&lt;br /&gt;
若真要說到活在台灣的心態，我只能說：『我們在台灣長大，只是將在台灣這一二十載所學的一一展現出來，如此單純而已。』。我們挺胸開發自己的程式，一步步『意外』走入國際，最終，我們不過就是個程式開發者，只是其中的一塊齒輪，雖然被人稱為『作者』，卻不能隨心控制所有狀況，更別說我們也不是專職在做這工作，家中也沒有精神時光屋可以讓我的一天變一年。&lt;br /&gt;
&lt;br /&gt;
所以，我並不認為我們缺乏自信心，更不認為我們有什麼錯，只是當我們埋頭苦幹，彎腰駝背天天搶著搭捷運的通勤時間來開發程式，再到處推廣後，卻只有外國人和第三世界國家的人熱心參與時，我們能夠怪誰不寫中文文件？&lt;br /&gt;
&lt;br /&gt;
我強調，這無關政治，也無關民族意識，而是身為人的修養問題。至少我相信，出生於台灣和活在台灣對我來說是不可抹滅的事實，所以，不亢不卑，不懷疑自己身份，並實地展現自己一切的人，比起只會打嘴炮，要靠對自己不斷摧眠『我有自信』的人，來的強得多了。&lt;br /&gt;
&lt;br /&gt;
不從自己做起，而想靠著人多壯膽有自信，那也只是逞地痞流氓之勢罷了，可謂沒自信的實例。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;後記&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
回想起很多幾年前的事，不免會心一笑。PCMAN 當年因為沉迷於程式開發，一度無法通過國家考試，拿不到醫生執照。但後來還是放棄軟體開發，順利通過國家考試，回到本行當醫生。如今聽說做的相當不錯，從他的行醫心得文章來看，可以稱得上是相當用心又努力向上的醫生，將生命交給他，可以說不用太擔心。此外，他也會在醫院的空檔，開發自己工作上的工具程式，改善工作效率，相當難得，對醫療體系也會是莫大貢獻。</description><link>https://fred-zone.blogspot.com/2012/02/blog-post.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>19</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-3116772297854436291</guid><pubDate>Tue, 07 Feb 2012 00:50:00 +0000</pubDate><atom:updated>2012-02-07T08:53:04.248+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Android</category><category domain="http://www.blogger.com/atom/ns#">Embedded System</category><category domain="http://www.blogger.com/atom/ns#">Linux</category><category domain="http://www.blogger.com/atom/ns#">Web</category><category domain="http://www.blogger.com/atom/ns#">商業</category><category domain="http://www.blogger.com/atom/ns#">心得分享、講座</category><category domain="http://www.blogger.com/atom/ns#">心情筆記</category><category domain="http://www.blogger.com/atom/ns#">顧問咨詢</category><title>有技術的創業者、SOHO族和個人接案者趕緊來吧！</title><description>這是創業者的心聲，我們永遠為了為數不多的錢，拼老命和客戶周旋。在客戶面前包到好包到腳，扛的責任何其多，回來後又當業務又當 PM 還要當 R&amp;amp;D，最後還得當會計，數不盡的心酸誰知道。沒辦法，對我們來說，現金流（CashFlow）比什麼都還重要，能不能挺過這個月或下個月，維持周轉才是首要任務，為此，任何苦我們也得吃下。&lt;br /&gt;
&lt;br /&gt;
筆者做了幾年的 Outsourcing，雖然不是當初創業的核心目標，但為了養家活口，接案卻成了主要業務。不過，筆者確實也因接案而活了下來並壯大團隊，能夠有機會持續朝理想發展。接案的過程中，風風雨雨自然不在話下，但慶幸受到前輩和朋友間的提攜，近來也總算是開始有個根基，剛好穩住腳。&lt;br /&gt;
&lt;br /&gt;
接案就是這樣，有一頓沒一頓，如果要穩定收入，案子的價與量就必需相應提高，這是所有以接案當食糧的創業者，所都要經歷的過程。但是，往往在提升價和量後，案子開始會吃掉團隊的全部時間，漸漸失去當初創業的核心目標。然後，進入了『招人力』後『招案子』再『招更多人力』後『招更多案子』的惡性輪迴中，最終你會想：『辛苦到快暴斃，工時責任換算下來，賺得比去上班來得少，那我何必創業？』。如果你有同感，不用灰心，因為現在有無數的創業團隊和創業者， 也都正面對著同樣問題，甚至面臨崩潰。&lt;br /&gt;
&lt;br /&gt;
有鑑於此，筆者尋思，何不與大家分享自己的經營成果，讓大家各自都能得到更多時間經營自己的核心目標？創業者、SOHO族和個人接案者所需要的源源不絕的食糧，不必各自重新發展重新耕耘，只需要從筆者這接手過去即可。由於舊有團隊原本已經有業務和PM的佈署，大家也可專注於技術性的工作，並提供如自來水般的技術供應即可。&lt;br /&gt;
&lt;br /&gt;
對筆者來說，自己的核心技術團隊得以鬆口氣， 能慢慢以階段性放下養活自己的短期目標，有更多時間朝自己原本創業的核心目標前進。另一方面，也能給予其他創業者、SOHO族和個人接案者穩定的收入支持，不必重蹈覆徹筆者同樣的道路。如此雙贏的局面，樂見其成。&lt;br /&gt;
&lt;br /&gt;
所以，如果你們是有技術的創業者或創業團隊，SOHO族或個人接案者，趕緊來報到吧！&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;後記&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
無論你是專精或有經驗於哪一領域（Web、Linux、Android System（Integration/Porting）、Android App、iOS App 或 Embedded System... etc），都歡迎來信，最少先交個朋友也是不錯的。:-)</description><link>https://fred-zone.blogspot.com/2012/02/soho.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>19</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-7819999918408718215</guid><pubDate>Mon, 06 Feb 2012 02:10:00 +0000</pubDate><atom:updated>2012-02-06T10:13:18.282+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">C/C++</category><category domain="http://www.blogger.com/atom/ns#">X11</category><category domain="http://www.blogger.com/atom/ns#">程式心得筆記</category><category domain="http://www.blogger.com/atom/ns#">自由軟體</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><title>用 XRandR Extension 監控螢幕設定變化</title><description>最近一直感覺到系統有時莫名緩慢，CPU 負載也相當高，初期沒去注意，也一味的相信應該都是瀏覽器開太多網頁和 Flash 檔案所造成的，但種種情況令人不解，總覺得不完全是瀏覽器所造成的。將所有瀏覽器關閉後追蹤發現，自己之前寫的『&lt;a href=&quot;http://code.google.com/p/jushelf/&quot;&gt;JuShelf&lt;/a&gt;』（Dock 程式）竟吃掉近 50% 的 CPU 資源。原兇是之前寫的一段程式，用來偵測目前螢幕設定是否有改變，並依據螢幕設定而調整顯示位置，以讓 Dock 永遠顯示在螢幕的中央。&lt;br /&gt;
&lt;br /&gt;
原本的做法相當笨，利用 g_idle_add_full() 讓程式空閒時就去偵測螢幕設定，但由於 Dock 通常是晾在一旁沒有工作的，所以程式經常有空閒時間，這讓程式以每秒百計的次數不停檢查。這還不打緊，由於每次檢查都要與 X Server 做一次至數次溝通，付出的代價更為高昂。&lt;br /&gt;
&lt;br /&gt;
抓到臭蟲以後，便開始著手改進，使用了 XRandR Extension 來取代原有的解決方案。過去筆者曾撰文『&lt;a href=&quot;http://fred-zone.blogspot.com/2010/12/c-xrandr-extension.html&quot;&gt;寫支 C 程式用 XRandr Extension 設定你的螢幕解析度&lt;/a&gt;』說明 XRandR 的使用方式，不過當時只著重於取得 X Server 的顯示設定，這次將利用 XRandR Extension 以等待&amp;nbsp;RRScreenChangeNotify event 來得知螢幕設定何時改變。&lt;br /&gt;
&lt;br /&gt;
由於完整程式太長，這邊只摘錄重點片斷，主要是檢查 XRandR 版本和註冊事件：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;int rr_major_version,&amp;nbsp;rr_minor_version;
int rr_minor_version,&amp;nbsp;xrandr_error_base;

/* Initializing X Stuffs... */

/* XRandr extension */
XRRQueryVersion(disp, &amp;amp;rr_major_version, &amp;amp;rr_minor_version);
if (rr_major_version &amp;lt; 1 || (rr_major_version == 1 &amp;amp;&amp;amp; rr_minor_version &amp;lt; 2)) {
&lt;span class=&quot;Apple-tab-span&quot; style=&quot;white-space: pre;&quot;&gt; &lt;/span&gt;printf(&quot;RANDR extension is too old (must be at least 1.2)\n&quot;);
&lt;span class=&quot;Apple-tab-span&quot; style=&quot;white-space: pre;&quot;&gt; &lt;/span&gt;return 1;
}

/* Allow XRandR Extension Event */
XRRSelectInput(disp, DefaultRootWindow(disp), RRScreenChangeNotifyMask);
XRRQueryExtension(disp, &amp;amp;rr_minor_version, &amp;amp;xrandr_error_base);&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
之後在等待 X Event 時，可以利用&amp;nbsp;ev-&amp;gt;type - xrandr_event_base 去取得 XRandR 的事件：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;/* XRandr Extension Event */
switch(ev-&amp;gt;type - xrandr_event_base) {
case RRScreenChangeNotify:
&lt;span class=&quot;Apple-tab-span&quot; style=&quot;white-space: pre;&quot;&gt; &lt;/span&gt;printf(&quot;Got RRScreenChangeNotify\n&quot;);
&lt;span class=&quot;Apple-tab-span&quot; style=&quot;white-space: pre;&quot;&gt; &lt;/span&gt;break;
}&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
&lt;b&gt;後記&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
無論是舊的 GNOME 2.0 還是新的 GNOME 3.0，通常都在 gnome-settings-daemon 實作 XRandR 的事件監聽，所以才能實現自動切換螢幕設定，當 External Monitor 接上電腦時。</description><link>https://fred-zone.blogspot.com/2012/02/xrandr-extension.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-5413885758091904763</guid><pubDate>Thu, 02 Feb 2012 09:25:00 +0000</pubDate><atom:updated>2012-02-02T17:25:28.670+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">NodeJS</category><category domain="http://www.blogger.com/atom/ns#">NPM</category><category domain="http://www.blogger.com/atom/ns#">Web 相關技術</category><category domain="http://www.blogger.com/atom/ns#">程式心得筆記</category><category domain="http://www.blogger.com/atom/ns#">自由軟體</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><title>NodeJS 應用佈建快手：利用 package.json 處理麻煩的模組相依性</title><description>NodeJS 日益強大，相關開發資源也越來越多，許多功能已經不再需要自己寫，通通都可以直接利用官方的 NPM（Node Package Manager）工具，從網路上把別人已經開發好的模組抓回來用。這樣的模組交換平台加快了開發速度，讓開發者可以全心全意專注於當前應用的開發工作。但是也因為如此，讓我們的 Project 總是相依於一大堆的第三方模組，而每當要將我們的程式佈建於伺服器時，就必需手動先將所有的模組先安裝好，甚至是用最笨的方法邊試邊裝遺漏的模組。&lt;br /&gt;
&lt;br /&gt;
事實上，我們可以在程式目錄中，建立 package.json 檔，直接管理 Project 的相依性以及所使用到的模組套件：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;{
&lt;span class=&quot;Apple-tab-span&quot; style=&quot;white-space: pre;&quot;&gt; &lt;/span&gt;&quot;name&quot;: &quot;my-website&quot;,
&lt;span class=&quot;Apple-tab-span&quot; style=&quot;white-space: pre;&quot;&gt; &lt;/span&gt;&quot;version&quot;: &quot;0.0.1&quot;,
&lt;span class=&quot;Apple-tab-span&quot; style=&quot;white-space: pre;&quot;&gt; &lt;/span&gt;&quot;private&quot;: true,
&lt;span class=&quot;Apple-tab-span&quot; style=&quot;white-space: pre;&quot;&gt; &lt;/span&gt;&quot;dependencies&quot;: {
&lt;span class=&quot;Apple-tab-span&quot; style=&quot;white-space: pre;&quot;&gt;  &lt;/span&gt;&quot;express&quot;: &quot;2.4.7&quot;,
&lt;span class=&quot;Apple-tab-span&quot; style=&quot;white-space: pre;&quot;&gt;  &lt;/span&gt;&quot;jade&quot;: &quot;&amp;gt;= 0.0.1&quot;,
&lt;span class=&quot;Apple-tab-span&quot; style=&quot;white-space: pre;&quot;&gt;  &lt;/span&gt;&quot;oauth&quot;: &quot;&amp;gt;= 0.9.5&quot;
&lt;span class=&quot;Apple-tab-span&quot; style=&quot;white-space: pre;&quot;&gt; &lt;/span&gt;}
}&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
一旦目錄中存在 package.json，下次要移到新的環境裝起來時，可以直接利用 NPM 把所有的相依模組一次裝好，省下很多功夫：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;$ npm install&lt;/pre&gt;&lt;/div&gt;</description><link>https://fred-zone.blogspot.com/2012/02/nodejs-packagejson.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-6645633590262608877</guid><pubDate>Wed, 01 Feb 2012 05:42:00 +0000</pubDate><atom:updated>2012-02-01T13:43:01.195+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Express Web Framwork</category><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">NodeJS</category><category domain="http://www.blogger.com/atom/ns#">Web 相關技術</category><category domain="http://www.blogger.com/atom/ns#">自由軟體</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><title>使用 NodeJS + Express 從 GET/POST Request 取值</title><description>過去無論哪一種網站應用程式的開發語言，初學者教學中第一次會提到的起手式，八九不離十就是 GET/POST Request 的取值。但是，在 Node.js + Express 的世界中，彷彿人人是高手，天生就會使用，從不曾看到有人撰文說明。&lt;br /&gt;
&lt;br /&gt;
這應該算是開發 Web Service 的入門，在 Client 與 Server 的互動中，瀏覽器發出&amp;nbsp;GET/POST Request 時會傳值給 Server-side，常見應用就是網頁上以 POST method 送出的表單內容，或是網址列上的 Query Strings （ex: page?page=3&amp;amp;id=5）。然後，我們的網站應用程式透過解析這些參數，得到使用者上傳的資訊。&lt;br /&gt;
&lt;br /&gt;
取得 GET Request 的 Query Strings：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;GET /test?name=fred&amp;amp;tel=0926xxx572&lt;/pre&gt;&lt;/div&gt;&lt;div&gt;&lt;pre&gt;app.get(&#39;/test&#39;, function(req, res) {
&amp;nbsp; &amp;nbsp; console.log(req.query.name);
&amp;nbsp; &amp;nbsp; console.log(req.query.tel);
});&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
如果是透過表單且是用 POST method：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;&amp;lt;form action=&#39;/test&#39; method=&#39;post&#39;&amp;gt;
&amp;lt;input type=&#39;text&#39; name=&#39;name&#39; value=&#39;fred&#39;&amp;gt;
&amp;lt;input type=&#39;text&#39; name=&#39;tel&#39; value=&#39;0926xxx572&#39;&amp;gt;
&amp;lt;input type=&#39;submit&#39; value=&#39;Submit&#39;&amp;gt;
&amp;lt;/form&amp;gt;&lt;/pre&gt;&lt;/div&gt;&lt;div&gt;&lt;pre&gt;app.post(&#39;/test&#39;, function(req, res) {
&amp;nbsp; &amp;nbsp; console.log(req.body.name);
&amp;nbsp; &amp;nbsp; console.log(req.body.tel);
});&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
當然也可以 Query Strings 和&amp;nbsp;POST method 的表單同時使用：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;&amp;lt;form action=&#39;/test?id=3&#39; method=&#39;post&#39;&amp;gt;
&amp;lt;input type=&#39;text&#39; name=&#39;name&#39; value=&#39;fred&#39;&amp;gt;
&amp;lt;input type=&#39;text&#39; name=&#39;tel&#39; value=&#39;0926xxx572&#39;&amp;gt;
&amp;lt;input type=&#39;submit&#39; value=&#39;Submit&#39;&amp;gt;
&amp;lt;/form&amp;gt;&lt;/pre&gt;&lt;/div&gt;&lt;div&gt;&lt;pre&gt;app.post(&#39;/test&#39;, function(req, res) {
&amp;nbsp; &amp;nbsp; console.log(req.query.id);
&amp;nbsp; &amp;nbsp; console.log(req.body.name);
&amp;nbsp; &amp;nbsp; console.log(req.body.tel);
});&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
&lt;br /&gt;
順帶補充，還有另一種方法傳遞參數給 Server，就是使用路徑的方式，可以利用 Web Server 的 HTTP Routing 來解析，常見於的各種 Web Framework。這不算是傳統標準規範的做法，是屬於 HTTP Routing 的延伸應用。&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;GET /hello/fred/0926xxx572&lt;/pre&gt;&lt;/div&gt;&lt;div&gt;&lt;pre&gt;app.get(&#39;/hello/:name/:tel&#39;, function(req, res) {
&amp;nbsp; &amp;nbsp; console.log(req.params.name);
&amp;nbsp; &amp;nbsp; console.log(req.params.tel);
});&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;後記&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
筆者最近收到網友 $10 USD 的贊助，這對持續在網路上經營寫作，是莫大的股舞，由衷感謝！</description><link>https://fred-zone.blogspot.com/2012/02/nodejs-express-getpost-request.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>7</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-464918745872835857</guid><pubDate>Sat, 21 Jan 2012 11:10:00 +0000</pubDate><atom:updated>2012-01-21T19:10:45.124+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">商業</category><category domain="http://www.blogger.com/atom/ns#">心情筆記</category><title>商業操弄，軟體研發人員不喜歡做的事！</title><description>在國外，許多大型軟體公司林立，老牌軟體商建立了不少現今軟體業仍在使用的遊戲規則。而比較小的軟體外包商、工作室或是個人，也糊裡糊塗依照這些大公司使用的遊戲規則走，甚至是遵守不合時宜的淺規則。然後，軟體業進入了國內，又被代工思維牽著走，軟體業因此哀嚎聲不斷。軟體研發人才不喜歡去操弄商業，卻被商業操弄著。&lt;br /&gt;
&lt;br /&gt;
身為軟體外包或是軟體客製化的外包商，你是否曾遇過，客戶有很多大大小小的鎖碎要求，不定期會交代過來? 這些修修改改，看似不癢的功能和需求，做起來毫不費功夫，三兩下順手就能完成，卻往往造成雙方驗收標地的分岐，此外，不知不覺中所累積的份量，也讓結案時間遙遙無期，甚至造成軟體產生更多的問題待解決。對於只想當水電工的研發人員，總是在客戶的要求下，修完馬桶順道通水管，又補個土、也打個臘。另外，如果馬桶旁邊剛好有漏水，那你要自認倒楣了，因為客戶也會請你幫忙『順手』解決一下，或認定是你在修馬桶時弄壞的。是吧，令人難過。&lt;br /&gt;
&lt;br /&gt;
難道研發人員就要這麼可憐嗎？我們做錯了什麼，為何就要這樣被對待？明明是為了提供更好服務，卻累死三軍，又讓人有把柄指著鼻子唸。更慘的是，一旦這些看似不起眼的修修改改，累積成有影響力，讓專案與合約內的需求不完全相符。客戶就有機會利用『與合約不符』的矛盾點，再殺下一城。&lt;br /&gt;
&lt;br /&gt;
筆者也同是天涯淪落人，曾經不斷思考這問題也同時咒罵著台灣的業界，咒罵他們總不給軟體商有活命的空間。尤其受了委屈後，更是一股惱自命不凡，認為自己無論做什麼樣的事，都應該比一般人收取更多費用，以平心頭之恨。但過些時日後，經過冷靜思考，發現是一開始雙方的互動模式就錯了，或許更仔細檢視這個行業，才能找到改變現況的辦法。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;大多數軟體開發不是演藝事業&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
一直不認為『軟體程式設計師』是演藝事業，所謂『台上一分鐘，台下十年功』不能完全形容軟體開發的工作。雖然，越厲害的研發人員，越能設計好品質的軟體；越是名不經傳的軟體商，也越難生存於業界。許多面向與藝人生態有些相似，但其實完全不是這個樣子。&lt;br /&gt;
&lt;br /&gt;
因為我們雖練功多年，在專案過程中，卻仍然做很多沒技術性又取代性高的工作，或是『順手工作』這類連項目都寫不出來的事。所以，與演藝事業最大的不同是，我們不是只做一件事：『像巨星一樣站上台吸引全場目光，解決觀眾們長達數十分鐘的無聊時間』，而是因應專案需求，巨星仍然要下海當工讀生或工作人員，甚至是清潔工。所以，大多數軟體研發人員，其實扮演的是『跨年晚會工程的承包商』，不是巨星。許多客戶的額外零碎需求，就像有人在會場中亂扔垃圾需要被收拾，或是突然有人身體不適昏倒需要救護車送醫，這些都無法預期也無法很容易的按工計酬。&lt;br /&gt;
&lt;br /&gt;
這一切，除非你有將風險成本都算在專案內，更重要的是案子也要夠大，不然相當不容易去完成工作。這也是為什麼許多比較大的軟體商，比較不受影響，因為他們接的案子也夠大，風險成本都能很輕易的被安插進總報價裡面。&lt;br /&gt;
&lt;br /&gt;
因此，除了這類大型軟體商，多數中小外包商和客戶的想法，其實一開始便有了分岐。很多研發人員真的分不太清楚，自己只是當個明星，還是承包整個工程的承包商。但無論如何，客戶的心中一直很清楚認定：『承接專案的人就是整場晚會的承包商，我們不管你是不是把巨星拿來當小弟用，我們要得到的就是一個成功又圓滿的晚會』。&lt;br /&gt;
&lt;br /&gt;
一切的痛苦，已經不能責怪是不是當初定需求不夠明確，也不能怪研發人員利用太多『舉手之勞』提供給客戶額外的服務。最後你會發現，想再多的方法與客戶確認需求、利用商業手段與客戶定下契約，或是出動威脅利誘的業務手法與客戶協商，都是頭痛醫頭腳痛醫腳之舉。或許先認清楚自己想要做的角色和可以做的角色，再引入適當的制度，才是突破現況的第一要務。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;我們只想當水電工&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
說明白了，我們只想當個『軟體水電工』，有工就有酬，沒經驗的小工拿一份酬，有經驗的工頭拿雙倍酬，立即解決問題就立即收錢，然後打道回府等下一個工。至於整體工程承包，我們其實一點興趣都沒有。&lt;br /&gt;
&lt;br /&gt;
只是，當水電工似乎是遙不可及的夢，因為客戶就是自己不能解決或決定，才外包找人解決，他們想要得到的是整個解決方案，而不只是工人。更重要的是，在資訊科技業中，客戶無法看到漏水就能自行判斷要找水電工，而是要先找醫生診斷病情，然後才選擇治療方案和醫療團隊。&lt;br /&gt;
&lt;br /&gt;
雖然很可惜的是，大多數情況下，軟體研發人員沒辦法只當個『水電工』，但肯定有不少需求，是可以規劃成專業服務，使用水電工的收費方式，若是多加思考，應該能有些斬穫。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;br /&gt;
&lt;b&gt;我們也許可以當醫生&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
其實仔細省閱一下醫生的工作，除了要聽取問題、診斷病情、評估可行性和提出處方，必要時要進入手術室開刀和操作儀器去治療病人，就像軟體外包在做的一般，開玩笑的說，我們也許可以稱醫生為『人體工程師』。此外，而客戶基於安心考量，多少也會自行去尋找名醫。這些種種情況，個人認為軟體研發人員其實更像個醫生。&lt;br /&gt;
&lt;br /&gt;
因為像醫生，所以在簽約進行手術或治療前，我們也必需要做許多評估。但不一樣的是，無論醫生是否有治療成功，醫院總是有錢領，但軟體業則是要摸著鼻子認了。許多軟體外包商，在談案子的階段，最怕的就是不清楚技術上可行與否，或是不知自己有沒有能力達成。所以，多數軟體研發人員在面對不同案子簽約之前，就算不確定是否會得到案子，也都會先做許多評估或提前研究，甚至，必需提前偷跑才能知道可行性。&lt;br /&gt;
&lt;br /&gt;
不過，面對現在科技產業客戶都追求創新的情勢，這種不確定性的程度更甚也更常見，如果案子接到了還算好，如果沒接到，這種事前研究期的資源浪費，讓更多中小軟體商非常痛苦。更還沒提到，接到案子後如果有不可預期的技術瓶頸（像是配合的硬體平台有嚴重且之前未被發現的 Bug），造成手術失敗或時間拉長的情況。真正的醫療體系或許還有保險可以支付手術費用，但在軟體業，這意味著無法結案，收不到款。&lt;br /&gt;
&lt;br /&gt;
因為這樣不易收到款的制度，軟體承包商為了結案，屢見不鮮地用盡『各種手段』處理和討好客戶的需求，包括了文章開頭提到的『舉手之勞』和隨之而來的各種問題。導致其開發出來的軟體品質不佳，客戶也得不到最好的成果，常有狗尾續貂的情況發生，講的更難聽些，虎頭蛇尾會更為貼切。&lt;br /&gt;
&lt;br /&gt;
可憐的是，軟體研發不是人命關天，一切在商言商，客戶們通常不願只付『掛號費』，還可能貨比三家。哪一家能『價格殺的夠低』又拍胸脯『保證全部完成』，哪一家就能搶到案子。如此惡性競爭，更加深擴大了軟體研發人員被劃開的傷口。&lt;br /&gt;
&lt;br /&gt;
軟體產業沒有軟體健康保險補助，不是所有人都能夠負擔起看醫生、身體檢查和評估的費用，所以在這產業中有錢人才能夠看醫生。不然就是醫生得扛起所有風險和責任，並吸收許多成本，才能去拓展市場。&lt;br /&gt;
&lt;br /&gt;
或許，要求政府有相應的保險制度不太可能，但更細的分工整合和處方制度，可能是一個可行的思路。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;試圖尋求保障雙方的軟體開發模式&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
客戶和軟體商雙方的拉距戰，就像一個很大的槓桿，一不小心另一邊就飛上了雲端。&lt;br /&gt;
&lt;br /&gt;
我們可以理解，不能完全怪罪於客戶，畢竟所有人都總是希望得到完整的解決方案、降低風險，又盡可能支付最少的錢。小客戶是預算不高要緊扣，大客戶則是怕付了錢，卻達不到目的，或是得到一堆無法使用的 01 文字。&lt;br /&gt;
&lt;br /&gt;
但對於軟體研發人員來說，卻也太過不公平，只能全賠和拼命乞討二選一。&lt;br /&gt;
&lt;br /&gt;
現有的各種手段都無法幫助軟體商突破現況，太多的慣例也惡習累積已久。這也是為什麼，很多以接案起家的新創公司(Startup)，到五、六個人之後，開始碰到瓶頸。因為小案子已經養不起公司，中間的案子不好做，再上去就要一股作氣引進人員到數十人才能做。但人數到這麼多時，大案子的獲利也只是勉勉強強。&lt;br /&gt;
&lt;br /&gt;
所以，如何想出方法，雙方保障，是不可放棄的目標。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;後記&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
本文只是先起了個開頭，省視當前情勢。雖然對於實際執行，已經有些初步方案，有空再記錄下來，期望大家共同尋找出一個可行的制度。</description><link>https://fred-zone.blogspot.com/2012/01/blog-post_21.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>2</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-7539429172421930586</guid><pubDate>Wed, 11 Jan 2012 05:17:00 +0000</pubDate><atom:updated>2012-01-11T13:35:03.616+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Express Web Framwork</category><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">NodeJS</category><category domain="http://www.blogger.com/atom/ns#">Web 相關技術</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><title>NodeJS + Express + i18next 支援多國語系吧！</title><description>除非是區域性的網站服務，不然在這個網路通全世界的時代，開發網站服務就一定有多國語系的需求。一般來說，i18n 的支援都是由 Web Framework 所提供，但 Express 並沒有支援，所以我們要借助 i18next 這個模組。i18next 可以和 Express 以及 template 很完美的結合，最重要的是，有客戶端支援(clientside support)，若搭配 jquery，我們也可以使前端的 JavaScript 支援多國語系。&lt;br /&gt;
&lt;br /&gt;
先使用 NPM 安裝 i18next：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;npm install i18next&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
在 express 的應用程式(app.js)中引入使用 i18next：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;var express = require(&#39;express&#39;);
&lt;b&gt;var i18n = require(&#39;i18next&#39;);&lt;/b&gt;

var app = module.exports = express.createServer();

&lt;b&gt;/* Initializing i18n */
i18n.init();
i18n.registerAppHelper(app);&lt;/b&gt;

app.configure(function(){
  app.set(&#39;views&#39;, __dirname + &#39;/views&#39;);
  app.set(&#39;view engine&#39;, &#39;jade&#39;);
  app.use(express.bodyParser());
  &lt;b&gt;app.use(i18n.handle);&lt;/b&gt;
  app.use(express.methodOverride());
  app.use(app.router);
  app.use(express.static(__dirname + &#39;/public&#39;));
});

app.get(&#39;/&#39;, function(req, res) {
  res.render(&#39;index&#39;);
});

app.listen(3000);&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
接著要在應用程式的目錄下，建立預設的翻譯檔和存放路徑（locales/dev/translation.json）：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;{
  &quot;example&quot;: {
    &quot;string1&quot;: &quot;Hello World!&quot;
  }
}&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
如果要支援繁體中文(zh-TW)，直接建立相應的翻譯檔（locales/zh-TW/translation.json）：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;{
  &quot;example&quot;: {
    &quot;string1&quot;: &quot;哈囉 世界！&quot;
  }
}&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
然後就可以在 template 中使用之前所定義的字串(views/index.jade)：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;span= t(&#39;example.string1&#39;)&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
完成之後，i18next 會自動依照用戶瀏覽器的語系設定，回應相對應的頁面。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;後記&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
文章一開頭有提到，i18next 可以配合 jquery 使用，有興趣的人可以直接參考『&lt;a href=&quot;http://jamuhl.github.com/i18next/&quot;&gt;官方說明&lt;/a&gt;』。</description><link>https://fred-zone.blogspot.com/2012/01/nodejs-express-i18next.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-5560914666573214213</guid><pubDate>Fri, 06 Jan 2012 20:18:00 +0000</pubDate><atom:updated>2012-01-07T10:06:47.928+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Android</category><category domain="http://www.blogger.com/atom/ns#">Embedded System</category><category domain="http://www.blogger.com/atom/ns#">Linux</category><category domain="http://www.blogger.com/atom/ns#">Qt</category><category domain="http://www.blogger.com/atom/ns#">顧問資詢</category><title>【企業顧問咨詢】為您的產品選擇作業系統解決方案</title><description>你將為自家的產品，選用什麼樣的作業系統？我想，這年頭，不外乎是 Android。但是，可能不是最佳選擇。&lt;br /&gt;
&lt;br /&gt;
我想大多數人都會同意，由於智慧型行動裝置的掘起，Google Android 在業界大放異彩，使用 Linux 作業系統核心的 Android，實現了許多華麗的UI，也讓眾多廠商取得入門門票，有機會與 Apple iOS 一爭長短。許多人都相信，未來是 Android 的天下，除了手機要用 Android，電視要 Android，救人一命的醫療器材也要用 Android，所謂的 Android anywhere 是終極目標。&lt;br /&gt;
&lt;br /&gt;
不過，經過這幾年下來，許多人發現這不過是我們資訊科技業一廂情願，有太多的領域是難以使用 Android，如工控市場等，無論是以穩定、價格、移植維護成本考量的因素，都可能是無法使用 Android 的重點。因此，如今要選擇作業系統解決方案，又變成一個需要思考的難題。&lt;br /&gt;
&lt;br /&gt;
筆者擔任顧問時，每當面對客戶躊著於這個問題時，我總問他們幾個大問題：&lt;br /&gt;
&lt;ol&gt;&lt;li&gt;你們的產品用途是否單一且單純？是否要有讓使用者自行上網安裝其他第三方軟體的擴充性？&lt;/li&gt;
&lt;li&gt;你們的產品是否為消費行電子產品？是否需要高可用行和高穩定性？&lt;/li&gt;
&lt;li&gt;你們的軟體需要有華麗特效的 UI 嗎？你們現在擁有的人員有哪些開發 UI 程式的經驗？&lt;/li&gt;
&lt;li&gt;有網路的需求嗎？如果有，需要哪些需求？Ethernet、Wifi、3G？&lt;/li&gt;
&lt;li&gt;計劃中的產品硬體規格？&lt;/li&gt;
&lt;li&gt;有多少規模的人可以參與開發？&lt;/li&gt;
&lt;/ol&gt;&lt;br /&gt;
很明顯的，對於手機和行動裝置業者是這樣的答案：（他們都會選用 Android）&lt;br /&gt;
&lt;ol&gt;&lt;li&gt;用途不單一，需要讓使用者任意自行上網安裝軟體。&lt;/li&gt;
&lt;li&gt;是功能複雜的消費性電子產品，所以客戶多多少少能接受偶爾當機&lt;/li&gt;
&lt;li&gt;UI 需要有華麗的特效。開發人員都熟悉並使用 Java 的經驗。&lt;/li&gt;
&lt;li&gt;有所有網路的需求。&lt;/li&gt;
&lt;li&gt;擁有一定等級以上或是當前最頂級的 ARM 處理器。&lt;/li&gt;
&lt;li&gt;最少數十個，多半是上百人甚至上千人的研發人員。&lt;/li&gt;
&lt;/ol&gt;&lt;br /&gt;
&lt;br /&gt;
如果你是『非手機』和『非行動裝置』業者，你的產品可能不完全具備這些條件。建議您，一旦有任何一點不具備，請慎重考慮是否使用 Android 還是有機會選擇其他的解決方案。&lt;br /&gt;
&lt;br /&gt;
而當前的解決方案主要常見有三種：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;Android
Linux + Qt Framework
Linux + Own Application&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
限於篇幅，太細節的評估無法一一列舉，也要視實際狀況而定，但這邊有個簡易的初步評估方法可以提供參考：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;檢視產品的用途是否單純&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
如果你的產品用途不單純，應用不單一且不易規範使用範疇，Android 會是你首要的選擇。&lt;br /&gt;
&lt;br /&gt;
此外，假設你們的產品用途非常單純，又或者是功能並不複雜，建議您可以採用非 Android 的解決方案。理由如下：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;&lt;ol&gt;&lt;li&gt;Android本身有許多公認的問題，運氣好沒事，運氣不好這些問題都需要自行解決，由於可能出問題的範圍相當廣，要確保有足夠資源處理這些問題。&lt;/li&gt;
&lt;li&gt;Android有太多版本且隨時可能會有更新和問題修正，再加上上述問題，開發過程中不可能不追加資源去跟進或監視 Android 當前的狀態。&lt;/li&gt;
&lt;li&gt;如果從產品開發開始到開發結束，都需要人力資源去維護和跟隨，這無疑是一種人力上的消耗。&lt;/li&gt;
&lt;li&gt;到此為止還沒提到正題，產品本身應用的開發，在處理好以上問題之後，才開始準備進入正軌。&lt;/li&gt;
&lt;/ol&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
很多人常說，使用 Android 開發產品的時程是 Debug 的時間比寫軟體的時間多，有時甚至多達 10:1 都不為過。對於行動裝置廠商來說，整個系統就是產品的整體，花大量資源去維護可以說是理所當然的，但是對非行動裝置廠商來說，這就是額外的開銷甚至是條不歸路，除非您也想找筆者救火（但是筆者當救火員的日子可能不多了，笑）。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;後記&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
如果您也正在煩腦選擇作業系統的問題，希望這本文能幫上忙。</description><link>https://fred-zone.blogspot.com/2012/01/blog-post_07.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-1504757578733292085</guid><pubDate>Thu, 05 Jan 2012 16:32:00 +0000</pubDate><atom:updated>2012-01-11T13:19:48.331+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">MongoDB</category><category domain="http://www.blogger.com/atom/ns#">NodeJS</category><category domain="http://www.blogger.com/atom/ns#">Web 相關技術</category><title>NodeJS 與 MongoDB 的邂逅</title><description>雖然 NodeJS 的模組和開發資源相當多，但相關文件卻非常不足或是不完整，多半文獻都只著重於基礎的使用和片斷的說明，如果不去看原始程式碼，使用 NodeJS 來完成實用的網站，會有很大的困難度。對於已經有過 Web 開發經驗的人，轉換使用 NodeJS 不免也需要花一番功夫，過去經驗中許多的常用的功能，都仍要一一花大量時間嘗試才得以解決。而這樣的情況，對於開發者來說相當的糟，也是很多人重新再評估是否使用 NodeJS 的重點因素之一。有道是『一人得道，雞犬升天』，因此筆者未來將嘗試將自己的實際經驗，寫成一篇篇重點功能實作的文章和隨 Copy 即用的範例，減少其他人浪費同樣的時間再摸索。&lt;br /&gt;
&lt;br /&gt;
開發一個 Web 應用程式，最重要的莫過於資料庫的使用，過去 PHP 有 MySQL 當最佳夥伴，而現在 NodeJS 有 MongoDB 做最佳的組合。MongoDB 是 NoSQL 的代表之一，其採用 JSON/BSON 當做資料儲存和溝通的格式，亦使用 JavaScript 做為 Server-side 的執行程序語言（相當於傳統 RDBMS 的預儲程序），一切設計和習慣與 NodeJS 搭配使用起來，簡直絕配。若你對 MongoDB 的一些基本操作有疑問，可以先參考舊文『&lt;a href=&quot;http://fred-zone.blogspot.com/2011/07/mongodb.html&quot;&gt;MongoDB 快速筆記&lt;/a&gt;』。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;使用 MongoDB&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
MongoDB 擁有 NoSQL 的普遍特色，不用預先定義 Schema，所有的 database 和 collection（相當於傳統 RDBMS 的 Table），都會在新增資料後，自動被建立，我們只要專注於使用 NodeJS 操作資料庫即可。&lt;br /&gt;
&lt;br /&gt;
要在 NodeJS 裡使用 MongoDB，可以安裝 mongodb native driver，若透過 npm 來安裝：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;npm install mongodb&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
然後可以使用 NodeJS 建立 MongoDB connection pool ，做一些基礎的操作：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;var mongodb = require(&#39;mongodb&#39;);

var mongodbServer = new mongodb.Server(&#39;localhost&#39;, 27017, { auto_reconnect: true, poolSize: 10 });
var db = new mongodb.Db(&#39;mydb&#39;, mongodbServer);

/* open db */
db.open(function() {
    /* Select &#39;contact&#39; collection */
    db.collection(&#39;contact&#39;, function(err, collection) {
        /* Insert a data */
        collection.insert({
            name: &#39;Fred Chien&#39;,
            email: &#39;cfsghost@gmail.com&#39;,
            tel: [
                &#39;0926xxx5xx&#39;,
                &#39;0912xx11xx&#39;
            ]
        }, function(err, data) {
            if (data) {
                console.log(&#39;Successfully Insert&#39;);
            } else {
                console.log(&#39;Failed to Insert&#39;);
            }
        });

        /* Querying */
        collection.find({ name: &#39;Fred Chien&#39; }, function(err, data) {
            /* Found this People */
            if (data) {
                console.log(&#39;Name: &#39; + data.name + &#39;, email: &#39; + data.email);
            } else {
                console.log(&#39;Cannot found&#39;);
            }
        });
    });
});&lt;/pre&gt;&lt;/div&gt;註：這是 MongoDB 的基本常識，每當新增一筆資料，MongoDB 會自動幫該筆資料加上 _id 欄位，並給與一個唯一值（格式是 ObjectId），所以我們不需要像過去  使用 SQL Server 一般，自己刻意去定義一個 ID 欄位。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;預設的 ObjectId 範圍太小，改用 UUID 來當資料的唯一 ID&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
如果你過去有過 Web 開發經驗，到這邊肯定會開始有一些疑問欲求解，第一個問題肯定是『ObjectId 的數量極限？』。筆者在此不會回答這問題，因為這答案並不重要，想要準確知道答案，可以去『&lt;a href=&quot;http://www.mongodb.org/&quot;&gt;mongodb.org&lt;/a&gt;』尋找答案。&lt;br /&gt;
&lt;br /&gt;
比起上述問題，相信你應該更想問：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;預設的 ObjectId 適用的範圍？
如果日後資料庫要擴展(Scale)，是否有其他的 ID 解決方案可使用？&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
一般情況， MongoDB 預設的 ObjectId 就相當夠用了，但如果你是要建構大型的 Web Service 或是保留未來的擴充性，可使用 UUID 去代替 ObjectId。不過，因為 MongoDB 本身並不生成 UUID，若是要使用 UUID，就必需先自行產生好 UUID，然後在新增資料時指定生成好的 UUID 給 _id 欄位，讓 MongoDB 改用我們給的 ID 而不使用預設生成的 ObjectId。&lt;br /&gt;
&lt;br /&gt;
因為要自行產生 UUID，必需先為 NodeJS 安裝模組 node-uuid：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;npm install uuid&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
然後生成 UUID 並在 insert 時使用：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;var uuid = require(&#39;node-uuid&#39;);
var mongodb = require(&#39;mongodb&#39;);

var mongodbServer = new mongodb.Server(&#39;localhost&#39;, 27017, { auto_reconnect: true, poolSize: 10 });
var db = new mongodb.Db(&#39;mydb&#39;, mongodbServer);

/* open db */
db.open(function() {
    /* Select &#39;contact&#39; collection */
    db.collection(&#39;contact&#39;, function(err, collection) {

        /* Generate UUID(16 Bytes) and convert to BinaryData object for mongodb */
        var uuidBinary = new Buffer(uuid.v1({}, []));
        var id = mongodb.BSONPure.Binary(uuidBinary, mongodb.BSONPure.Binary.SUBTYPE_UUID);

        /* Insert a data with uuid */
        collection.insert({
            _id: id,
            name: &#39;Fred Chien&#39;,
            email: &#39;cfsghost@gmail.com&#39;
        }, function(err, data) {
            if (data) {
                console.log(&#39;Successfully Insert&#39;);
            } else {
                console.log(&#39;Failed to Insert&#39;);
            }
        });
    });
});&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
你可能會發現，在上面的範例程式中，我們將 UUID 轉成 MongoDB BSON 的 BinaryData 格式，這是為了效能考量，因為用純字串當做 Unique ID，在資料庫搜尋上會比 BinaryData Object 慢很多。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;儲存時間戳(Timestamp)&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
關於儲存時間的問題，如果你去各大 MongoDB 討論區詢問或查詢，通常大家都會告訴你不必做這件事，因為每一筆資料被建立後，自動產生的 ObjectId 就包含了建立的時間訊息，我們只要去學習如何從中去解析時間即可。但是，不單只是建立時間，有時我們會為資料加上各種不同的時間戳，如：更新時間等，所以，儲存時間戳還是必要的。&lt;br /&gt;
&lt;br /&gt;
雖然網路上相關 NodeJS 範例並不多，但 MongoDB 確實有 Timestamp 的資料結構可以用，我們可以這樣使用：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;var uuid = require(&#39;node-uuid&#39;);
var mongodb = require(&#39;mongodb&#39;);

var mongodbServer = new mongodb.Server(&#39;localhost&#39;, 27017, { auto_reconnect: true, poolSize: 10 });
var db = new mongodb.Db(&#39;mydb&#39;, mongodbServer);

/* open db */
db.open(function() {
    /* Select &#39;contact&#39; collection */
    db.collection(&#39;contact&#39;, function(err, collection) {

        /* Generate Timestamp and convert for mongodb */
        var ts = new Date().getTime();
        var i = ts % 1000;
        var t = new mongodb.BSONPure.Timestamp(i, Math.floor(ts * 0.001));

        /* Insert a data with uuid */
        collection.insert({
            name: &#39;Fred Chien&#39;,
            email: &#39;cfsghost@gmail.com&#39;,
            created: t
        }, function(err, data) {
            if (data) {
                console.log(&#39;Successfully Insert&#39;);
            } else {
                console.log(&#39;Failed to Insert&#39;);
            }
        });
    });
});&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;建立資料庫索引(Index)&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
過去有接觸過資料庫的人應該都很清楚，索引(Index)是能優化資料查詢速度的重要功能，MongoDB 同樣也有索引的設計。&lt;br /&gt;
&lt;br /&gt;
可以在 NodeJS 中，這樣為 name 欄位加上索引：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;collection.createIndex({ name: 1 });&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;後記&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
到此為止，你應該能開始盡情使用 NodeJS + MongoDB 開發 Web Service 了，開始享受完全的 JavaScript 開發人生吧！</description><link>https://fred-zone.blogspot.com/2012/01/nodejs-mongodb.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>4</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-2895245458453245322</guid><pubDate>Sun, 01 Jan 2012 19:43:00 +0000</pubDate><atom:updated>2012-01-02T04:49:39.777+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">心情筆記</category><title>苦其心志，勞其筋骨後，世界末日年快樂！</title><description>2012 新年快樂！今年格外不同，也是人稱世界末日將來臨的一年，而在這人生將結束的一年，不免開始思考今年的自我期許。 :-)&lt;br /&gt;
&lt;br /&gt;
回顧去年的風雨，當了一年的『救火隊長』。曾經為了救援朋友的案子，連續三個月，每天平均睡不到一小時，且平均連續工作達72小時以上，如果過程中突然一命嗚乎，一點也不意外。還好，這樣可怕的惡夢，都一一挺了過去，雖然身體似乎出現了點問題，不時陣痛襲來。不過，老天讓我完好的活下來，是祂已經頒發『超級救火員勛章』的最佳證明。&lt;br /&gt;
&lt;br /&gt;
我總相信，只要有信念和方向，任何困境都是老天賜予的挑戰，每當苦盡甘來，就會有許多更好的機運等著。事實上，當了一年職業救火員不是沒有好處，看到了許多人不為人知面貌，許多業界的問題和盲點，更認清了自己該做的事，也得到了更棒更成熟的點子。甚至，某個程度上，已經不害怕死亡突然降臨。&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;為什麼過的這樣辛苦？為什麼總是孤單的拼命？是否該檢討一下自己？&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
還清楚記得，這一年有些人這樣問過我，我都沒有正面回答，直到現在才有時間靜下來思考這個問題，也自我檢討了一番。&lt;br /&gt;
&lt;br /&gt;
如果因為負責任而辛苦減壽，為了理想而拼了命和全世界作對，那我真的想不出有任何一點不正確的地方。我只能推論，許多人不願意辛苦，甚至可以『不負責任』去避免辛苦；許多人沒有懷抱理想，所以沒有拼命的目標，只有隨波逐流的生活。&lt;br /&gt;
&lt;br /&gt;
真的要自我檢討，除了憤世忌俗外，就是我死腦筋的總是依據一件事的『順利和成就』，考量應該做什麼，甚至不是自己的義務，也會執行到底，貫徹著父親所囑付：『幫忙之前叫幫忙，幫忙之後叫責任。』然而，許多人理所當然以為處處都是我的義務和責任，一股惱全加諸於我身上。使我，做會自己勞累不堪，不做則內心不舒服，痛苦萬分。&lt;br /&gt;
&lt;br /&gt;
而這樣的個性，讓我和傳統做生意一點都不搭軋，因為我永遠不愛也不喜歡打『把對手權利變成對手義務』的拉拒戰。在這樣的商場戰爭下，人人都不想執行自己的義務，只想行使自己的權利，甚至讓對方的權利變成自己的權利。不過，雖然不喜歡，但不代表我不懂，每次回歸原點和原貌來看，就會發現『人有多不要臉』和『許多醜陋的臉孔』的戲碼都正在天天上演，過程中的種種掩飾都讓人不敢相信，任誰看了都會臉紅找洞鑽。&lt;br /&gt;
&lt;br /&gt;
如果這樣才能不辛苦不孤獨，我寧可苦一輩子。我也永遠不相信，那樣的結黨，能夠有什麼未來。此外，如此彆扭的生意，也不是我所期望的目標。&lt;br /&gt;
&lt;br /&gt;
過去無數個日子，腦袋從未停止轉動過。啄磨過許多想法，也思考過許多問題，思考中入眠已經是常態。堅信，苦其心志，勞其筋骨，然後，痛痛快快又能無後顧之憂的完成件『應該做的事』，這就是今年的自我期許。</description><link>https://fred-zone.blogspot.com/2012/01/blog-post.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-1730665112095651505</guid><pubDate>Thu, 29 Dec 2011 09:14:00 +0000</pubDate><atom:updated>2011-12-29T17:26:12.126+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Startup</category><category domain="http://www.blogger.com/atom/ns#">心情筆記</category><title>【Startup 談心酸】 當革命情感對上體制</title><description>我總認為，當別人問起職業，回答『Startup(新創)』是一件很自傲的事。雖然公司不大，但是從兩手空空開始，完成理想的感覺真的是棒呆了！你必需經歷『養活自己』、『勒緊褲帶』、『想破腦袋』還有『革命不怕失敗』的種種過程，最重要的是，伴隨而來的『人的問題』，很有可能成為最後一根稻草，壓垮我們。但每當跨越過每一個階段，彷彿就吃了甜美多汁的果實，相當有滋味。&lt;br /&gt;
&lt;br /&gt;
常聽到有人說：『先不談成功與否，Startup能做超過一年，就相當不容易了。』，其實是事實，從創業中敗陣下來的人不在少數，敗陣的原因更不勝枚舉，有人是受不了壓力，有人把持不住Cash Flow（現金流），有人經營心態不正確，或是 idea 失敗等等。但是，這些原因真的都比不上『同伴』所造成的打擊。&lt;br /&gt;
&lt;br /&gt;
如果你不是一個人創業，而是三五好友有志一同，那我要恭喜你，畢竟要能找到方向相同，又有足夠能力的人一起是非常困難的。但是，我也要為你感到遺憾，因為『人的問題』會帶來更多波折和困難，造成創業失敗的比例更高。『同伴』是個雙刃劍，雖能共患難，有革命情感，但也會因決裂而一夕之間毀滅。&lt;br /&gt;
&lt;br /&gt;
這裡有一個朋友的小故事，相當常見，或許大家都很熟悉其中情節：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;有一群人在學校就是好朋友，而所會的技術也讓他們相當出類拔粹，於是，他們孕釀出創業的想法。為了小試身手，這些人開始共同接一些小案子，並培養革命情感。看似一切很美好，但在過程中問題出現了，有人怠惰（或是能力不足）造成其他人負擔增加，又或者是因此造成案子無法順利結案。好不容易，其他隊員們跳出來『Cover』，讓一切過了關，客戶也付了錢。

然而，問題才正要發燒，在分配利益時，有人開始提出不滿：『為什麼出包的同伴，可以拿的錢和沒出包的人一樣多？為什麼救火的人，幫出包的人完成不是自己的大部份工作，卻只拿到原本的待遇？』此外，對工作的分配也頗有微詞，有人認為自己做的工作，相對困難許多，應該分到更多的錢。更誇張的是，每一個人都生怕別人多拿或是拿的比自己多。

還好，為了讓大家不要決裂，盡可能滿足大家的要求，有人跳出來處理這件事，除了自己掏腰包，甚至動用了原本大家講好要保留下來的『公基金』。然後發現，雖然大家都同意切出一部份成為公基金，但心中仍然認定這公基金有一部份是屬於自己的，隨時都可以要回去，有人更不時想動用或拿回這個錢。一旦動用公基金，就會牽動到大夥的神經，引發更多莫明其妙的問題。

問題並沒有因為時間久而消失，之後的幾次案子，多多少少都出現類似的狀況，磨光了真正想做事的人，其心中的熱情。最終，革命情感不敵絕望，因看不到未來而拆夥。&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
這個劇情也許正在你的團隊中上演，更或者有更多超出這故事內容的問題發生。總而言之，問題出在於每個人都不肯讓自己的利益有任何損傷，也只能看到自己眼前的利益。就算有人出來願意奉獻，也只是減緩問題發酵，並沒有真正解決問題。一旦緩充的空間消耗殆盡，這個團隊就走向末路。&lt;br /&gt;
&lt;br /&gt;
很多人會說，大家都是好朋友，也都有相同志向，過程中難免會有些問題，我們都可以不用太計較，這樣也有共患難的革命情感。但事實上，人性很難抵抗，已經有太多人實驗過，都證明了『不計較才計較』。只是有人爆發的點比較高，平時看不出來，可是一旦爆發，便天崩地裂跟你沒完。想想看，連親密的親人都可以為了遺產殺紅了眼，更何況只是『普通朋友』。&lt;br /&gt;
&lt;br /&gt;
最後你會發現，革命情感是解決不了任何事，正確的建立公司體制是最好的方式，雖然很死板無情，但不容任何人有第二句話，也給參與者有人性上的約束，前人所遺留下來的方式，是有其道理的。</description><link>https://fred-zone.blogspot.com/2011/12/startup.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-6201160083333968898</guid><pubDate>Sun, 25 Dec 2011 09:21:00 +0000</pubDate><atom:updated>2011-12-26T17:56:28.552+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Linux Kernel</category><category domain="http://www.blogger.com/atom/ns#">作業系統實作</category><category domain="http://www.blogger.com/atom/ns#">自由軟體</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><title>從 Linux Kernel 出發看！探討 Process 組成結構！</title><description>對所有電腦使用者而言，執行一支應用程式就像吃蛋糕一樣簡單。對於程式開發者而言，寫支 Hello World Program 並跑起來，也是三十秒內可以完成的事。但是，這樣容易的動作，其實底層有著複雜的機制，否則短短十行程式碼的 Hello World 能夠動起來，真的是奇跡了。想要知曉程式是怎麼被作業系統執行，要追溯到 Process 的機制，在研究過 Linux Kernel 的 Process 相關機制後，一切就將明朗。&lt;br /&gt;
&lt;br /&gt;
&lt;span class=&quot;Apple-style-span&quot; style=&quot;color: #666666;&quot;&gt;註：在本篇文章內，都將會假設 binary program 已經被作業系統解析並放到記憶體執行，說明只著重於 Process 的部份 。雖然 Process 與 binary program 和其載體息息相關，但本文不會討論 ELF 的細節，有興趣的讀者可以自己去尋找相關文件閱讀。&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
若你是程式開發者，對『Process（程序）』一詞應該不陌生。嚴格來說， Process 就是處於執行狀態的程式，如果參考一些原文書或英文文獻，它們也許會這樣定義：『Process is a program in execution』。所以我們可以認定，Process 的組成，是擁有著『程式執行檔的 binary code + 記錄資料的記憶體』。&lt;br /&gt;
&lt;br /&gt;
本文將 Process 分成內外兩個角度來探討：&lt;br /&gt;
&lt;ol&gt;&lt;li&gt;從 Process 內部，程式執行的觀點&amp;nbsp;&lt;/li&gt;
&lt;li&gt;從 Process 外部，也就是作業系統的觀點&amp;nbsp;&lt;/li&gt;
&lt;/ol&gt;&lt;br /&gt;
&lt;b&gt;程式執行的觀點&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
單純以 Process 內部的角度來看，Process 就是一般的程式碼被放到記憶體執行。曾於舊文『&lt;a href=&quot;http://fred-zone.blogspot.com/2010/12/linux.html&quot;&gt;Linux 下程序的記憶體映射&lt;/a&gt;』略為提及，一支程式擁有著數個 segments（區段），並仰賴著這些 segment 來持續運作。大致上來說，每個 segment 有不同的用途：&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;Code Segment - 存放主要程式&lt;/li&gt;
&lt;li&gt;Data Segment -&amp;nbsp;存放已被初始化並賦予值的全域變數&lt;/li&gt;
&lt;li&gt;BSS Segment - 紀錄尚未被賦予值的全域變數&lt;/li&gt;
&lt;li&gt;Stack Segment(Stack/Heap) - 紀錄 Process 在執行時動態註冊的變數包括 function 中的 local variable&lt;/li&gt;
&lt;/ul&gt;&lt;br /&gt;
對程式本身來說，會動態並隨機使用的是 Stack Segment，程式向作業系統(OS) 要記憶體空間後，就可以在 Stack Segment 讀寫該記憶體空間。而實際上 OS 底層的行為，是配置記憶體， 然後映射到 Stack Segment 供程式使用。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;作業系統的觀點&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
與程式觀點相呼應，作業系統有記憶體管理機制，其建立虛擬記憶體空間，將程式對映進去該空間後，開始執行。作業系統管理著 Process 所擁有的 Segments，為每個 Segment 都配置了『虛擬記憶體區域(VMA, Virtual Memory Area)』。&lt;br /&gt;
&lt;br /&gt;
在 Linux 之下，我們可以透過 cat /proc/&amp;lt;Process ID&amp;gt;/maps 的方式去取得一個 Process 所用到的記憶體，以下是觀察系統上的 bash（PID=18165）：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;$ cat /proc/18165/maps
&lt;b&gt;08048000-0810a000 r-xp 00000000 08:01 1851399 &amp;nbsp; &amp;nbsp;/bin/bash&lt;/b&gt;
&lt;b&gt;0810a000-0810f000 rw-p 000c1000 08:01 1851399 &amp;nbsp; &amp;nbsp;/bin/bash&lt;/b&gt;
0810f000-08114000 rw-p 00000000 00:00 0
&lt;b&gt;08752000-08a46000 rw-p 00000000 00:00 0 &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;[heap]&lt;/b&gt;
b737b000-b7396000 r--p 00000000 08:01 28598806 &amp;nbsp; /usr/share/locale/zh_TW/LC_MESSAGES/libc.mo
b7396000-b73a0000 r-xp 00000000 08:01 12755293 &amp;nbsp; /lib/i386-linux-gnu/i686/cmov/libnss_files-2.13.so
b73a0000-b73a1000 r--p 00009000 08:01 12755293 &amp;nbsp; /lib/i386-linux-gnu/i686/cmov/libnss_files-2.13.so
b73a1000-b73a2000 rw-p 0000a000 08:01 12755293 &amp;nbsp; /lib/i386-linux-gnu/i686/cmov/libnss_files-2.13.so
b73a2000-b73b5000 r-xp 00000000 08:01 12755189 &amp;nbsp; /lib/i386-linux-gnu/i686/cmov/libnsl-2.13.so
b73b5000-b73b6000 r--p 00012000 08:01 12755189 &amp;nbsp; /lib/i386-linux-gnu/i686/cmov/libnsl-2.13.so
b73b6000-b73b7000 rw-p 00013000 08:01 12755189 &amp;nbsp; /lib/i386-linux-gnu/i686/cmov/libnsl-2.13.so
b73b7000-b73b9000 rw-p 00000000 00:00 0
b73ca000-b73cc000 r--p 00000000 08:01 28600275 &amp;nbsp; /usr/share/locale/zh_TW/LC_MESSAGES/bash.mo
b73cc000-b73d3000 r--s 00000000 08:01 28666899 &amp;nbsp; /usr/lib/i386-linux-gnu/gconv/gconv-modules.cache
b73d3000-b754a000 r--p 00000000 08:01 28615144 &amp;nbsp; /usr/lib/locale/locale-archive
b754a000-b754b000 rw-p 00000000 00:00 0
b754b000-b7568000 r-xp 00000000 08:01 12755040 &amp;nbsp; /lib/i386-linux-gnu/libtinfo.so.5.9
b7568000-b756a000 r--p 0001c000 08:01 12755040 &amp;nbsp; /lib/i386-linux-gnu/libtinfo.so.5.9
b756a000-b756b000 rw-p 0001e000 08:01 12755040 &amp;nbsp; /lib/i386-linux-gnu/libtinfo.so.5.9
b756b000-b76be000 r-xp 00000000 08:01 12755289 &amp;nbsp; /lib/i386-linux-gnu/i686/cmov/libc-2.13.so
b76be000-b76bf000 ---p 00153000 08:01 12755289 &amp;nbsp; /lib/i386-linux-gnu/i686/cmov/libc-2.13.so
b76bf000-b76c1000 r--p 00153000 08:01 12755289 &amp;nbsp; /lib/i386-linux-gnu/i686/cmov/libc-2.13.so
b76c1000-b76c2000 rw-p 00155000 08:01 12755289 &amp;nbsp; /lib/i386-linux-gnu/i686/cmov/libc-2.13.so
b76c2000-b76c6000 rw-p 00000000 00:00 0
b76c6000-b76c8000 r-xp 00000000 08:01 12755049 &amp;nbsp; /lib/i386-linux-gnu/i686/cmov/libdl-2.13.so
b76c8000-b76c9000 r--p 00001000 08:01 12755049 &amp;nbsp; /lib/i386-linux-gnu/i686/cmov/libdl-2.13.so
b76c9000-b76ca000 rw-p 00002000 08:01 12755049 &amp;nbsp; /lib/i386-linux-gnu/i686/cmov/libdl-2.13.so
b76ca000-b76ec000 r-xp 00000000 08:01 12755034 &amp;nbsp; /lib/i386-linux-gnu/libncurses.so.5.9
b76ec000-b76ed000 r--p 00021000 08:01 12755034 &amp;nbsp; /lib/i386-linux-gnu/libncurses.so.5.9
b76ed000-b76ee000 rw-p 00022000 08:01 12755034 &amp;nbsp; /lib/i386-linux-gnu/libncurses.so.5.9
b76f4000-b76fd000 r-xp 00000000 08:01 12754995 &amp;nbsp; /lib/i386-linux-gnu/i686/cmov/libnss_nis-2.13.so
b76fd000-b76fe000 r--p 00008000 08:01 12754995 &amp;nbsp; /lib/i386-linux-gnu/i686/cmov/libnss_nis-2.13.so
b76fe000-b76ff000 rw-p 00009000 08:01 12754995 &amp;nbsp; /lib/i386-linux-gnu/i686/cmov/libnss_nis-2.13.so
b76ff000-b7705000 r-xp 00000000 08:01 12755103 &amp;nbsp; /lib/i386-linux-gnu/i686/cmov/libnss_compat-2.13.so
b7705000-b7706000 r--p 00005000 08:01 12755103 &amp;nbsp; /lib/i386-linux-gnu/i686/cmov/libnss_compat-2.13.so
b7706000-b7707000 rw-p 00006000 08:01 12755103 &amp;nbsp; /lib/i386-linux-gnu/i686/cmov/libnss_compat-2.13.so
b7707000-b7708000 r--p 00176000 08:01 28615144 &amp;nbsp; /usr/lib/locale/locale-archive
b7708000-b770a000 rw-p 00000000 00:00 0
b770a000-b770b000 r-xp 00000000 00:00 0 &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;[vdso]
b770b000-b7726000 r-xp 00000000 08:01 12755299 &amp;nbsp; /lib/i386-linux-gnu/ld-2.13.so
b7726000-b7727000 r--p 0001b000 08:01 12755299 &amp;nbsp; /lib/i386-linux-gnu/ld-2.13.so
b7727000-b7728000 rw-p 0001c000 08:01 12755299 &amp;nbsp; /lib/i386-linux-gnu/ld-2.13.so
&lt;b&gt;bf916000-bf937000 rw-p 00000000 00:00 0 &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;[stack]&lt;/b&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
大致上來說可以看到有這些 VMA 的存在（已省略會偏離主題的 VMA）：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;08048000-0810a000 &amp;nbsp;  Code
0810a000-0810f000 &amp;nbsp;  Data
08752000-08a46000  &amp;nbsp; Heap
bf916000-bf937000  &amp;nbsp; Stack&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
對於作業系統而言，管理這些記憶體和程式狀態，才是真正的重點。所以，就作業系統核心的角度來看 Process，可以從排程的程式來切入瞭解。翻閱 Linux Kernel&amp;nbsp;原始程式碼的檔案『&lt;a href=&quot;http://git.kernel.org/?p=linux/kernel/git/next/linux-next.git;a=blob_plain;f=include/linux/sched.h;hb=HEAD&quot;&gt;include/linux/sched.h&lt;/a&gt;』，Process 的狀態結構被定義在&amp;nbsp;struct task_struct 之中，而其中的 mm 就是記錄著該 Process 的記憶體配置資訊，我們可以從中得到 Process 當前所擁有的全部 VMA 和 Segments 所在位址：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;struct task_struct {
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ...
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; struct mm_struct *mm, *active_mm;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ...
};&lt;/pre&gt;&lt;/div&gt;&lt;div&gt;&lt;pre&gt;struct mm_struct {
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; struct vm_area_struct * mmap; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; /* list of VMAs */
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ...
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; unsigned long start_code, end_code, start_data, end_data;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; unsigned long start_brk, brk, start_stack;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ...
};&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
另外，從『&lt;a href=&quot;http://git.kernel.org/?p=linux/kernel/git/next/linux-next.git;a=blob_plain;f=include/linux/mm.h;hb=HEAD&quot;&gt;include/linux/mm.h&lt;/a&gt;』可以找到 VMA 的串列資料結構定義：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;struct vm_area_struct {
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; struct mm_struct * vm_mm; &amp;nbsp; &amp;nbsp; &amp;nbsp; /* The address space we belong to. */
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; unsigned long vm_start; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; /* Our start address within vm_mm. */
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; unsigned long vm_end; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; /* The first byte after our end address
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;within vm_mm. */
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ....
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; /* linked list of VM areas per task, sorted by address */
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; struct vm_area_struct *vm_next;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; ....
}&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
&lt;b&gt;後記&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
若要繼續追查下去，還有動態連結的部份要探討，會牽涉到更多機制，由於篇幅有限，下次有時間再來寫完。</description><link>https://fred-zone.blogspot.com/2011/12/linux-kernel-process.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-9084482339625306410</guid><pubDate>Fri, 23 Dec 2011 05:18:00 +0000</pubDate><atom:updated>2011-12-23T13:55:08.153+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Debian</category><category domain="http://www.blogger.com/atom/ns#">Express Web Framwork</category><category domain="http://www.blogger.com/atom/ns#">Linux</category><category domain="http://www.blogger.com/atom/ns#">NodeJS</category><category domain="http://www.blogger.com/atom/ns#">NPM</category><category domain="http://www.blogger.com/atom/ns#">Web 相關技術</category><title>如何在 Debian 建置 NodeJS + Express 環境</title><description>截至目前為止，只有 Debian Sid(unstable) 提供 Nodejs 和 Express&amp;nbsp;套件可讓使用者直接安裝。但是，在 Server 的環境之下，一般都使用 Debian 5.0/6.0 (Lenny/Squeeze) 穩定版的系統，因此沒有套件可以直接安裝，唯一的方法就是自己下載編譯 Nodejs。還好自己手動安裝的過程並不困難，幾個步驟就可以完成。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;安裝 Nodejs&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
更新系統並安裝編譯 Nodejs 所需的套件：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;sudo apt-get update
sudo apt-get install git-core curl build-essential openssl libssl-dev&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
從 Git Repository 下載 Nodejs 原始碼：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;git clone https://github.com/joyent/node.git&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
進入 Nodejs 原始碼目錄，並切選擇我們要的版本（截至本文，0.6.6 是最新版）：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;cd node
git checkout v0.6.6

# Note: 可以使用&amp;nbsp;git tag 看到 Nodejs 所有的版本列表&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
編譯並安裝 Nodejs （預設會裝到 /usr/local/lib/node）：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;./configure
make
sudo make install&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
如果安裝過程中沒有任何問題，就可以使用 node 指令查看 Nodejs 的版本了：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;node -v&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
手動設定公用的 Nodejs Module 路徑：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;echo &quot;NODE_PATH=/usr/local/lib/node_modules&quot; &amp;gt;&amp;gt; .bashrc

# Note: 如果想讓所有 Server 上的 user 都套用設定，可以放在 /etc/profile&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;安裝 Express Web Framework&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
使用 npm 安裝 express（使用 -g 選項會安裝到公用的目錄&amp;nbsp;/usr/local/lib/node_modules）：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;sudo npm install express -g&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Express 通常預設使用 jade template engine，也需要手動安裝：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;sudo npm install jade -g&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
安裝完成後，就可以立即測試 nodejs + express：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;mkdir test
cd test
express
node app.js&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
如果從瀏覽器可以看到畫面，就代表安裝成功：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;http://ServerIP:3000/&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
&lt;b&gt;後記&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
截至本文，Debian Sid 的 Nodejs 套件還是 0.4.12 版本，如果想要用新的 Nodejs，就要透過本文的方法來安裝。</description><link>https://fred-zone.blogspot.com/2011/12/debian-nodejs-express.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-6242517634508437513</guid><pubDate>Tue, 20 Dec 2011 00:16:00 +0000</pubDate><atom:updated>2012-01-11T13:20:26.849+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Express Web Framwork</category><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">NodeJS</category><category domain="http://www.blogger.com/atom/ns#">Web 相關技術</category><category domain="http://www.blogger.com/atom/ns#">程式心得筆記</category><category domain="http://www.blogger.com/atom/ns#">自由軟體</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><title>Nodejs + Express 的 Route 流程規劃</title><description>如大多數人的習慣，往往我們都把 Route 的路徑都定義在 app.js 當中。Express 所提供的眾多範例程式中，雖然有將 Route 抽出來放在 routes 目錄下的實作方式，但也太簡單，只支援一層的設計。這意味著，如果 routes 之下有更多子目錄，放著更多的 route 設定，都是不會被讀取的。&lt;br /&gt;
&lt;br /&gt;
這邊做了簡單的修改，利用遞迴的做法，掃描所有的子目錄，然後去載入所有的 js 檔案以擴充 Route 的設定。&lt;br /&gt;
&lt;br /&gt;
以下是 route.js 的程式碼：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;var vm = require(&#39;vm&#39;);
var fs = require(&#39;fs&#39;);

module.exports = function(app) {
  var dir = __dirname + &#39;/routes&#39;;
  var context = {
    app: app,
    require: require
  };
  var newContext;

  /* Initializing Context */
  for (var key in global)
    context[key] = global[key];

  newContext = vm.createContext(context);

  /* Loading all routes */
  loadRouteDir(newContext, dir);
};

function loadRouteDir(context, path) {
  /* Scanning files */
  fs.readdirSync(path).forEach(function(file) {
    loadRouteFile(context, path, file);
  });
}

function loadRouteFile(context, path, file) {
  var fullpath = path + &#39;/&#39; + file;

  fs.stat(fullpath, function(err, stats) {
    if (stats.isFile()) {
      /* Loading route */
      var str = fs.readFileSync(fullpath, &#39;utf8&#39;);
      vm.runInContext(str, context, file);
    } else if (stats.isDirectory()) {
      /* Loading sub-directory */
      loadRouteDir(context, fullpath);
    }
  });
}&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
在 app.js 中只要這樣使用即可：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;/* app is Express Server  */
require(&#39;./route&#39;)(app);&lt;/pre&gt;&lt;/div&gt;</description><link>https://fred-zone.blogspot.com/2011/12/nodejs-express-route.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-5147006748348510455</guid><pubDate>Thu, 08 Dec 2011 00:25:00 +0000</pubDate><atom:updated>2011-12-08T10:13:56.597+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Open Hardware</category><category domain="http://www.blogger.com/atom/ns#">openmoko</category><category domain="http://www.blogger.com/atom/ns#">商業</category><category domain="http://www.blogger.com/atom/ns#">心情筆記</category><category domain="http://www.blogger.com/atom/ns#">手機平台</category><category domain="http://www.blogger.com/atom/ns#">自由軟體</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><title>Openmoko 永遠活在我們的心中</title><description>還記得 2006 年 12 月業界出現了一個瘋狂的想法：『Openmoko 的開放手機』，但就在 2011 年 12 月滿五年，這家公司(Openmoko, Inc) 也將劃下了一個圓滿的句點。&lt;br /&gt;
&lt;br /&gt;
回顧過去，Openmoko 不只是單純的軟體層面開放，而是連硬體設計的細節都公開。這對當時的大環境，高端手機技術和 Know-how 都被嚴密掌握在特定公司的時局下，無疑是個瘋狂的舉動。想當然，Openmoko 花了大量錢財與人力物力，還有國內外社群的力量，終於做出了自己的第一代手機原型（參考圖 Neo 1973），爾後又開發出第二代硬體加強版的機種（參考圖 Neo FreeRunner）。&lt;br /&gt;
&lt;br /&gt;
&lt;table cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;float: right; margin-left: 1em; text-align: right;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;http://upload.wikimedia.org/wikipedia/commons/7/7a/1112FIC326x550.jpg&quot; imageanchor=&quot;1&quot; style=&quot;clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;320&quot; src=&quot;http://upload.wikimedia.org/wikipedia/commons/7/7a/1112FIC326x550.jpg&quot; width=&quot;188&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Neo 1973&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
還記得，當時大多數人都還是使用功能手機(Feature phone)，智慧型手機(Smart Phone)也還停留在被難用 Windows CE 稱霸的時代，誰也還沒有想到未來會有 iPhone 和 Android Phone，對絕大多數人而言，手機是個神祕且只有少數人可以參與開發的領域。有趣的是，Openmoko 除了做開放的手機之外，也對應用程式(Apps)安裝於手機上的想法有了初步的構想，而這些想法，都觸動了不少軟體開發者和投資人的目光。&lt;br /&gt;
&lt;br /&gt;
雖然直到最後，Openmoko 的手機還不足以讓一般消費者順暢使用，但許多構想，無論是開放手機，有擴充性的應用程式平台，還是使用 Linux 開發前衛的智慧型手機，都確實已深埋在不少人心中。世界各地的 Hacker 和高手，無一不想擁有這樣一台的開放手機。&lt;br /&gt;
&lt;br /&gt;
Openmoko 也在教育界扎根，除了提供開發的教育訊練外，也去拜訪了許多大專院校，讓許多原本沒有機會碰到手機開發的學生和技術人員，有機會一睹這神秘的領域。然後，也有不少學校買了 Openmoko 的手機，做為開發教學使用，直至今天，這些手機還是相當好的開發教材，甚至，也已經有不少教授在這上面開發了一系列教材。（如：淡江大學資工系已經將 Openmoko 手機當成 C 語言教學和相關基礎技術的實作平台）&lt;br /&gt;
&lt;table cellpadding=&quot;0&quot; cellspacing=&quot;0&quot; class=&quot;tr-caption-container&quot; style=&quot;float: left; margin-right: 1em; text-align: left;&quot;&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: center;&quot;&gt;&lt;a href=&quot;http://upload.wikimedia.org/wikipedia/commons/e/e7/Freerunner02.png&quot; imageanchor=&quot;1&quot; style=&quot;clear: right; margin-bottom: 1em; margin-left: auto; margin-right: auto;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;320&quot; src=&quot;http://upload.wikimedia.org/wikipedia/commons/e/e7/Freerunner02.png&quot; width=&quot;280&quot; /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class=&quot;tr-caption&quot; style=&quot;text-align: center;&quot;&gt;Neo FreeRunner&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;br /&gt;
隨著 iPhone 的問市，Google Android 的出現，Openmoko 逐漸失去了媒體焦點，也有不少不為人知的事情，讓公司逐漸縮編並轉型（之後轉做過 WikiReader），以致現在的人，多半都只是耳聞，漸漸忘了當時 Openmoko&amp;nbsp;花大錢招頂級人才，遍布全世界的開發者和分公司，有如全身鍍金般受各界祝福和朝聖的風光。&lt;br /&gt;
&lt;br /&gt;
就在最旺的時候，筆者曾經有幸為 Openmoko 工作過一陣子，雖然只是在家工作，很少去公司，和裡面的人員甚少當面接觸，但也為曾在那工作感到驕傲。為此，看著 Openmoko 的殞落，聽到 Openmoko 將結束的消息，不免有些震驚和失落感，當年筆者離開時的不悅，瞬間煙消雲散，之中衝突原由已不需要再提及。&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
最近筆者回鍋，回去幫忙 Openmoko 做一些案子，原本只打算幫忙不打算談錢，但結果最後被&amp;nbsp;Openmoko 的創始人&amp;nbsp;Sean Moss-Pultz 硬逼著接下案子，成為了 Openmoko 名符其實的最後一個案子外包者，真不知該哭還該笑。&lt;br /&gt;
&lt;br /&gt;
至於之後會如何？Sean 將回到事業原點，與我們一樣同為新創(Startup)界的一員，落實他全新的想法。在此祝福&amp;nbsp;Sean，能再接再勵，帶著過去的經驗，創出一番新事業。而 Openmoko，將永遠活在我們的心中。</description><link>https://fred-zone.blogspot.com/2011/12/openmoko.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-3143903431514190514</guid><pubDate>Sat, 03 Dec 2011 02:33:00 +0000</pubDate><atom:updated>2011-12-05T20:55:56.073+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Android</category><category domain="http://www.blogger.com/atom/ns#">Java</category><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">程式心得筆記</category><category domain="http://www.blogger.com/atom/ns#">系統研發手札</category><title>Android 問題百出之 2.3.x 的 JavaScript Interface</title><description>有鑑於 Android 問題太多，只好定了個系列標題『Android 問題百出』當開頭，並將碰到的問題和解決或避開的方法記錄在內。話說回來，筆者個人其實相當討厭 Android，自 Android 出現以來，從未真的投入其中並賺過什麼錢，會接觸，多半是興趣玩弄或是幫一些朋友的公司臨時打工救火。不過，既然是救火，任何千奇百怪的問題或狀況都會遭遇到，甚至還得『限時』解決別人解決不了的問題。&lt;br /&gt;
&lt;br /&gt;
這次碰到的問題就是 Android 2.3.x Gingerbread 缺少 JavaScript Interface 的實作，如果你的應用程式有自己實作 API 供 JavaScript 程式使用，那這些 API 將會完全失效。而這樣的問題，對於使用 Web 技術（HTML5 + Javascript）的應用程式來說，相當嚴重。&lt;br /&gt;
&lt;br /&gt;
話說 Android 在 2.3 版本之後，採用了 V8 做為 JavaScript Engine。在快速掃過 Android 關於 WebView 和 WebKit 的程式碼後，發現不幸的是『Google 再次未將程式寫完』。但這一次，不只是功能未完成，而是將原本可以用的功能（在 Android 2.2），變成不能用。這讓筆者非常不能理解，為何 Google 換了 V8 Engine 後，明知功能沒有改完，卻仍將這部份實作隨新版 Android 釋出？然後，絕大多數廠商，在毫無感覺之下，直接繼承了這樣的 Bug。&lt;br /&gt;
&lt;br /&gt;
對硬體裝置的廠商來說，重新實作這個 JavaScript Interface 支援是最佳的解決辦法，髒一點的方法是將 V8 改回舊的 JSCore。但很可惜的是，就算有人改好了，也沒有廠商願意釋出這部份的程式碼。（所以才會不停有人願意花大錢找筆者這種臨時救火工呀。）&lt;br /&gt;
&lt;br /&gt;
此外，如果你是一般的應用程式開發者，因為動不了底層，則完全無解。但是，這邊有個 Workaround，可以暫時代替 JavaScript Interface，讓應用程式開發者可以避開這問題（以下假設自定的 JavaScript Interface Class 為 myAPI）。&lt;br /&gt;
&lt;br /&gt;
定義將會用到的變數：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;/* Define private variable */
private static WebView mWebView;
private static myAPI mJSIF;
private static boolean javascriptInterfaceBroken = false;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
初始化 WebView 時加上 Workaround：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;/* Initializing WebView */
mWebView = (WebView) findViewById(R.id.supermark_dialog);
mWebView.getSettings().setJavaScriptEnabled(true);

&lt;b&gt;/* Check Android version */
try {
    if (Build.VERSION.RELEASE.contains(&quot;2.3.&quot;)) {
        javascriptInterfaceBroken = true;
    }
} catch (Exception e) { }&lt;/b&gt;

/* Create own JavaScript Interface */
mJSIF = new myAPI(mWebView);

&lt;b&gt;if (!javascriptInterfaceBroken) {&lt;/b&gt;
    mWebView.addJavascriptInterface(mJSIF, &quot;Fred&quot;);
&lt;b&gt;} else {
    /* Workaround to add JavaScript Interface to webview for Fucking Android 2.3 */
    mWebView.setWebViewClient(new WebViewClient() {
        @Override
        public void onPageFinished(WebView view, String url) {
            super.onPageFinished(view, url);
            if (javascriptInterfaceBroken) {
                String handleGingerbreadStupidity =
                    &quot;javascript: function do() {window.location=&#39;http://Fred:do:null&#39;;};&quot; +
                    &quot;javascript: function handler() {&quot; +
                    &quot;this.do=do;&quot; +
                    &quot;}; &quot; +
                    &quot;javascript: var Fred = new handler();&quot;;
                view.loadUrl(handleGingerbreadStupidity);
            }
        }

        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            if (javascriptInterfaceBroken) {
                if (url.contains(&quot;Fred&quot;)) {
                    /* Parsing URL */
                    StringTokenizer st = new StringTokenizer(url, &quot;:&quot;);
                    st.nextToken();
                    st.nextToken();
                    String function = st.nextToken();
                    String parameter = st.nextToken();

                    /* Call function */
                    if (function.equals(&quot;do&quot;)) {
                        mJSIF.do();
                    }
                }

                return true;
            }

            return false;
        }
    });
}&lt;/b&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
最後，我們還是可以如使用原生的 JavaScript Interface 一般，在 HTML5 + JavaScript 中呼叫自定的 API：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;&amp;lt;script&amp;gt;
window.Fred.do();
&amp;lt;/script&amp;gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
簡單來說，就是創造假的 URL 和相應的 Parser，去接收從 JavaScript 傳來的要求。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;後記&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
如果說一直專心並精通於某樣領域的人稱之為專家，那筆者肯定不是 Android 專家。但是，好在長久來與 Open Source 拼命所練就出來的功夫，無論什麼樣的火，總算都能夠快速找到火源和撲滅，應該可以算是滅火專家吧。 :-D&lt;br /&gt;
&lt;br /&gt;</description><link>https://fred-zone.blogspot.com/2011/12/android-23x-javascript-interface.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>11</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-8260364942701193043</guid><pubDate>Sun, 27 Nov 2011 01:06:00 +0000</pubDate><atom:updated>2011-11-27T09:46:35.076+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Express Web Framwork</category><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">NodeJS</category><category domain="http://www.blogger.com/atom/ns#">Web 相關技術</category><category domain="http://www.blogger.com/atom/ns#">程式心得筆記</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><title>NodeJS + Express 實作檔案上傳</title><description>『&lt;a href=&quot;http://expressjs.com/&quot;&gt;Express Web Framework&lt;/a&gt;』是基於『Connect Middleware Framework』所開發，大部份的常見功能，藉由 Connect 本身支援或 Connect 的第三方(Third-party)模組，就可以實作出來。如果要實作檔案上傳的功能，可以使用第三方模組『&lt;a href=&quot;https://github.com/visionmedia/connect-form&quot;&gt;connect-form&lt;/a&gt;』來達成。&lt;br /&gt;
&lt;br /&gt;
直接透過 npm 安裝需要的模組：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;npm install connect-form&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
在 app.js 中實作：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;var express = require(&#39;express&#39;);
&lt;b&gt;var form = require(&#39;connect-form&#39;);&lt;/b&gt;
var app = module.exports = express.createServer();

app.configure(function(){
  app.set(&#39;views&#39;, __dirname + &#39;/views&#39;);
  app.set(&#39;view engine&#39;, &#39;jade&#39;);
  app.use(express.bodyParser());
  app.use(express.methodOverride());
  &lt;b&gt;app.use(form({&lt;/b&gt;
    &lt;b&gt;keepExtensions: true,&lt;/b&gt;
    &lt;b&gt;uploadDir: __dirname + &#39;/uploads&#39;&lt;/b&gt;
  &lt;b&gt;}));&lt;/b&gt;
  app.use(app.router);
  app.use(express.static(__dirname + &#39;/public&#39;));
});

app.get(&#39;/&#39;, function(req, res) {
  res.send(&#39;&amp;lt;form method=&quot;post&quot; enctype=&quot;multipart/form-data&quot;&amp;gt;&#39;
    + &#39;&amp;lt;p&amp;gt;Image: &amp;lt;input type=&quot;file&quot; name=&quot;image&quot; /&amp;gt;&amp;lt;/p&amp;gt;&#39;
    + &#39;&amp;lt;p&amp;gt;&amp;lt;input type=&quot;submit&quot; value=&quot;Upload&quot; /&amp;gt;&amp;lt;/p&amp;gt;&#39;
    + &#39;&amp;lt;/form&amp;gt;&#39;);
});

app.post(&#39;/&#39;, function(req, res, next) {
&lt;b&gt;  req.form.complete(function(err, fields, files){
    if (err) {
      next(err);
    } else {
      console.log(&#39;\nuploaded %s to %s&#39;
        ,  files.image.filename
        , files.image.path);
      res.redirect(&#39;back&#39;);
    }
  });

  req.form.on(&#39;progress&#39;, function(bytesReceived, bytesExpected){
    var percent = (bytesReceived / bytesExpected * 100) | 0;
    process.stdout.write(&#39;Uploading: %&#39; + percent + &#39;\r&#39;);
  });&lt;/b&gt;
});

app.listen(3000);&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
&lt;b&gt;後記&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
本文的範例與 express 所提供的是一樣的，只是在 app.configure() 裡多了幾行 app.use()。其實目的只是希望告訴讀者，務必要在 app.router 之前引入 connect-form。原因是在 Express Web Framework 設計上，每次處理 Request 時，會依照 app.use() 所引入的順序去使用模組。&lt;br /&gt;
&lt;br /&gt;
由於原生的 app.router 並不處理上傳檔案的特殊需求，我們一定要在 Request 經過 app.router 導引並進入 app.post() 所定義的 handler 之前，讓 connect-form 先行遇處理和檢查 Request 是否為上傳檔案，進而擴充功能甚至進一步分析處理。</description><link>https://fred-zone.blogspot.com/2011/11/nodejs-express.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-7211299374084540210</guid><pubDate>Wed, 23 Nov 2011 15:12:00 +0000</pubDate><atom:updated>2011-11-23T23:51:40.262+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Express Web Framwork</category><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">NodeJS</category><category domain="http://www.blogger.com/atom/ns#">Web 相關技術</category><category domain="http://www.blogger.com/atom/ns#">程式心得筆記</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><title>在 NodeJS + Express 使用 Cookie-based Session</title><description>開發網站的人應該對 Session 都不陌生，主要是用於 Client 與 Server 之間的溝通和狀態記錄。底層的實作細節本文就不多說明，若仍不理解他的用途，只要知道它可以讓你在不同頁面之間傳遞資料，像是登入狀態等。&lt;br /&gt;
&lt;br /&gt;
一般標準的『Express』利用『Connect Middleware Framework』（之後簡稱 Connect）中的模組來實作 Session 的，而預設的方法是使用 MemoryStore 的方式，也就是將 Session 資料存放於記憶體上。若你開發的是中大型的網站，也可以使用 Connect 的第三方模組『&lt;a href=&quot;https://github.com/visionmedia/connect-redis&quot;&gt;connect-redis&lt;/a&gt;』，利用『&lt;a href=&quot;http://redis.io/&quot;&gt;Redis&lt;/a&gt;』這樣的 Key-Value Database 來存放 Session 的資料。這些在 NodeJS + Express 的各類開發文獻中相當常見，你可以從許多網站上找到使用方法。&lt;br /&gt;
&lt;br /&gt;
不過，也許你仍不滿足，希望使用近年來興起的 Cookie-based Session，Connect 也有第三方模組『&lt;a href=&quot;https://github.com/caolan/cookie-sessions&quot;&gt;cookie-sessions&lt;/a&gt;』可以使用。&lt;br /&gt;
&lt;br /&gt;
可以直接使用 npm 安裝：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;npm install cookie-sessions&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
在你的 app.js 這樣使用：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;var express = require(&#39;express&#39;);
&lt;b&gt;var CookieStore = require(&#39;cookie-sessions&#39;);&lt;/b&gt;

var app = module.exports = express.createServer();

app.configure(function(){
 app.set(&#39;views&#39;, __dirname + &#39;/views&#39;);
 app.set(&#39;view engine&#39;, &#39;jade&#39;);
 app.use(express.bodyParser());
 app.use(express.methodOverride());
 app.use(express.cookieParser());
 &lt;b&gt;app.use(CookieStore({ secret: &#39;FredChien&#39; }));&lt;/b&gt;
 app.use(app.router);
 app.use(express.static(__dirname + &#39;/public&#39;));
});

app.get(&#39;/&#39;, function(req, res) {
 &lt;b&gt;req.session = { email: &#39;cfsghost@gmail.com&#39; };&lt;/b&gt;

 /* Redirect to another page */
 res.redirect(&#39;/getsession&#39;);
});

app.get(&#39;/getsession&#39;, function(req, res) {
 &lt;b&gt;if (req.session)&lt;/b&gt;
  &lt;b&gt;res.end(&#39;This is Fred&#39;s email address:&#39; + req.session.email);&lt;/b&gt;
});

app.listen(3000);&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
&lt;b&gt;後記&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
其實 NodeJS 還算發展階段，相關資源不算很多，若想要實作過去常用的一些功能，會需要花點功夫請教 Google 大神。</description><link>https://fred-zone.blogspot.com/2011/11/nodejs-express-cookie-based-session.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-3667799374069067707</guid><pubDate>Fri, 18 Nov 2011 20:19:00 +0000</pubDate><atom:updated>2011-11-19T05:16:13.149+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Android</category><category domain="http://www.blogger.com/atom/ns#">Dalvik VM</category><category domain="http://www.blogger.com/atom/ns#">Java</category><category domain="http://www.blogger.com/atom/ns#">程式心得筆記</category><category domain="http://www.blogger.com/atom/ns#">系統研發手札</category><title>千萬別相信 Android 上的應用程式</title><description>或許有些過於危言聳聽，但我們確實得正視這樣的議題：『資料無故從手機中消失』。這是可能發生的問題，原因在於 Android Dalvik VM 的 Garbage Collection 機制太過強大，如果程式開發者在寫程式的過程中都沒注意到這問題，那這肯定是個不定時炸彈。&lt;br /&gt;
&lt;br /&gt;
話說，寫過 Android Application 的人應該都很習慣了變數宣告後，用完了就不管他，彷彿系統會有無限量供應的記憶體一般。不過，這對『慣C』一族來說，永遠是種荒繆的事，實在無法想像『憑什麼』不用釋放記憶體。其實說穿了，這一切是 VM 裡面的 Garbage Collection 在『Hold 住』場面，他會自動將目前 Reference 數量稀少的記憶體回收，以避免系統記憶體被用光後又不釋放。而所有回收的動作，有一系列『聰明』的演算法，有興趣的人可以自己去找相關資訊閱讀。&lt;br /&gt;
&lt;br /&gt;
但是別以為 Garbage Collection 可以聰明到百分之百準確知道，什麼東西回收後是對系統或是應用程式無傷的，因為這就算要人腦來判斷，也一定會犯錯。更者，也別以為 Garbage Collection 不太會動當前正在跑的程式，因為只要符合條件，就算是你程式當前不斷在用的變數，也可能消失被回收，導致程式錯亂，就像被鬼憑空抓走一般。&lt;br /&gt;
&lt;br /&gt;
如果你認為這只是理論，那你就錯了，這問題實際發生在最近筆者幫忙救火的案子裡。詳細情況是，這是瀏覽器相關的案子，當時我們宣告了幾個 Private 變數，然後使該變數在使用者操作觸發後，會被設定了一些數值供接下來的許多運算使用。一般情況下，我們實作的程式都很正常，不過，一旦開到比較複雜或充滿 JavaScript 的網站後，我們的程式彷彿就開始像中邪，完全不照我們的想像去執行，無論怎麼找問題，也找不出個所以然。&lt;br /&gt;
&lt;br /&gt;
由於問題的發生是過程中變數無故被歸零，而我們又確定沒有任何一段自己的程式會讓他歸零，所以只剩下一種可能：『Garbage Collection 為了因應 WebView 過多的需求，而把變數給回收了。』&lt;br /&gt;
&lt;br /&gt;
最後，我們將這變數設為 static 以解決這問題。不過，雖然用 static 暫時解決了問題，那也只是減少變數不被回收的機會，並不是最妥當的做法。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;後記&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
別小看這問題，其實在 Android Framework 中也處處藏滿類似的問題，只是不知道什麼時候會爆出來而已。而這次救火的案子，就倒霉碰到鬼。&lt;br /&gt;
&lt;br /&gt;
或許多半處於最上層的應用程式開發者，都覺得這些底下的事情，應該極穩定又可信賴，從來就不會在意這些東西，但是這可能會造成重大問題，相關知識還是要多充實。</description><link>https://fred-zone.blogspot.com/2011/11/android.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>3</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-7541526801068949323</guid><pubDate>Sun, 13 Nov 2011 09:51:00 +0000</pubDate><atom:updated>2011-11-13T21:52:34.622+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">Socket.IO</category><category domain="http://www.blogger.com/atom/ns#">Web 相關技術</category><category domain="http://www.blogger.com/atom/ns#">WebSocket</category><category domain="http://www.blogger.com/atom/ns#">程式心得筆記</category><category domain="http://www.blogger.com/atom/ns#">自由軟體</category><title>自認帥氣的 WebSocket 簡單命令處理模型</title><description>最近參與的一個 Project，需要在 WebSocket 下實作許多機制，這著實讓人傷透腦筋。又由於 JavaScript 完全是非同步（asynchronous）的設計概念，若遇到必需等待 Server 回應後才能繼續執行的工作，處理起來可是一件異常麻煩的事。雖然很熟悉運用回調函數（Callback Function）和暱名函數（Anonymous Function)來做，但一層包一層的設計，總讓人覺得進入無限階層的夢境地獄，什麼時候醒來都不知道。&lt;br /&gt;
&lt;br /&gt;
這是筆者要達成的需求：『發送自己定義的命令去 Server，然後等待 Server 回應，一旦收到 Server 回應就可以繼續往下處理。』&lt;br /&gt;
&lt;br /&gt;
一般的情況下，多數人都會這樣做（這理使用 Socket.IO 來建立 WebSocket，並和 Server 要當前時間為例）：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;/* Define handler to receive response from server */
socket.on(&#39;Clock&#39;, function(data) {
    /* Do something */
    alert(data);
});

/* Send command to server */
socket.emit(&#39;Clock&#39;, &#39;Time&#39;);&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
可是，並非每次對 Server 的請求，都希望透過 alert() 跳出結果，我們有時只是想要利用 Server 給的時間做一些其他處理，雖然是下同樣的命令，但後續處理完全不一樣。於是，筆者，設計了這樣的方式，簡單的去處理這種問題：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;var command = {
    &#39;Time&#39;: [],
    &#39;Date&#39;: []
    /* You can define more commands */
};
socket.on(&#39;Clock&#39;, function(data) {
    if (command[data.command].length) {
        for (var index in command[data.command])
            command[data.command][index](data.result);

        command[data.command] = [];
    }
});

/* API to Send command to server */
function sendCommand(domain, cmd, callback) {
    command[cmd].push(callback);
    if (command[data.command].length == 1) {
        socket.emit(domain, cmd);
    }
}

/* New method to send command and show result in alert window */
sendCommand(&#39;Clock&#39;, &#39;Time&#39;, function(data) {
    alert(data);
});

/* New method to send command and set window title with result */
sendCommand(&#39;Clock&#39;, &#39;Time&#39;, function(data) {
    window.title = data;
});&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
這邊需要在 Server-side 改變回送的格式，告知 Client 現在回應的結果是屬於哪一個命令：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;data = {
    command: &#39;Time&#39;,
    result: &#39;11:11:11&#39;
};&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
這樣的命令處理模型，適合用在讀取即時性低的公用狀態或常數，因為這種做法只會送出一個命令給 Server，程式在發送命令時就會自動合併當下發出的『同樣要求』，直到 Server 回應後，同時間一併觸發所有相應的 callback function。除了能減少傳送的資料量，也避免與 Server 的溝通等待，提升一些效能。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;後記&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
若是不希望合並要求，且想要明確等到自己發送的命令回應後，才觸發自己的 callback function，廣泛的做法是建立計數器，但這種方法就不在本文討論的泛疇。</description><link>https://fred-zone.blogspot.com/2011/11/websocket.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-7316025852799509810</guid><pubDate>Wed, 09 Nov 2011 03:15:00 +0000</pubDate><atom:updated>2011-11-09T11:17:10.463+08:00</atom:updated><title>使用 Jade 快速製作 HTML Template</title><description>如果你用 NodeJS 和『&lt;a href=&quot;http://expressjs.com/&quot;&gt;Express Web Framework&lt;/a&gt;』來寫網站，又參考官方給的範例程式，『&lt;a href=&quot;http://jade-lang.com/&quot;&gt;Jade&lt;/a&gt;』應該是你首選的 Template Engine。不過 Jade 相當容易，如果你已經很熟悉 HTML，學習它不會是件難事。&lt;br /&gt;
&lt;br /&gt;
Jade 用來表示每一個 HTML Tag 的格式：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;&lt;i&gt;&amp;lt;tag&amp;gt;&lt;/i&gt;(&lt;i&gt;&amp;lt;attributes&amp;gt;&lt;/i&gt;) &lt;i&gt;&amp;lt;inner content&amp;gt;&lt;/i&gt;

&lt;i&gt;或是&lt;/i&gt;

&lt;i&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-style: normal;&quot;&gt;&lt;i&gt;&amp;lt;tag&amp;gt;&lt;/i&gt;(&lt;i&gt;&amp;lt;attributes&amp;gt;&lt;/i&gt;)=&amp;nbsp;&lt;i&gt;&amp;lt;inner content&amp;gt;&lt;/i&gt;&lt;/span&gt;&lt;/i&gt;

ex: img(src=&#39;/images/logo.jpg&#39;)
ex:&amp;nbsp;a(href=&#39;http://www.mandice.com/&#39;) Mandice
ex: title=&amp;nbsp;This is Jade Example&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
使用縮排描述巢狀結構（ 如同 Python）：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;Jade 語法：
html
&amp;nbsp;&amp;nbsp;head
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;title= This is Jade Example
&amp;nbsp;&amp;nbsp;body
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;Hello World

結果：
&amp;lt;html&amp;gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;head&amp;gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;title&amp;gt;This is Jade Example&amp;lt;/title&amp;gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;/head&amp;gt;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;lt;body&amp;gt;Hello World&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
賦予 Element 一個 ID 有兩種方式：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;div(id=&#39;element1&#39;)

或是

div#element1&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
引入並使用另一個 Jade 檔案：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;div#element1= partial(&#39;another.jade&#39;)&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
賦予 Tag 有多個&amp;nbsp;Attributes 有兩種方法：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;使用『,』連接多個 Attribute：
img(src=&#39;/images/logo.jpg&#39;, title=&#39;This is a Logo&#39;)

使用換行連接多個：
img(
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;src=&#39;/images/logo.jpg&#39;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;title=&#39;This is a Logo&#39;
)&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
建立 div 元素並指定 ID 的特殊例子，可在開頭省略 div：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;#element1(class=&#39;test&#39;)&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
&lt;b&gt;後記&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
在配合使用 Jade 時，當然還會碰到傳入值的情況，以及簡單的判斷式，不過這部份就留給實地操作的人自己去發掘了。 Have fun!</description><link>https://fred-zone.blogspot.com/2011/11/jade-html-template.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-4647124624940606337</guid><pubDate>Tue, 08 Nov 2011 00:27:00 +0000</pubDate><atom:updated>2011-11-08T11:56:08.886+08:00</atom:updated><title>我如何看待 NodeJS</title><description>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;a href=&quot;http://nodejs.org/logos/monitor.png&quot; imageanchor=&quot;1&quot; style=&quot;clear: right; float: right; margin-bottom: 1em; margin-left: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;242&quot; src=&quot;http://nodejs.org/logos/monitor.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;
其實，這是篇推薦文，但推薦的角度和坊間很多人不一樣，所以這也是為什麼標題下『我如何看待 NodeJS』。&lt;br /&gt;
&lt;br /&gt;
誠如大家所知，『&lt;a href=&quot;http://nodejs.org/&quot;&gt;NodeJS&lt;/a&gt;』原先被開發出來的目的，是為了解決 Web Server 效能的問題，其採用 Google Chrome/Chromium 使用的『&lt;a href=&quot;http://code.google.com/p/v8/&quot;&gt;Google V8 Javascript Engine&lt;/a&gt;』做為直譯器引擎。最特別的是，NodeJS 讓開發者完全可以用 JavaScript 來開發整個伺服器端的所有程式，這意味你可以不用再使用 Python、PHP、Perl、Ruby、ASP 等其他的語言，只需要學會 JavaScript 就可以將網站通通搞定。也因為幾乎所有的 Web 開發者都熟悉 JavaScript，NodeJS 很理所當然的成為一個 Web 圈內受很多人擁護的新技術，在各大 Cloud Hosting 也都開始有支援。&lt;br /&gt;
&lt;br /&gt;
當然不只是如此， NodeJS 也吸收了各方面的經驗，提供應用程式框架（Framework）讓開發者可以快速開發。平心而論，比其他的語言更能兼固快速開發和效能的問題。&lt;br /&gt;
&lt;br /&gt;
談到這裡，還是僅止於坊間多數人介紹的內容，筆者認為，如果只是這樣，也未免小覻了 NodeJS。&lt;br /&gt;
&lt;br /&gt;
事實上，NodeJS 就是 Original JavaScript Without Browser + Libraries，在舊有的 JavaScript 抽離瀏覽器後，再上加上更多 System-level Library 的支援。這讓我們可以重新看待 JavaScript 這個語言，因為它不再只是限於『特定領域』使用，而是提升至『System Level』。&lt;br /&gt;
&lt;br /&gt;
我們可以這樣認定：『JavaScript 已經可以和 Python 相提並論』。因此，使用 JavaScript 寫一支系統應用程式或是桌面應用程式，已經不再是夢，而是伸手可及。如果你有印像，HP WebOS 就是率先引入 NodeJS 的先行者，讓原本被視為 Web 技術的 JavaScript，跳出舊有框框，進入桌面系統的領域。&lt;br /&gt;
&lt;br /&gt;
目前，NodeJS 仍持續發展中，許多函式庫支援也被慢慢的被實作出來。有時為了效能考量，我們也可以自己使用 C/C++ 為他開發更多 Library/Module。可以期待，未來 NodeJS 將會更為強大。</description><link>https://fred-zone.blogspot.com/2011/11/nodejs.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-6982429377467999730</guid><pubDate>Fri, 04 Nov 2011 22:48:00 +0000</pubDate><atom:updated>2012-01-11T13:20:46.080+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Express Web Framwork</category><category domain="http://www.blogger.com/atom/ns#">JavaScript</category><category domain="http://www.blogger.com/atom/ns#">NodeJS</category><category domain="http://www.blogger.com/atom/ns#">Socket.IO</category><category domain="http://www.blogger.com/atom/ns#">WebSocket</category><category domain="http://www.blogger.com/atom/ns#">自由軟體</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><title>使用 NodeJS + Express + Socket.IO 實作 WebSocket 服務</title><description>一直處於假標準狀態的 HTML5，過程中一直有變化，讓人無法放心使用。不過到了今年，HTML5 總算進入了準決賽的階段，大部份功能已經確定下來，其中 WebSocket 就是最讓人關切的項目之一，因為這意味著未來 Web 不再只是單次性的觸發服務，而是可以讓使用者端與伺服器長時間處於連線狀態，並進行即時性的資料傳交換。&lt;br /&gt;
&lt;br /&gt;
關於 WebSocket，彷間許多隨 HTML5 起舞的人，其實都說明不夠清楚，多半只強調能達成 TCP Socket 形式的連線，和無所不能的優點。其實，WebSocket 並非是我們所知的原生 Socket ，而只是一種架構於 HTTP Protocol 上的延伸定義，所以換句話來說，不能直接拿來連線到非 HTTP 以外的通訊協定。&lt;br /&gt;
&lt;br /&gt;
至於它的做法，就是在瀏覽器向伺服器要資料時，在 Request Header 裡加上『Connection:Upgrade』來告訴伺服器：『我要改變連線形態』。然後伺服器會依照瀏覽器給的 WebSocket 相關標頭（header）和交握用的金鑰（Key），進行認證和改變處理方式。進入 WebSocket 連線的客戶端，就有如在使用 TCP Socket 或 Telnet 一樣，可以隨意在任意時間點傳送資料給伺服器處理，伺服器也可以在這連線下即時回應。所以就某方面來說，這剛好可以含蓋並取代過去 Web 1.0 的 Refresh Page 和 Web 2.0 後的 Long-polling 技術。&lt;br /&gt;
&lt;br /&gt;
寫過網路程式的人都知道，如何定義 Protocol 才是傷腦筋的事，開啟了一個『類 Socket』意味著我們又回到了過去的年代，要自己解決 Protocol 的定義。不過也已經有現成的 Library 可以使用，許多問題我們已經不用擔心，本文將提到的 Socket.IO 就是其中之一。此外，由於 WebSocket 需要 Client/Server 同時實作，所以，這些 Library 多半都包括了兩部份的實作。&lt;br /&gt;
&lt;br /&gt;
Server 端，使用 NodeJS + Express web framework + Socket.IO(0.8.6)：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;/* Module dependencies */
var express = require(&#39;express&#39;);
var io = require(&#39;socket.io&#39;);

/* Initializing Express Framework */
var app = module.exports = express.createServer();
app.listen(3000);

/* Create a Socket.IO instance, to establish WebSocket Service */
var socket = io.listen(app);
socket
    .on(&#39;connection&#39;, function(client) {
        console.log(&#39;A connection was established&#39;);
        client
            .on(&#39;pretty-girl&#39;, function(data) {
                console.log(data);
                socket.emit(&#39;ugly-man&#39;, &#39;get out of my way&#39;);
            });

        client.on(&#39;disconnect&#39;, function() {
            console.log(&#39;Server has disconnected&#39;);
        });
    });&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
Client 端，使用 Socket.IO(0.8.6)：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;socket = io.connect();
socket.on(&#39;connect&#39;, function() {
    console.log(&#39;Client has connected to the server!&#39;);
})
socket.on(&#39;disconnect&#39;, function() {
    console.log(&#39;The client has disconnected!&#39;);
});
socket.on(&#39;ugly-man&#39;, function(data) {
    console.log(data);
});
socket.emit(&#39;pretty-girl&#39;, &#39;hello!&#39;);&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
&lt;b&gt;後記&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
回到原始的網路溝通，也有許多議題將再次面臨，舉例來說，客戶端(Client)可以同時發送很多道命令給伺服器，但伺服器可能每道命令完成時間有快有慢，以至有些命令是後發卻先完成。那伺服器如何告知客戶端，是哪一道命令完成了？而這種問題非常容易在多功能的服務中見到，尤其是遊戲伺服器。不過，對於過去有過伺服器設計經驗的人，顯然是不用怕。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;備註：使用 Socket.IO 要注意版本的變化，因為各版 API 改動伏度都不小。而且也要注意 Client/Server 要使用同一版本，之前筆者犯錯，在 Client 使用官方提供的 CDN 版本，但在 Server 端使用自己下載的最新版本，結果一直無法讓 WebSocket 成功被建立。&lt;/b&gt;</description><link>https://fred-zone.blogspot.com/2011/11/nodejs-express-socketio-websocket.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-3501094879648126670</guid><pubDate>Thu, 27 Oct 2011 04:11:00 +0000</pubDate><atom:updated>2011-10-27T12:11:32.330+08:00</atom:updated><title>偏好的 JavaScript Class 實作『基本款』</title><description>&lt;p&gt;最近有一些想法，便和伙伴在空閒時間寫一些 Prototype 的專案，既然是嘗試形態的專案開發，就不必考慮穩定性和熟悉度，也不用顧慮失敗的問題，可以盡情玩弄新的，或是過去不常使用的技術。所以，這次使用了 nodejs + express + HTML5 來開發，大玩 JavaScript。&lt;/p&gt;

&lt;p&gt;JavaScript 雖然看起來像 Java，但畢竟它不是真的 Java，很多功能考量以易於使用為優先，所以並不嚴謹，一些功能也因此被拿掉。畢竟，當初被設計出來只是為了讓『網頁動起來』，並沒有考量太多。但是隨著需求大增，而現在我們所看到的 JavaScript ，已經是一層層為了需求而疊出來的產物，其不旦保留與舊的 JavaScript 相容，又要擴充物件導向的現代化語言特性，這也是為什麼使用起來總是沒有一個固定的模式。&lt;/p&gt;

&lt;p&gt;筆者過去總苦惱 JavaScript 的寫法變化太大，在過程中跌跌撞撞，雖寫了不少 Code，但總是覺得不順手，非常想尋求一個 JavaScript 設計模式的『基本款』，也希望和開發伙伴們有共同遵循的標準，讓專案更容易維護。&lt;/p&gt;

經過和隊友們的一番討論，總算有一些結論：
&lt;div&gt;&lt;pre&gt;/* Example Class */
var Example = function() {
    /* Private */
    var Status = 0;
    var doms = {
        object: false;
    };
    var subObject = false;

    /* Constructor */
    subObject = new OtherClass();
    doms.object = document.createElement(&#39;div&#39;);

    /* Public */
    return {
        setStatus: function(_status) {
            Status = _status;
        },
        getStatus: function() {
            return Status;
        },
        getDOMs: function() {
            return doms;
        },
        getOtherClass: function() {
            return subObject;
        },
        Run: function() {
            /* Call own methods */
            this.setStatus(2);
            this.getStatus();
        }
    };
};&lt;/pre&gt;&lt;/div&gt;

然後我們可以用這樣的方式使用定義出來的物件：
&lt;div&gt;&lt;pre&gt;/* Create a new Example Object */
var ex = new Example();

/* Set to private variable */
ex.setStatus(1);

/* Get something from private area */
ex.getDOMs().object;

/* Using subObject&#39;s Methods */
ex.getOtherClass().publicMethods()&lt;/pre&gt;&lt;/div&gt;

其實有寫 OO 的人，不管換到哪一種語言，第一時間都會尋找如何實作私有（Private）、公用（Public）和 Class 的寫法，不過，和其他語言不一樣的是， JavaScript 卻有很多種方式可以達成。當然，這邊只是列出我們比較偏好的寫法，供讀者們參考。</description><link>https://fred-zone.blogspot.com/2011/10/javascript-class.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-3252114719286644302</guid><pubDate>Mon, 17 Oct 2011 01:15:00 +0000</pubDate><atom:updated>2011-10-17T09:27:02.210+08:00</atom:updated><title>程式開發者之不要過度依賴別人解決問題</title><description>常有人說，資訊產業變化太快，如果不及時跟上，馬上就落伍了。事實上不全然如此，應該說在這產業是不進則退，至於要不要跟著潮流走，不一定。其實講明了，電腦科學並不是真正的科學，而是種社會學，由於大家的方向和需求多半是一致的，可以很容易預測到未來需要用到的各種技術，或是常有別人已經完成的工具或技術可直接使用，而且研發新技術也不需要像真正科學一樣，我們不需要長篇幅的理論基礎，只要能解決當前問題即可，如何不斷的解決需求和問題才是我們所在意。&lt;br /&gt;
&lt;div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
在之前舊文『程式開發者之萬事起頭難』有提過，開發軟體相當依賴經驗。所以，不管是如何找到解決問題的辦法，都必需要經過實作執行然後吸收，並轉化成程式人員的經驗才有意義。也因此，只要能獲得新的經驗，拓展視野，什麼方法都可以。不過，筆者認為『追著最新的外來技術』和『自己發展技術』兩種途徑最為重要，而且缺一不可。如果用武俠小說的角度來說，就是招式和心法的配合，增加內力的累積。&lt;/div&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
自己發展技術就不用多說，我們能從實地的演練，獲得大量經驗和想法，而且會得到實務考量的觀念。比較需要注意的是『追著最新的外來技術』，因為很多時候，我們面對這些技術的態度，會讓自己無法分辨自己所得到的是『招式』還是『武功』。&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
或許很多人以『學了很多招式』而自豪，但面對真正的需求和挑戰時，便無法應對自如，招式套不上去，此時才抱怨『書到用時方恨少』，殊不知是學習新知的態度有問題所造成。因為缺少了質的招式，並不具有太大的意義，而且永遠要追求更新更有用的招式，才不會有招用老的一天。可是，一旦碰到 Google 大神或書裡沒有答案，就完蛋了。記得，學會很多招式或工具，只是『幫助提升工作效率』，相當於大學生會剪剪貼貼罷了，缺少深入的理論和資訊定位分析，或沒有學習問題的根本，並無法累積解決問題能力。&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
所以時時都要準備哪一天『如果沒有 Google』，也不要再只是窮追新的 Library 或 open source project。導致一旦沒有其他人供獻這些東西，自己便沒能力完成專案。雖然說要全部都懂是很困難或不可能達成，但要盡量做到，才能得到屬於自己的關鍵技術。以前人說的：『站在巨人肩上』，指的就是追到最新技術後，再發展技術，如果只是使用而沒有再發展，就等於沒有進步可言，遲早會被騎在更高巨人身上的人所打敗。&lt;/div&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
程式人員要盡可能快速從『請 Google 幫忙』職業學校畢業，才能當一個稱職的開發者。&lt;/div&gt;</description><link>https://fred-zone.blogspot.com/2011/10/blog-post_17.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-525079490372185518</guid><pubDate>Sat, 15 Oct 2011 14:34:00 +0000</pubDate><atom:updated>2011-10-15T22:45:21.275+08:00</atom:updated><title>程式開發者之傻傻搞不清楚技術定位</title><description>常聽到有人說專案選錯技術而失敗，或是造成什麼嚴重問題，其實這其中，是有很多原因導致選錯技術。不過，對於初入這行業的人來說，所知所見不夠廣，是最主要的問題。&lt;br /&gt;
&lt;br /&gt;
使用正確的解決方案能讓軟體專案或是產品專案更加順利。所以開發前要做足功課，通盤的瞭解和業界各種大小的動態都不能放過（相信身為程式人員，看英文資訊的能力應該不必擔心），哪怕該技術存在的領域可能與我們當前熟悉的領域相差十萬八千里遠。因為如果視野太狹窄，就看不到更好的技術方案，更沒有機會觸類旁通。有了足夠資訊，如同有了一張地圖，也就有跡可尋自己不足之處和其解決之道。&lt;br /&gt;
&lt;br /&gt;
有了地圖，便要開始認清方位，身為程式開發者，懂一項技術的優點和極限是絕對必要的，所以一定要了解一件事實：『懂一種技術能做些什麼，並不代表了解該技術只能做些什麼。』瞭解透徹，才能正確的選擇解決方案和確實將目標達成。&lt;br /&gt;
&lt;br /&gt;
搞清楚各種技術的定位後，便要應用在一個專案開始前，使用所知去選擇解決方案。記得過程中要隨時保持所選擇的技術，在緊急時刻可替換的彈性。專案開始後，切勿將陌生領域的東西寫得太過死硬，要可以容易被切割獨立。因為若是在開發過程中，才發現原先使用的技術可能並不適合在目前的用途上時，可以用另外的技術來彌補或替換。除非，有時間上的壓力或限制，硬是使用不適當的技術完成任務是容易有後遺症的，而且容易傷害到使用者體驗。更重要的是有很大機會達不成目標的標準。&lt;br /&gt;
&lt;br /&gt;
此外選擇技術的評估，除了應該先有通盤知識為基礎，應該以客觀或是跨時代的角度來做，而不是以自己手上的電腦硬體，或是自己目前正習慣使用的平台或喜好的技術為準。一旦專案有挫折或失敗，才來說當初選錯平台，實在是浪費時間。而且，還可能錯上加錯，因為不是選錯平台而是選錯技術的可能性更大。&lt;br /&gt;
&lt;br /&gt;
要知道，這個時代是多元的，只熟悉或知曉單一方向技術，是無法有太多創意和實作能力的。解決問題的方法並不需要貫徹技術到底的決心，只要選擇適當即可。</description><link>https://fred-zone.blogspot.com/2011/10/blog-post_15.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>4</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-915439468639776331</guid><pubDate>Mon, 10 Oct 2011 08:13:00 +0000</pubDate><atom:updated>2011-10-10T18:38:38.902+08:00</atom:updated><title>程式開發者之萬事起頭難</title><description>&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-family: Verdana;&quot;&gt;&lt;/span&gt;&lt;br /&gt;
接觸程式開發也已經十五年有餘，雖然不算多，但也算經歷過不少風雨。於是趕在二十五歲結束前，開始記錄起『程式開發打字工』的種種心得。之後，如同數學家的二十五歲大限，希望『程式開發打字工』將成為興趣與次要的工作，不敢說可以完全放下，但必要動手做時也會不假別人之手。&lt;br /&gt;
&lt;br /&gt;
首先談到開發一個新的專案，俗話說萬事起頭難，我們馬上會碰到千頭萬緒襲來。而且我們什麼都想做，什麼都想完美，往往還沒開始就已經結束了。此時心神不定，煩燥之時若螢幕上閃過X浪或 Facebxxk 的身影，再來個香蕉動新聞，一整天馬上就過去。&lt;br /&gt;
&lt;div&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div&gt;
寫程式很重要的的一個原則就是：『不要想一次都要做太多功能。』尤其是在初次接觸的領域。因為，光是計劃架構和細節就會先消磨掉大半熱情。甚至開始實作以後，你會因為到處要留後著而難以前進，把自己陷於無法動彈之地。一但沒有動力，也沒有前進的路，該專案將胎死腹中。&lt;br /&gt;
&lt;br /&gt;
如果有足夠的時間，專案初期不斷打掉重練，是可以接受的。因為這比起日後累積太多量後，才發現不適合再打掉重練而來得好。也不會因為在錯的基礎上開發，而降低開發效率，甚至寫出問題重重的軟體。&lt;br /&gt;
&lt;br /&gt;
至於『如何才叫太多功能』，這依每個人而定，經驗多寡決定了一個人能快速設計多大規模的架構，並且能在該架構下工作而不會自亂陣腳。同樣類型的程式，如果你有過去的實作經驗，這次肯定有思緒改進或是延用。反之，如果沒有過去的經驗，很難做太長遠的細節規劃，你能靠的只有『過去學會的程式技巧』和『類似經驗』，盡力設計一個勘用的架構罷了，然後經過提早發現問題提早重新來過，盡可能補完整個專案所需。&lt;br /&gt;
&lt;br /&gt;
當然，除了程式技巧和相關知識，經驗是不通用的，如果碰到不熟悉的專案，還是得從頭累積經驗，從小規模的設計一步步前進。記得不要貪多，一個功能一個功能確實完成，『慢慢來，比較快』。&lt;/div&gt;
</description><link>https://fred-zone.blogspot.com/2011/10/blog-post.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>1</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-4824725177768593150</guid><pubDate>Wed, 05 Oct 2011 20:05:00 +0000</pubDate><atom:updated>2011-10-06T05:46:33.669+08:00</atom:updated><title>Apple 大軍壓境，電腦王國準備好了嗎？</title><description>如果沒意外，Apple 將以低價重回戰場，掃蕩剩下的反抗勢力，用升級版的硬體，鞏固現有的防線。那麼，過去的電腦王國，準備好應戰了嗎？顯而易見：『還沒。』&lt;br /&gt;
&lt;br /&gt;
我們可以期待，接下來手機裝置的局面很可能是三分天下：『Apple、Nokia、Moto』，他們分別背後擁有強悍的軟體公司或團隊支援。雖然 Google 目前沒有說要開始親自投入戰場，但是品牌和代工合體後的 GMoto(Google + Moto Mobility)，難保有一天不會自己殺出條血路。就算是台灣當前的對手 - 『韓國』， Samsung 也擁有著自己的軟體系統。那麼，電腦王國怎麼辦？身為電腦王國的國民，不免擔憂起這樣的局面。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;舊時代王者的現實&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
郭董已經證明，鐵血毛利之下除了逼人跳樓之外，缺少政府『幫助』後，現在才正要開始體現代工產業下應有的利潤和風險。在這薄利又不景氣的時代，比誰和銀行借的錢多，已經不管用了。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;缺少商業模式的資訊產業&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
如果有夠大的商業模式或願景為前提，就算是當年 PC 時代的『大補帖』歪風，都能有掀起錢潮的機會。任何點子和創意，都足以大舉刺激商業發展。&lt;br /&gt;
&lt;br /&gt;
今天，拼命的生蛋和不停的脫皮，算斤兩的生意，卻已經變成主流。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;硬體廠商心中的不平衡&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
還記得當年 PC 獨霸的年代，微軟一舉跳上枝頭，收盡一切好處。硬體廠商雖然拼命做，扛下所有生產風險，但每台電腦所獲得的利潤逐年降低，直至今天這『毛利』的勢態後咬牙苦撐。反觀微軟靠著數不清的五毛光碟片，賺得荷包滿滿。現在殘存的硬體廠商，誰不恨得牙癢癢？所以只要有機會解決微軟，讓原本應該進微軟進自己口袋，何樂而不為？&lt;br /&gt;
&lt;br /&gt;
此外，Apple 以一家非硬體製造商的身份，用 iPhone/iPad 打下一片江山，讓所有人失了面子也失了市場。沒有一家廠商不這麼想：『既然 Apple 可以從一家軟體（或稱為系統整合）的公司，跨足到完全不相干的手機產業，那麼，身為硬體製造商的我們，為什麼不能跨界成為 Apple 呢？』&lt;br /&gt;
&lt;br /&gt;
於是，成為『類 Apple 公司』變成這些傳統 PC 製造商的夢。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Android 滿足了硬體廠商心中的不平衡&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
終於有機會可以自己掌控作業系統，不用再給微軟或任何系統軟體業者半毛錢。各家廠商無一不開始宣布自家有完整的系統技術團隊，可以針對作業系統做任何改進和維護。對硬體商來說，有了一個可以佔有的作業系統後，這不就代表：『我也是 Apple 了嗎？』&lt;br /&gt;
&lt;br /&gt;
於是，喊出各種軟硬、垂直整合的『口號』。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Open Source 讓所有人忘了自己如何起家而 Apple 以前怎麼失敗&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
還記得當年，Apple 推出了個人電腦，就如同今天的 iPhone/iPad 一般的封閉。爾後，被 IBM 所領導的 PC 大軍所打敗，台灣因而開始有電腦王國的稱號。今天的各家硬體製造商，都是當年聯軍中的一份子，靠著開放的標準和朝共同目標努力而起家。&lt;br /&gt;
&lt;br /&gt;
二十多年後的今天，這些聯軍將領，確因眼紅於 Apple 的名利雙收，無一不走起與 Apple 相同的路。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;為了創新而創新&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
看到 Apple 投下的原子彈，任誰都會想：『有武力才能當王者。』因此，所有廠商在軍備競賽之下，只看到眼前的船堅炮利，已經看不到『內求』的重要。徒打造出外表亮麗的艦隊，卻沒有經營的觀念，也沒有足以支撐的知識基礎。&lt;br /&gt;
&lt;br /&gt;
當別人正在突破自己，我們的創新正在煩惱『如何快速現代化』。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;已經沒有進步可言的產品&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
機海策略，可以針對各種客群做攻略。但這樣龐大的研發、製造成本和時效風險，造成商品可以有嚴重問題的『遣規則』。&lt;br /&gt;
&lt;br /&gt;
如果想要問題獲得改善，『請買下一代』已經變成廠商業務很容易說出口的『官方建議』，更可怕的是，就算買了下一代產品，問題可能依舊。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;把軟體當做隨用即拋的零件&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
硬體的零件，可以壞了直接換新，這批貨用不完下批繼續用。可是軟體卻是壞了不一定有能修，這次用完，下個產品可能放不進去。&lt;br /&gt;
&lt;br /&gt;
過去的台灣的生存環境太注重個人戰力而缺少團隊協同開發，所以我們常常可以看到個人色彩濃厚的程式碼，然後時效性的因素加碼後，往往會產出他人難以維護，甚至是無法再利用的軟體。&lt;br /&gt;
&lt;br /&gt;
當沒有實體成本的軟體，變得無法維護和改進時，反而變成日後嚴重的人力成本浪費。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;活在灰色地帶的廠商&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
PC 時代，系統有問題我們可以罵微軟。手機時代，如果用 Android，我們罵 Google 也沒用，因為我們不是 Google 的客戶。罵製造商也沒用，因為很可能是 Android 本身的問題。&lt;br /&gt;
&lt;br /&gt;
不過，有一種狀況更棘手：『系統被廠商修改後產生的問題，但第一線服務人員為守住戰線，直接推給 Google。』&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;立刻決定！不當對不起自己和後代的廠商！&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
身為後輩，不期望能給予當年聯軍的名將們當頭棒喝，也不認為老大哥們不懂當前困境。只是希望，前輩能留下一條讓後代好走的路，指明一條我們應該走的路。</description><link>https://fred-zone.blogspot.com/2011/10/apple.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-2935396036845932310</guid><pubDate>Sat, 01 Oct 2011 02:57:00 +0000</pubDate><atom:updated>2011-10-01T10:57:51.509+08:00</atom:updated><title>改善 QML 圖型渲染效能</title><description>雖然 Nokia 擁抱了 Microsoft，但不可否認，其旗下的 Qt Framework 經過十載演進，也有 Open Source 出來，實在是個好東西。因此，筆者認為 Nokia 的決定並不會影響我們繼續使用 Qt 的意願，長期在 Linux 下並以 Qt 為基礎的 KDE 桌面環境，就足夠證明了&amp;nbsp;Qt 尚未死。&lt;br /&gt;
&lt;br /&gt;
而最近筆者使用 QML 實做了一些應用程式，發現圖型渲染效能並不是非常好。猜想是和 Android 有相同的問題，於是著手將程式改用 OpenGL 來繪圖，果然，效能立即獲得大幅度改善：
&lt;br /&gt;
&lt;div&gt;
&lt;pre&gt;int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    QGLWidget *glWidget;
    QGLFormat format = QGLFormat::defaultFormat();

    /* Create OpenGL Widget to render QML */
    format.setSampleBuffers(false);
    glWidget = new QGLWidget(format);
    glWidget-&amp;gt;setAutoFillBackground(false);

    /* Create QDeclarativeView to open QML file */
    QDeclarativeView *viewer = new QDeclarativeView;
    viewer-&amp;gt;setViewport(glWidget);
    viewer-&amp;gt;setSource(QUrl::fromLocalFile(&quot;main.qml&quot;));
    viewer-&amp;gt;show();

    return app.exec();
}&lt;/pre&gt;
&lt;/div&gt;
</description><link>https://fred-zone.blogspot.com/2011/10/qml.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-6713183329875107559</guid><pubDate>Sat, 10 Sep 2011 21:20:00 +0000</pubDate><atom:updated>2011-09-11T05:22:52.017+08:00</atom:updated><title>Flat Project Demo - An OS for Tablets 平板作業系統</title><description>無奈，前些日子在 COSCUP 2011 因為 Lightening Talk 時電腦出現異常，展示以失敗告終，耿耿於懷。於是趁著這次中秋假期，將 Flat Project（詳細說明請見前文：&lt;a href=&quot;http://fred-zone.blogspot.com/2011/08/flat-project.html&quot;&gt;Flat Project - 從山寨做起，親手打造炫麗的平板系統&lt;/a&gt;）開發至一半的測試影片準備好並上傳至 Youtube。但錄影效果不佳，還請包涵。&lt;br /&gt;
&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;
&lt;iframe allowfullscreen=&#39;allowfullscreen&#39; webkitallowfullscreen=&#39;webkitallowfullscreen&#39; mozallowfullscreen=&#39;mozallowfullscreen&#39; width=&#39;320&#39; height=&#39;266&#39; src=&#39;https://www.youtube.com/embed/ujJberGChOg?feature=player_embedded&#39; frameborder=&#39;0&#39;&gt;&lt;/iframe&gt;&lt;/div&gt;
&lt;br /&gt;
主要實作了 3D 視窗管理器（Window Manager+Compositor），如果不熟 X11 架構，也可將它視為 Android 上的 SurfaceFlinger，更多深入的技術細節也已經在 COSCUP 2011 &amp;nbsp;Unconference 議程做了粗略說明。藉著這個成果，可輕易將各式應用程式（Firefox+Google Map+OpenGL Application）畫在我們的 3D 物件上，因此可以運用 3D 技術對應用程式做各類特效或翻轉，在本例中，仿 iPad 的『程式啟動/關閉』和『按兩下 Home 鍵』的效果就是這樣完成的。&lt;br /&gt;
&lt;br /&gt;
此外在影片中可以看到，我們也實作了桌面程式選單和原生的 Google Map 應用程式。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;後記&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
目前除了正在補上整個環境所需的元件外和完善架構外，完美結合和運行原生的 Android 應用程式也是長期的目標。期望以過去的成果（見舊文：&lt;a href=&quot;http://fred-zone.blogspot.com/2010/08/android-is-working-on-x.html&quot;&gt;Android is Working on X&lt;/a&gt;）為基礎，讓 Android JVM 可以被移植並整合進來，使&amp;nbsp;Flat Project 可以執行各式 Linux/Meego 應用程式外，也可以跑 Android App。</description><link>https://fred-zone.blogspot.com/2011/09/flat-project-demo-os-for-tablets.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-1783823387248154581</guid><pubDate>Thu, 08 Sep 2011 18:08:00 +0000</pubDate><atom:updated>2011-09-09T02:08:47.715+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">C/C++</category><category domain="http://www.blogger.com/atom/ns#">Embedded System</category><category domain="http://www.blogger.com/atom/ns#">Linux</category><category domain="http://www.blogger.com/atom/ns#">Linux硬體驅動</category><category domain="http://www.blogger.com/atom/ns#">patch</category><category domain="http://www.blogger.com/atom/ns#">Qt</category><category domain="http://www.blogger.com/atom/ns#">系統研發手札</category><category domain="http://www.blogger.com/atom/ns#">自由軟體</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><title>發瘋的 Qt QWS TTY Keyboard Driver</title><description>最近接了一個小案子，主要是協助客戶將 Qt 應用程式從舊板子移植到新的硬體上，由於硬體是 Third-party 做，當然所有的驅動程式也是由別人處理，我們並不經手，只和硬體廠商密切合作。&lt;br /&gt;
&lt;br /&gt;
這次我們碰到了一些 Keyboard 問題，一開始是 Linux driver 缺少 input_sync() 的操作，導致 Qt 無法正確辯別按鍵的『壓放』。接著碰到個更棘手的問題，就是 Qt 送到應用程式的 Key 事件（Event）有很大機率會送錯。這將導致按鍵所觸發的結果，有很大的機會我們無法預期。檢查過 evdev event 和其發出來的 keycode，我們確定了 Linux Driver 是正確的，判斷應該是 Qt 的問題。&lt;br /&gt;
&lt;br /&gt;
經過一番檢查後，發現是因為 Qt-Embedded 預設使用 TTY Driver(qkbdtty) 去驅動鍵盤，大部份情況下不太會有問題，但因為我們走的是標準的 Linux evdev (/dev/input/eventX)，如果很快速或多重觸發鍵盤按鍵，就有可能讓 Qt 的 TTY Driver 取樣和解析出錯誤的&amp;nbsp;Event 值。最快的解決辦法就是捨棄 Qt TTY Driver，然後改用 Qt &amp;nbsp;LinuxInput Driver(qkbdlinuxinput)。&lt;br /&gt;
&lt;br /&gt;
不過 qkbdlinuxinput 並不處理 VT，這是與 qkbdtty 最大的差異，此舉會造成板子上的 Console 不正常切換，Debug 用的 Console 會因此壞掉。筆者在此對 QWS Keyboard Driver 做了些修改，讓 tty driver 使用&amp;nbsp;linuxinput driver 的方式去解析 keyboard event，並保留 VT 的處理機制。&lt;br /&gt;
&lt;br /&gt;
Patch 檔案連結如下：&lt;br /&gt;
&lt;a href=&quot;http://fred-opensource.googlecode.com/git/patches/qkbdtty_qws.patch&quot;&gt;http://fred-opensource.googlecode.com/git/patches/qkbdtty_qws.patch&lt;/a&gt;</description><link>https://fred-zone.blogspot.com/2011/09/qt-qws-tty-keyboard-driver.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-4464780896479087577</guid><pubDate>Sat, 03 Sep 2011 16:21:00 +0000</pubDate><atom:updated>2011-09-04T00:22:02.081+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Linux</category><category domain="http://www.blogger.com/atom/ns#">心情筆記</category><category domain="http://www.blogger.com/atom/ns#">自由軟體</category><title>開啟網路蟲洞穿越時空，活用 SSH Tunnel VPN</title><description>最近去了一趟上海考察，尋找創業發展的機會，但機會暫且不論，此行確實深深體會在中國的網路世界非常不一般。中國和諧的社會實在不容許影響善良風俗的事情存在，許多外來的邪惡，一一被偉大的長城擋在外面。不過，道高一尺魔高一丈，很多人很聰明，懂得翻牆方法，進出自如，多數人使用 Reverse SSH Tunnel（有興趣可參考筆者舊文『&lt;a href=&quot;http://fred-zone.blogspot.com/2010/08/reverse-ssh-tunnel.html&quot;&gt;Reverse SSH Tunnel 反向打洞實錄&lt;/a&gt;』）建立一個臨時的 Proxy Server，讓網頁連線繞道而行。&lt;br /&gt;
&lt;br /&gt;
不過，使用 Proxy Server 的方式只能讓特定的通訊協定能夠不受防火牆的阻擋，如果說要全面性的逃脫網路限制和監控，就要靠 VPN（Virtual Private Network）的方式。VPN 是什麼？簡而言之，就是與遠端的網路環境建立通道，使電腦彷彿真實身處於遠端的內部網路一般。所以 VPN 一旦建立成功，除了可以存取遠方的內部網路，當然亦可以將遠端網路當為中繼站再連到網際網路，換言之，就是可繞過防火牆的一切限制。至於&amp;nbsp;VPN 的建立方法有很多種， 但都不在本文範疇，這邊要說明的是如何用 SSH Tunnel 建立 VPN。&lt;br /&gt;
&lt;br /&gt;
設定必要的環境變數後，直接執行以下的 Script，就可以和遠端 Server 建立 VPN 連線：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;#!/bin/bash

# Local Network
NETWORK=192.168.16.0
GW=192.168.16.1

# Server IP Address
SERVER=123.123.123.123
# Server 對外的 Interface
SERVER_IF=eth0

echo &quot;Creating Connection&quot;
ssh -w 0:0 -f $SERVER &quot;ifconfig tun0 10.0.2.1 netmask 255.255.255.252 pointopoint 10.0.2.2 ; echo 1 &amp;gt; /proc/sys/net/ipv4/ip_forward ;/sbin/iptables -t nat -A POSTROUTING -o $SERVER_IF -j MASQUERADE ;route add -net $NETWORK gw 10.0.2.2 dev tun0&quot;

echo &quot;Settingi local interface&quot;
ifconfig tun0 10.0.2.2 netmask 255.255.255.252 pointopoint 10.0.2.1
route add -net ${SERVER%.*}.0/24 gw 10.0.2.1 dev tun0
route add $SERVER gw $GW
route add default gw 10.0.2.1 tun0
route del default gw $GW&lt;/pre&gt;&lt;/div&gt;註一：如果不能正確建立連線，請先確認 SSH Port(22) 是否已被防火牆阻擋。&lt;br /&gt;
註二：記得修改 /etc/resolv.conf 的 DNS 設定，有些網路是直接在名稱解析做阻擋（如：在中國的網路）。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;後記&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
SSH 有高度的安全性，透過 SSH 建立 VPN，也可以多少避免網路被當下的網路提供者竊聽。</description><link>https://fred-zone.blogspot.com/2011/09/ssh-tunnel-vpn.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>3</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-5949986940560605239</guid><pubDate>Wed, 31 Aug 2011 13:55:00 +0000</pubDate><atom:updated>2011-09-04T16:31:35.449+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Desktop</category><category domain="http://www.blogger.com/atom/ns#">Juice</category><category domain="http://www.blogger.com/atom/ns#">Linux</category><category domain="http://www.blogger.com/atom/ns#">User Interface</category><category domain="http://www.blogger.com/atom/ns#">X11</category><category domain="http://www.blogger.com/atom/ns#">技術新知</category><category domain="http://www.blogger.com/atom/ns#">自由軟體</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><title>新鮮果汁吧！果汁桌面環境(Juice Desktop Environment)！</title><description>筆者喜愛喝果汁，故為這個新的 Project 取了 Juice 這樣的名稱，意旨一個自己喜愛的桌面環境。在前些日子，因為放棄了 GNOME 而轉向使用 Enlightenment（以下簡稱E17），雖然各方面還算不錯，但在新版 Package 的更新上實在是缺少人在維護。因此，若是使用 Debian 官方版本的 E17 Package，功能非常少，而 E17 官方的 Repository 版本又太舊。其實，原本希望能幫忙打包，但因為種種因素而做罷。&lt;br /&gt;
&lt;br /&gt;
既然本身需求不高，就發揮今年貫徹執行的土砲精神，在空閒時間動手開發一個合用的桌面環境。目前，已經釋出其中一個 Dock/Panel 元件『&lt;a href=&quot;http://code.google.com/p/jushelf/&quot;&gt;JuShelf(Juice Shelf)&lt;/a&gt;』的初步版本，也已經有實作模組(Module)機制，和一種 module。下面是畫面截圖（Screenshot）和最新的展示影片：&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiPj1UeIkUT_2khnKdsi38r9mqfG6tqKZ5lMaPgpVx27YIygnK2kN8HtjIejxf9Ol6-BokzNyJCNBI67RZamzEYXsMfxqk-BHBmy-xeL6UwLjHN8-mFg1VJznKhxUt0Y1fGKUSXzqgB7Iw/s1600/jushelf.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;79&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiPj1UeIkUT_2khnKdsi38r9mqfG6tqKZ5lMaPgpVx27YIygnK2kN8HtjIejxf9Ol6-BokzNyJCNBI67RZamzEYXsMfxqk-BHBmy-xeL6UwLjHN8-mFg1VJznKhxUt0Y1fGKUSXzqgB7Iw/s320/jushelf.png&quot; width=&quot;320&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;
&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;iframe allowfullscreen=&#39;allowfullscreen&#39; webkitallowfullscreen=&#39;webkitallowfullscreen&#39; mozallowfullscreen=&#39;mozallowfullscreen&#39; width=&#39;320&#39; height=&#39;266&#39; src=&#39;https://www.youtube.com/embed/zmAa6b13fhE?feature=player_embedded&#39; frameborder=&#39;0&#39;&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;br /&gt;
&lt;br /&gt;
畫面中展示的是 Launch module，能提供圖示讓使用者點擊並啟動應用程式。其中的特效設計是放大圖示然後傾斜，帶有相當的趣味性。&lt;br /&gt;
&lt;br /&gt;
目前仍然待完成的模組：&lt;br /&gt;
&lt;br /&gt;
&lt;ol&gt;&lt;li&gt;工作視窗清單(taskbar)&lt;/li&gt;
&lt;li&gt;系統狀態顯示區(systray)&lt;/li&gt;
&lt;li&gt;時間顯示(Clock)&lt;/li&gt;
&lt;/ol&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;&lt;b&gt;後記&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;目前 Juice 本身還沒有 Project 網站，只是以個人動機為出發點，若是日後有較完整的開發成果，會再補上。此外，Juice 初期打算使用 Compiz 或 Mutter 做為視窗管理器（Window Manager），以及配合一基本的 GNOME 小元件（如同筆者前些日子在整合 E17 和 GNOME 一般）。&lt;/div&gt;</description><link>https://fred-zone.blogspot.com/2011/08/juice-desktop-environment.html</link><author>noreply@blogger.com (Fred Chien)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiPj1UeIkUT_2khnKdsi38r9mqfG6tqKZ5lMaPgpVx27YIygnK2kN8HtjIejxf9Ol6-BokzNyJCNBI67RZamzEYXsMfxqk-BHBmy-xeL6UwLjHN8-mFg1VJznKhxUt0Y1fGKUSXzqgB7Iw/s72-c/jushelf.png" height="72" width="72"/><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-2022597570279125087</guid><pubDate>Tue, 30 Aug 2011 22:46:00 +0000</pubDate><atom:updated>2011-08-31T06:48:03.859+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">C/C++</category><category domain="http://www.blogger.com/atom/ns#">Embedded System</category><category domain="http://www.blogger.com/atom/ns#">epaper</category><category domain="http://www.blogger.com/atom/ns#">Linux</category><category domain="http://www.blogger.com/atom/ns#">Qt</category><category domain="http://www.blogger.com/atom/ns#">程式心得筆記</category><category domain="http://www.blogger.com/atom/ns#">系統研發手札</category><title>針對『電子紙』優化 Qt 繪圖事件</title><description>最近手上有一些電子紙的案件，指定使用 Qt 做為圖型平台。與一般裝置不一樣的地方，電子紙的更新頻率很低，平均下來每秒頂多刷新一次，相對於 LCD Panel 的每秒 60 次起跳，實在低的可憐。對於專職繪圖的 Qt 而言，要如何壓低刷新的頻率和確認畫面繪圖完成，最後再推入 Display，便是一個重要的課題。&lt;br /&gt;
&lt;br /&gt;
壓低更新頻率比較單純，透過修改 Framebuffer Driver 就可以達成。此外，也必須利用 setUpdatesEnabled() 和 update() 盡量避免 Qt 連續多次的繪圖需求，合併一次送入 framebuffer，這樣可以減少&amp;nbsp;Qt 來不及畫完和畫面閃爍的問題。&lt;br /&gt;
&lt;br /&gt;
到此為止只是改善顯示效果和基礎的建置，真正要完成完美的一次推送，通常需要使用電子紙驅動程式所提供的額外 API，它同意我們自己決定何時將 Framebuffer 的資料推送到紙上。&lt;br /&gt;
&lt;br /&gt;
至於何時該將畫面推送至電子紙上？這時需要針對 QApplication 做一些修改，監聽和自己創造一個新的事件：&lt;br /&gt;
&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;const QEvent::Type UpdateScreenEvent = (QEvent::Type)1234;
bool Application::notify(QObject *obj, QEvent *e)
{

&amp;nbsp; &amp;nbsp; &amp;nbsp;if (e-&amp;gt;type() == UpdateScreenEvent) {
&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; if (updateScreen) {
&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; EPaperDriverCtrl *ctrl = (EPaperDriverCtrl*)EPaperDriverCtrl;
&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ctrl-&amp;gt;pushFBtoPanel();
&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; updateScreen = false;
&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; }
&amp;nbsp;
&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; return true;
&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;} else if (e-&amp;gt;type() == QEvent::Paint) {
&amp;nbsp; &amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; updateScreen = true;
&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; QApplication::postEvent(this, new QEvent(UpdateScreenEvent));
&amp;nbsp;&amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;}

&amp;nbsp; &amp;nbsp;&amp;nbsp;return QApplication::notify(obj, e);
}
&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;
原理相當簡單，監聽所有的繪圖事件，然後送一個自定的畫面推送事件，推送事件會在所有繪圖事件完成後被執行。最後再利用 updateScreen 這個旗標來避免多次的推送事件，無論有多少推送要求，都只做一次的推送。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;後記&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
Qt 的優點就不必多說，經過十多年的演進，已成為世上數一數二的圖形化系統，雖然 Nokia 在政治考量上，在未來產品中放棄了使用基於 Qt 的 Meego，但仍不滅 Qt 的威風，因為他早已深入各個大大小小嵌入式系統和桌面系統，我們常聽到的 KDE 便是使用 Qt 的產物。和多數 Open Source Solution 相比，Qt&amp;nbsp;最大的不同是採用了 C++，所以開發速度快捷，又由於長年來在嵌入式系統上的發展，也有很大的彈性和效能，此外 footprint 也相當小。</description><link>https://fred-zone.blogspot.com/2011/08/qt.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>0</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-1388382223892653556</guid><pubDate>Sun, 21 Aug 2011 22:35:00 +0000</pubDate><atom:updated>2011-08-22T06:35:36.448+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Embedded System</category><category domain="http://www.blogger.com/atom/ns#">Flat</category><category domain="http://www.blogger.com/atom/ns#">Linux</category><category domain="http://www.blogger.com/atom/ns#">Tablet</category><category domain="http://www.blogger.com/atom/ns#">User Interface</category><category domain="http://www.blogger.com/atom/ns#">Window Manager</category><category domain="http://www.blogger.com/atom/ns#">X11</category><category domain="http://www.blogger.com/atom/ns#">心得分享、講座</category><category domain="http://www.blogger.com/atom/ns#">技術新知</category><category domain="http://www.blogger.com/atom/ns#">自由軟體</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><title>Flat Project - 從山寨做起，親手打造炫麗的平板系統</title><description>已經過了近兩年，至今仍然沒有一台 PC 廠商做的平板電腦能勝過 iPad，精緻度估且不論，其速度與流暢度，相較之下只能堪稱工程機的程度。其實真正原因不在於這些廠商行銷廣告中的 CPU 『數量』，而是 Android 系統軟體本身處處存在了一些效能上的問題，重點是這些問題不是工程師所在意的，而且吃力又不一定討好，沒人會拿飯碗去賭。另一方面，Android UI 設計永遠就像工程師自我良好的作品，單獨看每一個元件都很漂亮，可是拼裝起來後感覺就是盤剩菜剩飯，就算換了 UI，也不過只是換了封面罷了，換湯不換藥。&lt;br /&gt;
&lt;br /&gt;
Open Source Project 的開發，最困難的就是修改機制，在很多時候，我們只有能力挖肉，沒能力整骨，畢竟整個 Project 不是我們自己寫的。當然，也因此很多設計是無法加上去的，就算加上去也無法好用。就像現在廠商所提倡的軟硬體垂直整合，雖然我們已經有了硬體與軟體的溝通，但在應用軟體到軟體系統之間，其實更需要有好的垂直整合，否則出現斷層後，就像現在 Android 平板總是說不出的有問題。也難怪有人在罵大多數場商只是把 Open Source Software 隨便放到硬體上就拿出來賣，根本不用心。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;我們何不來自己動手寫一個平板系統？&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
運用現成的 Open Source Project 為基礎，以 iPad 為學習目標，重新打造一個平板用的作業系統。重要的是，品質要能出貨，又有好的擴充性和可用的底層機制，而不只是用套軟體拉一拉 UI 就完事。&lt;br /&gt;
&lt;br /&gt;
&lt;blockquote&gt;&lt;b&gt;Mandice Flat Project&lt;/b&gt;（&amp;nbsp;&lt;a href=&quot;http://code.google.com/p/flat&quot;&gt;http://code.google.com/p/flat&lt;/a&gt;）&lt;/blockquote&gt;&lt;br /&gt;
&lt;br /&gt;
[&lt;a href=&quot;http://code.google.com/p/flat&quot;&gt;Flat Project&lt;/a&gt;] 就是一個這樣的產物，目標是自己動手打造一個開放的平板環境。（目前已經釋出程式碼的子項目是 GrandPa（視窗管理器），是一個仿 iPad 視窗行為的 3D&amp;nbsp;Window Manager。）&lt;br /&gt;
&lt;br /&gt;
Flat 的起源，[&lt;a href=&quot;http://www.mandice.com/&quot;&gt;Mandice&lt;/a&gt;] 是這幾年間與不少大大小小廠商合作過案子，已有不少經驗和成果。所以在今年的 [&lt;a href=&quot;http://coscup.org/&quot;&gt;COSCUP 2011&lt;/a&gt;] 活動，筆者一時興起，便把過去案子所開發的各種元件抽出來再開發並陸續釋出，這就是&amp;nbsp;[&lt;a href=&quot;http://code.google.com/p/flat&quot;&gt;Flat Project&lt;/a&gt;] 的由來，當然已經移除不應該公開的商業部份。&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;後記&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
順帶一提，今年度 COSCUP 2011 的 Unconference Session 筆者有講如何設計 Window Manager，有興趣的人可以以 GrandPa 做為實例印證。&lt;br /&gt;
</description><link>https://fred-zone.blogspot.com/2011/08/flat-project.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>7</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-9013904973463290682</guid><pubDate>Mon, 18 Jul 2011 02:53:00 +0000</pubDate><atom:updated>2011-07-18T10:53:29.758+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">MongoDB</category><category domain="http://www.blogger.com/atom/ns#">Web 相關技術</category><category domain="http://www.blogger.com/atom/ns#">程式心得筆記</category><category domain="http://www.blogger.com/atom/ns#">系統研發手札</category><category domain="http://www.blogger.com/atom/ns#">自由軟體</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><title>MongoDB 快速筆記</title><description>這幾年&amp;nbsp;Web Service 的龐大需求，對資料庫的要求是快速且吞吐量大，因此業界開始流行 NoSQL，它省略最花時間的資料庫操作和複雜的結構，用最合人類使用需求的方式在儲存資料，其帶來的好處，當然就是能提供極速的反應和龐大的資料吞吐量。目前最廣為人知的就是 BigTable，Google 提供全世界快速搜尋和各種線上服務，靠的就是這 NoSQL Database。&lt;br /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;其實 NoSQL Database 的選擇非常多，但本文只是記錄 MongoDB 的操作筆記，因此就不詳述。 有興趣的人，可以去查閱網路上更多的資料。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;選擇 Database：&lt;/div&gt;&lt;div&gt;&lt;pre&gt;use my_database&lt;/pre&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;利用 root 新增&amp;nbsp;Database 的管理帳號：&lt;/div&gt;&lt;div&gt;&lt;pre&gt;# 切換到 my_database
use my_database
# 先用 root 帳號認證
db.getSisterDB(&quot;admin&quot;).auth(&quot;root&quot;, &quot;rootpassword&quot;);
# 新增 frankie 帳號
db.addUser(&quot;frankie&quot;, &quot;fredpassword&quot;);&lt;/pre&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;新增 Database 的管理帳號：&lt;/div&gt;&lt;div&gt;&lt;pre&gt;# 切換到 my_database
use my_database
# 先用&amp;nbsp;frankie&amp;nbsp;帳號認證
db.auth(&quot;frankie&quot;, &quot;fredpassword&quot;);
# 新增 zombie 帳號
db.addUser(&quot;zombie&quot;, &quot;zombiepassword&quot;);&lt;/pre&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;插入新增資料（Insert）：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;# 插入一筆新資料到 users Collection （相當於傳統 SQL 裡的 Table 角色）
db.users.insert({ username: &quot;fred&quot;, password: &quot;12345678&quot; })
# 含當前時間
db.users.insert({ username: &quot;fred&quot;, password: &quot;12345678&quot;, created: new Timestamp() })&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;查詢（Query）：&lt;/div&gt;&lt;div&gt;&lt;pre&gt;# 查詢所有 username 為 fred 的資料
db.users.find({ username: &quot;fred&quot; })
# 查詢單筆
db.users.find_one({ username: &quot;fred&quot; })&lt;/pre&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;修改更新資料（Update）：&lt;/div&gt;&lt;div&gt;&lt;pre&gt;# 更新 username 為 fred 的 password 欄位
db.users.update({ username: &quot;fred&quot; }, { $set: { password: &quot;87654321&quot; }})
# 完全取代 username 為 fred 的資料（username 和 password&amp;nbsp;欄位會因此不見，只剩下 email 欄位）
db.users.update({ username: &quot;fred&quot; }, { email: &quot;cfsghost@gmail.com&quot;})&lt;/pre&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;刪除資料（Delete）：&lt;/div&gt;&lt;div&gt;&lt;pre&gt;db.users.remove({ username: &quot;fred&quot; })&lt;/pre&gt;&lt;/div&gt;</description><link>https://fred-zone.blogspot.com/2011/07/mongodb.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>1</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-1663430681042917567</guid><pubDate>Tue, 12 Jul 2011 03:06:00 +0000</pubDate><atom:updated>2011-07-12T11:25:44.032+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">E17</category><category domain="http://www.blogger.com/atom/ns#">GNOME</category><category domain="http://www.blogger.com/atom/ns#">Linux</category><category domain="http://www.blogger.com/atom/ns#">Window Manager</category><category domain="http://www.blogger.com/atom/ns#">X11</category><category domain="http://www.blogger.com/atom/ns#">系統研發手札</category><category domain="http://www.blogger.com/atom/ns#">自由軟體</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><title>不想用傻逼 GNOME3 ！好牛逼的雜牌軍替代方案 E17+GNOME/XFCE/Fluxbox Component！</title><description>好不容易，Linux 桌面經過十多年的演進，GTK+ 和 GNOME 總算進入了 3.0 的時代，向來最愛仗著『使用者之名』做盡任何事的 Ubuntu，也推出了他們的 Unity 介面，試圖重新打造桌面使用者的習慣。可惜的是，這些新的桌面設計雖然帶來了完全不一樣體驗，卻也造成不少使用者操作思維的混亂；更可怕的，這些標新立異的改變，將原本『好不容易』成熟穩定下來的桌面系統，在短時間內，又再次推向重新建立習慣和軟體崩潰的循環地獄。&lt;br /&gt;
&lt;br /&gt;
網路上一篇討論文章『&lt;a href=&quot;http://moto.debian.tw/viewtopic.php?f=27&amp;amp;t=16147&quot;&gt;Linux的桌面為什麼這麼傻逼&lt;/a&gt;』（這篇文章是有心人翻譯的，內有原文連結），對 Linux 桌面環境有很獨道的見解和體驗，其批判性的強烈言詞，可以感覺到這些年作者的沉痛經歷。&lt;br /&gt;
&lt;br /&gt;
就某方面來說，筆者相當讚同該文的論調，本身就長期使用 Linux 桌面，不時因為各種桌面系統的問題，親自動手去做程式開發或調整，可以說該文道盡筆者心聲。不過最近這一兩年，&amp;nbsp;GNOME 已經可以算是很好用的桌面環境，程式也很穩定，周遭初入 Linux 的朋友們也都可以輕易上手。但高興沒辦法太早，&amp;nbsp;GNOME 3.0 在此時投下了一顆超級炸彈，其更新除了讓許多元件壞東壞西，使用操作和程式開發上完全讓人覺得陌生。&lt;br /&gt;
&lt;br /&gt;
喔不！我不要再經歷一次『桌面環境的黑暗時代』。我只想穩穩定定且不要有意外的使用著我的作業系統，所以我也拒絕 GNOME3 和 Unity。在一切混亂的情況下，Enlightenment（簡稱 E17） 帶來了一線曙光。&lt;br /&gt;
&lt;br /&gt;
我對桌面環境的要求其實不高：&lt;br /&gt;
&lt;ul&gt;&lt;li&gt;可用性高，穩定度和使用性最好不要與 GNOME 2.0 有太大的差異。&lt;/li&gt;
&lt;li&gt;速度快&lt;/li&gt;
&lt;li&gt;漂亮又炫麗（最好能夠有 3D 桌面的支援，這讓我覺得我的系統比 Windows 高級）&lt;/li&gt;
&lt;li&gt;省系統資源&lt;/li&gt;
&lt;li&gt;畫面易客製化（如果能讓我看起來更像個專業&lt;strike&gt;&lt;i&gt;宅男&lt;/i&gt;&lt;/strike&gt;&amp;nbsp;Hacker更好）&lt;/li&gt;
&lt;/ul&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;經過一些拼裝和調整後，這是用 Enlightenment + GNOME Component + Thunar File Manager(XFCE) 組裝的桌面環境其最後樣貌：&lt;/div&gt;&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgaVYIPmVjin7urNg55aKJ-ONvkHBhn62frqfJihN7dOz2fx4qelVDimGv3utU3KNLVbufk9aI-YW9TnXJmWsmEHRd-EaiyBWEkWFQGLqF5xUiegwttD4QEJLbHSoG2CpZyzrAO04wTI8M/s1600/Screenshot.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;225&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgaVYIPmVjin7urNg55aKJ-ONvkHBhn62frqfJihN7dOz2fx4qelVDimGv3utU3KNLVbufk9aI-YW9TnXJmWsmEHRd-EaiyBWEkWFQGLqF5xUiegwttD4QEJLbHSoG2CpZyzrAO04wTI8M/s400/Screenshot.png&quot; width=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;Debian 使用者，可以照下面步驟拼裝出同樣的桌面環境（當然畫面上的元件排版要依各自喜好自行調整）：&lt;/div&gt;&lt;div&gt;&lt;ol&gt;&lt;li&gt;去&amp;nbsp;&lt;a href=&quot;http://packages.enlightenment.org/&quot;&gt;http://packages.enlightenment.org/&lt;/a&gt;&amp;nbsp;尋找和系統相對應的 Repository（筆者將以 Debian Sid 為例）&lt;/li&gt;
&lt;li&gt;在 /etc/apt/sources.list 裡加入：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;&lt;i&gt;deb http://packages.enlightenment.org/debian sid main extras&lt;/i&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;建立 /etc/apt/preferences.d/e17 空白檔案，然後寫入：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;&lt;i&gt;Package: *
Pin: origin packages.enlightenment.org
Pin-Priority: 600&lt;/i&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;下載並加入 E17 的 GPG Key：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;&lt;i&gt;wget http://packages.enlightenment.org/repo.key
sudo apt-key add repo.key&lt;/i&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;更新 Apt 套件清單和安裝 E17 + Compiz 相關 modules：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;&lt;i&gt;sudo apt-get update
sudo apt-get install e17 emodule-ecomorph compiz compiz-fusion-bcop compiz-plugins&amp;nbsp;&lt;/i&gt;&lt;i&gt;compiz-fusion-plugins-extra&amp;nbsp;&lt;/i&gt;&lt;i&gt;compiz-fusion-plugins-main&amp;nbsp;&lt;/i&gt;&lt;i&gt;compiz-fusion-plugins-unsupport compiz-gnome&lt;/i&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;安裝必要的 GNOME Components 和系統常駐程式（登入介面、GTK+環境管理、電源管理、網路管理、自動掛載管理）：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;&lt;i&gt;sudo apt-get install gdm3 gnome-settings-daemon gnome-power-manager gnome-screensaver gnome-screenshot network-manager-gnome udisks-glue&lt;/i&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;安裝 XFCE 的 Thunar 檔案管理程式（因為 E17 的不好用，而 GNOME Nautilus 太慢）：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;&lt;i&gt;sudo apt-get install thunar&lt;/i&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;安裝 Fluxbox 的 fbautostart（因為 E17 預設不支援 freedesktop.org autostart spec）&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;&lt;i&gt;sudo apt-get install fbautostart&lt;/i&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;重新啟動進入至 Enlightenment 桌面環境&lt;/li&gt;
&lt;li&gt;設定桌面環境，用滑鼠左鍵點擊桌面空白處，選擇 『Settings』→『Settings Panel』&lt;/li&gt;
&lt;ul&gt;&lt;li&gt;設定視窗外觀&lt;/li&gt;
&lt;ol&gt;&lt;li&gt;&lt;span class=&quot;Apple-style-span&quot;&gt;開啟『Extensions』&lt;/span&gt;&lt;span class=&quot;Apple-style-span&quot;&gt;→&lt;/span&gt;&lt;span style=&quot;font-weight: normal;&quot;&gt;『Modules』&lt;/span&gt;&lt;/li&gt;
&lt;span style=&quot;font-weight: normal;&quot;&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-weight: normal;&quot;&gt;&lt;b&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-weight: normal;&quot;&gt;&lt;b&gt;&lt;span class=&quot;Apple-style-span&quot; style=&quot;font-weight: normal;&quot;&gt;&lt;a href=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjvI3Sce2IQHwCvOvms9vUS9QwrI0l9oTVOMON5nI6N5YEgZTU26yS2Sm0xANnzPeMjgbIajcG0csDW9XR8KtZer6RuFfwgmqozCnOJhA_9VdTkEgGXI4esNU41E4Om8lk6AiE6oCEzdaU/s1600/Screenshot-Module+Settings.png&quot; imageanchor=&quot;1&quot; style=&quot;margin-left: 1em; margin-right: 1em;&quot;&gt;&lt;img border=&quot;0&quot; height=&quot;200&quot; src=&quot;https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjvI3Sce2IQHwCvOvms9vUS9QwrI0l9oTVOMON5nI6N5YEgZTU26yS2Sm0xANnzPeMjgbIajcG0csDW9XR8KtZer6RuFfwgmqozCnOJhA_9VdTkEgGXI4esNU41E4Om8lk6AiE6oCEzdaU/s200/Screenshot-Module+Settings.png&quot; style=&quot;cursor: move;&quot; width=&quot;170&quot; /&gt;&lt;/a&gt;&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/span&gt;
&lt;li&gt;將右方『Loaded Modules』的 Appearance 清空只剩下 Gadgets（如果你的系統沒有 3D 硬體支援不能跑 Compiz&amp;nbsp;，請保留 Bling 並略過下一步驟）&lt;/li&gt;
&lt;li&gt;從左方『Available Modules』的 System 找到 Ecomorph 載入（啟動 E17 的 Compiz Module）&lt;/li&gt;
&lt;/ol&gt;&lt;li&gt;載入系統小元件&lt;/li&gt;
&lt;ol&gt;&lt;li&gt;&lt;span class=&quot;Apple-style-span&quot;&gt;開啟『Extensions』&lt;/span&gt;&lt;span class=&quot;Apple-style-span&quot;&gt;→&lt;/span&gt;&lt;span style=&quot;font-weight: normal;&quot;&gt;『Modules』&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;font-weight: normal;&quot;&gt;這邊有很多桌面小元件像是工具列、時鐘等等，可依個人喜好載入，若不明白，可以將所有可能需要的元件載入，等桌面版面調整好後再回來卸載（不卸載會吃系統資源）。&lt;br /&gt;
&lt;br /&gt;
註：有需多元件需要先安裝 Package，可去尋找『emodule-*』套件。&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;&lt;li&gt;設定桌面系統程式&lt;/li&gt;
&lt;/ul&gt;&lt;ol&gt;&lt;ol&gt;&lt;li&gt;利用&lt;span class=&quot;Apple-style-span&quot;&gt;『Apps』&lt;/span&gt;&lt;span class=&quot;Apple-style-span&quot;&gt;→&lt;/span&gt;『New Application』新增下列系統程式：&lt;br /&gt;
&lt;div&gt;&lt;pre&gt;Name: &lt;i&gt;Autostart Daemon&lt;/i&gt;
Executable: &lt;i&gt;/usr/bin/fbautostart&lt;/i&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div&gt;&lt;pre&gt;Name: &lt;i&gt;GNOME Settings Daemon&lt;/i&gt;
Executable:&amp;nbsp;&lt;i&gt;gnome-settings-daemon&lt;/i&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div&gt;&lt;pre&gt;Name: &lt;i&gt;GNOME Screensaver Daemon&lt;/i&gt;
Executable: &lt;i&gt;gnome-screensaver&lt;/i&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div&gt;&lt;pre&gt;Name:&lt;i&gt;&amp;nbsp;GNOME Power Manager&lt;/i&gt;
Executable:&amp;nbsp;&lt;i&gt;gnome-power-manager&lt;/i&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div&gt;&lt;pre&gt;Name:&amp;nbsp;&lt;i&gt;Disk Automount Daemon&lt;/i&gt;
Executable:&amp;nbsp;&lt;i&gt;udisks-glue&lt;/i&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;利用&lt;span class=&quot;Apple-style-span&quot;&gt;&lt;span class=&quot;Apple-style-span&quot;&gt;&lt;span class=&quot;Apple-style-span&quot;&gt;『Apps』&lt;/span&gt;&lt;/span&gt;→&lt;/span&gt;『Startup Application』將此五項系統程式加到啟動程式清單。&lt;/li&gt;
&lt;/ol&gt;&lt;ul&gt;&lt;/ul&gt;&lt;/ol&gt;&lt;li&gt;&lt;span class=&quot;Apple-style-span&quot;&gt;重新啟動進入至 Enlightenment 桌面環境，桌面程式安裝完成。&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;&lt;div&gt;到目前為止，你可能覺得桌面空有特效，但預設介面不是很好用。這時可以移除掉系統正下方難用的 Dock，並利用 Shelf 建立喜好的 Dock 或各式工具列，打造合乎自己使用習慣的介面。以筆者的喜好為例：&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;視窗清單 Taskbar&lt;/li&gt;
&lt;li&gt;快捷圖示 Ibar&lt;/li&gt;
&lt;li&gt;系統程式選單 Starter&lt;/li&gt;
&lt;li&gt;時鐘 Clock &amp;amp; TClock&lt;/li&gt;
&lt;li&gt;系統常駐程式列&amp;nbsp;Systray（無線網路管理、電源狀態和應用程式常駐）&lt;/li&gt;
&lt;li&gt;音量控制 Mixer&lt;/li&gt;
&lt;/ul&gt;在之前的螢幕截圖可以看到，筆者將這些元件分成四個 Shelf （中上、中下、右上、右下）擺放。如果你沒有其他想法，也可以照這樣擺放，這樣設定和 GNOME 環境的使用上差異不大。當然，和 GNOME 一樣將這些元件放在同個 bar 上也是可行的。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;&lt;b&gt;後記&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;身為 LXDE 的開發者，不得不承認 Enlightenment 非常輕量和快速，就算沒有 3D 硬體支援，外觀和速度上仍然遠勝所有的桌面環境。如果開著 Compiz 和許多桌面元件，系統記憶體是 300MB 綽綽有餘，且環境整合度相當好。若是再配合上 GNOME 和各家桌面的元件後，可用性和各桌面應用程式的相容性絕不輸給純 GNOME。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;唯一目前已知問題是 E17 的 Place Gadget 和 File Manager 還是使用舊的 HAL 標準（現在的一般標準是使用 udisks），所以這些元件還沒辦法去 Umount 經由標準 Automount 機制的硬碟。因此筆者在這部份暫時使用 XFCE Thunar 來代替，或許過些時間，E17 的開發團隊會修正此問題。&lt;/div&gt;&lt;/div&gt;</description><link>https://fred-zone.blogspot.com/2011/07/gnome3-e17gnomexfcefluxbox-component.html</link><author>noreply@blogger.com (Fred Chien)</author><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgaVYIPmVjin7urNg55aKJ-ONvkHBhn62frqfJihN7dOz2fx4qelVDimGv3utU3KNLVbufk9aI-YW9TnXJmWsmEHRd-EaiyBWEkWFQGLqF5xUiegwttD4QEJLbHSoG2CpZyzrAO04wTI8M/s72-c/Screenshot.png" height="72" width="72"/><thr:total>14</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-5637638640248944215</guid><pubDate>Sat, 09 Jul 2011 03:26:00 +0000</pubDate><atom:updated>2011-07-09T12:54:59.923+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">心情筆記</category><title>『假新鮮人』獻給『真新鮮人』的話</title><description>畢業潮近了，學校又將放出一大群新鮮的人才，而每年這個時候，我總會有許多感觸。過去，是看著年紀相仿的朋友投履歷找工作，近距離觀察新鮮人準備進入職場；現在，則是看著後輩跌跌撞撞，撞出許多可歌可泣的新鮮事。&lt;br /&gt;
&lt;br /&gt;
一切都很新鮮，我們年少時對未來都有憧憬，畢業後準備進入職場，更是抱有許多夢想。有人想努力內求更上一層樓，有人想賺錢獨立，各有各的目標和想法，人生方向就因此不同。這些美麗的夢，就像小學生寫作文題目『我的座右銘』，開學時人人都可以把自我期許講得頭頭是道，但每當學期末拿模範生或各種獎項的人數來看，便知道起而行的人寥寥可數。&lt;br /&gt;
&lt;br /&gt;
以年紀來說，我應該算是社會大學的新生，但因為在學時就已經開啟了接案生涯，對社會種種並不陌生，所以嚴格說起，我是『假新鮮人』。這些社會經歷，雖微不足道，卻也讓我開了一些眼界。因此這篇文章，其實是獻給過去十幾歲的自己，也是算一種反省式的認罪，更是對生涯的交代。&lt;br /&gt;
&lt;br /&gt;
我承認，當年。&lt;br /&gt;
&lt;ol&gt;&lt;li&gt;許多案子總讓我提不起興趣，做起來總拖泥帶水。&lt;/li&gt;
&lt;li&gt;能夠最後一天做完，我絕不會提前兩天開始做。&lt;/li&gt;
&lt;li&gt;一旦工作做不完，最好不要讓我抓到別人的把柄，因為我將有藉口全身而退。&lt;/li&gt;
&lt;li&gt;累一天，就好像累一個星期。&lt;/li&gt;
&lt;li&gt;別人會如何我不在乎，我在乎的是自己的信用和利益。&lt;/li&gt;
&lt;li&gt;因為你們講的問題或臭蟲我都看不到，所以我給的成果完全沒問題。&lt;/li&gt;
&lt;li&gt;心裡總是想：『上頭賺很多在口袋裡，應該分我吧。』&lt;/li&gt;
&lt;li&gt;管你再急，只要我現在情緒不好，我就不鳥你。&lt;/li&gt;
&lt;li&gt;我做到這樣就夠了，反正我東西交了差已經丟了出去，剩下是你們家的事。&lt;/li&gt;
&lt;li&gt;不管我做對做錯或做得好不好，只要有做就沒有對不起任何人。&lt;/li&gt;
&lt;li&gt;網路神通廣大，讓我得到一堆人云亦云的知識，所以總是任意亂入別人的話題講些人云亦云的假知識。 &lt;/li&gt;
&lt;li&gt;我不管其他人說什麼，我認為該這樣做就這樣做，哪怕造成別人的困擾。&lt;/li&gt;
&lt;li&gt;只要老闆沒發現，就算我在混，時間也算是賣給老闆。&lt;/li&gt;
&lt;li&gt;如果有事情會需要讓我在私人時間做，那就一定是老闆的問題。&lt;/li&gt;
&lt;li&gt;總覺得自己做的事最關鍵也最重要，所以自己做一件事抵別人做十件。&lt;/li&gt;
&lt;li&gt;我只要做完了事，不管過程或結果有沒有造成別人不便或損失，都不是我的責任，所以賠錢也不干我的事。但是，給我的錢是萬萬不能少。&lt;/li&gt;
&lt;/ol&gt;同時，我總有藉口和說詞。&lt;br /&gt;
&lt;ol&gt;&lt;li&gt;無論工作有多少，只要不想做就說：『是你們沒評估好我的能力，工作太多或太難，所以我達不成。』&lt;/li&gt;
&lt;li&gt;『因為我東西有在最後一刻給了出來，所以 Project 有任何延誤也不干我的事。』&lt;/li&gt;
&lt;li&gt;『我已經很努力做了，請不要再亂給我壓力。我需要有休息時間。』&lt;/li&gt;
&lt;li&gt;『我拿的錢又不多，這不是我的責任。』&lt;/li&gt;
&lt;li&gt;碰到質疑時第一時間不論內容先回：『這不是我的問題，是 OOXX 的問題。』&lt;/li&gt;
&lt;li&gt;工作效率差時：『機器太慢，不是我的問題。』&lt;/li&gt;
&lt;li&gt;『這東西太無聊，我不想做。』&lt;/li&gt;
&lt;li&gt;總是抱怨：『上面都給我一些沒意義的工作，又學不到東西，浪費我時間。』&lt;/li&gt;
&lt;/ol&gt;&lt;br /&gt;
以上是許多年前的我，問題比較多，但在遭逢各種遽變後，開始時時刻刻提醒自己，不要再犯。在此，也向當時與我共事的人說聲抱歉，並請原諒我的不成熟。&lt;br /&gt;
&lt;br /&gt;
反觀現在的新鮮人，沒自信的人就不用說了，自然是維維諾諾。若是有些才能，普遍性有我當年同樣的不少惡習。另外，這些年下來，私下觀察其他人後，也發現額外的一些問題不在上列，在這就不討論了。&lt;br /&gt;
&lt;br /&gt;
這次畢業潮，期望『真新鮮人』們一同努力，避免不良惡習，共勉之。</description><link>https://fred-zone.blogspot.com/2011/07/blog-post.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>2</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-2335762796038502650</guid><pubDate>Mon, 25 Apr 2011 20:22:00 +0000</pubDate><atom:updated>2011-04-26T04:26:12.910+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Android</category><category domain="http://www.blogger.com/atom/ns#">自由軟體</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><title>不要小看華人呀，Android App 的逆向工程！</title><description>還記得在某次的 COSCUP 與 Google 的龐教授，交流了一些 Android 方面的意見。由於他專精於 Compiler，我們也對 Dalvik Virtual Machine 的部份有些許的討論。當時感到非常榮幸，也覺得驕傲，因為這樣發光於全球的 Project&amp;nbsp;，也有華人在其中，更難得的是就在眼前。&lt;br /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;最近空閒時間在研究一些 Android 的實作，煩腦之際，於 Google Code 發現了一個對岸朋友針對 apk 的逆向工程研究，有對 Dalvik VM 做了一系列的研究和說明，並開發了一支 apk 反組譯工具&amp;nbsp;[&lt;a href=&quot;http://code.google.com/p/dex2jar&quot;&gt;dex2jar&lt;/a&gt;]。如其名，該工具能將 DEX（Android apk 的格式）還原成 Java class 檔案，但更有趣的是，反向工程後的結果，不單只是 Binary 或 Bytecode，而是有『相當完整』的&amp;nbsp;Java 原始程式碼。&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;此外，在該 Project 的 Wiki 上，作者用『中文』記載了 [dex2jar] 的設計細節和反向工程所遭遇的問題，並寫了相應的解決手段，對技術有興趣的人可以去看看。 :-P&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;&lt;b&gt;後記&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;br /&gt;
&lt;/b&gt;&lt;/div&gt;&lt;div&gt;如同該 Project 首頁所標註，還是請玩家在把玩這支程式時要『遵循 Google 相關協議與相關法律法規』。&lt;/div&gt;</description><link>https://fred-zone.blogspot.com/2011/04/android-app.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>2</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-1534591961125602974</guid><pubDate>Sun, 17 Apr 2011 11:35:00 +0000</pubDate><atom:updated>2011-04-18T02:09:35.588+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Hacking 心得筆記</category><category domain="http://www.blogger.com/atom/ns#">python</category><category domain="http://www.blogger.com/atom/ns#">自由軟體</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><title>Python之我見</title><description>&lt;div class=&quot;separator&quot; style=&quot;clear: both; text-align: center;&quot;&gt;&lt;a href=&quot;http://www.python.org/images/python-logo.gif&quot; imageanchor=&quot;1&quot; style=&quot;clear: right; float: right; margin-bottom: 1em; margin-left: 1em;&quot;&gt;&lt;img border=&quot;0&quot; src=&quot;http://www.python.org/images/python-logo.gif&quot; /&gt;&lt;/a&gt;&lt;/div&gt;對一個『慣C』人來說，[&lt;a href=&quot;http://www.python.org/&quot;&gt;Python&lt;/a&gt;] 實在是讓人無法去接受的程式語言，無論是效能、速度還是所吃的系統資源，都讓人不滿意。但以一個類 Script 語言來看待它，又是個極度強大又好用的東西，開發時程也比其他傳統語言短相當多，開發完成之後，也有相當大的再擴展和加速的空間。所以，若是開發出來的東西，沒有絕對強烈的『即時』和『精巧』需求，用純 Python 來寫，會相當合理。&lt;br /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;br /&gt;
&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;應用需求導向&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
若您是熟悉 Windows 的開發者，可將 Python 視為 [&lt;a href=&quot;http://zh.wikipedia.org/wiki/Visual_Basic&quot;&gt;Visual Basic&lt;/a&gt;]，是一個著重於『立即應用』的存在（當然在本質上不相同，後者相對簡陋且更著重於【可視化程式設計】）。他們最相似之處就是開發模式，因為長久下來，已經有太多人為 Python 寫出無數的模組、功能，以致開發者只需要懂得引入和使用這些模組，就能達成所有能想到的功能，這點與 Visual Basic 的『控制項』開發模式沒什麼不同。&lt;br /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;在今天，由於太多人的貢獻成果，使 Python 開發者幾乎無所不能，再者，其已經跨足了許多平台。對開發者而言，同一支程式，只要注意引入模組是否能跨平台（大多數功能多半都有解決方案），就能輕易寫出能運行於不同平台上的應用軟體，稱之跨平台的 Visual Basic 可不為過。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;&lt;div&gt;但如同許多前端網頁開發者隨處濫用 JavaScript + HTML，濫用 Python 的人也有不少。由於 Python 過於易用，許多人在開發上只求達成功能，忘了效率的考量（這觸動了『慣C』人最大的忌諱），過去一些比較著名的例子就是： emesene（Linux 上常見的 MSN 軟體） 和 iBus（輸入法系統架構），速度慢或吃光系統資源還不打緊，甚至是造成系統鎖死當機的狀況。&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;br /&gt;
&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;獨特的語言特性&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;說到 Python 語言本身，它有極為結構化的設計，又支援各種先進的開發模式，物件導向等技術當然不會少，以致使用 Python 來開發大型專案是非常可行的。若要舉個實例，大概就是 Google 的各項網路服務。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;Python 擁有獨特的語言規則，像是捨棄『{};』等包裝語法，並強迫對齊等設計。這使得開發者不得不簡化和編排自己的程式，不能再寫出長又難以維護的程式碼。當然，這會讓已經習慣傳統語言規則的開發者，會有困擾和不習慣，需要一些時間適應。不過，一旦接受了 Python 的設計習慣，便可輕易的使用。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;&lt;b&gt;適用範圍&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;很明顯，Python 相當適用於網站程式上，其高階的包裝，開發時程的優勢，甚至功能性，都讓他方便實作任何 Web 服務。由於 Web 並不需要『即時反應』和『長時間待命』的需求，更不怕缺少在壓力下效能考量的不良設計。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;當然，一般功能性導向的應用軟體也是適合使用 Python 來開發，除了開發時程短之外，也能很容易做出想要的各種功能，日後的維護也非常簡單。唯一要注意的是，要時時考慮 Python 的效能問題，不然設計出來的程式，會經不起長時間考驗。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;雖然 Python 語言看似高階，但 Redhat Linux&amp;nbsp;作業系統從早期開始的圖形化安裝程式，就是用 Python 所撰寫，不難看出它也能勝任系統應用程式的工作。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;&lt;b&gt;如同 Virtual Basic（疑？） 的缺點&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;當然，沒有一樣東西是百分之百完美，Python 大體上和 Visual Basic 有一樣的缺點，如記憶體的消耗、執行效能不如原生的程式快速，雖然可以透過各種手段得到改進，但那已經跳脫出純 Python 開發的範疇。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;如同有人嘲諷 Visual Basic 一般，Python 的使用者若沒有真正低階的開發經驗，也會寫出令人哭笑不得的程式，若遭遇效能問題，其瓶頸將很難獲得解決。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;&lt;b&gt;整體評估是優良的&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;對於需要快速開發的專案和人力不夠充裕的團隊，Python 是很好的選擇，一種語言便可從系統層寫到 Web 和雲端，非常值得花點時間學習。但最好要搭配 C 語言的相關訓練，在必要時可當成許多效能和相關問題的解決手段。&lt;/div&gt;&lt;div&gt;&lt;br /&gt;
&lt;/div&gt;&lt;div&gt;如果是程式初學者，也推薦選用 Python&amp;nbsp;踏入程式開發之路。&lt;/div&gt;&lt;/div&gt;</description><link>https://fred-zone.blogspot.com/2011/04/python.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>2</thr:total></item><item><guid isPermaLink="false">tag:blogger.com,1999:blog-4389461234607418203.post-6690904601837045284</guid><pubDate>Mon, 28 Mar 2011 06:09:00 +0000</pubDate><atom:updated>2011-03-28T14:10:22.665+08:00</atom:updated><category domain="http://www.blogger.com/atom/ns#">Chewing</category><category domain="http://www.blogger.com/atom/ns#">Debian</category><category domain="http://www.blogger.com/atom/ns#">ibus</category><category domain="http://www.blogger.com/atom/ns#">Linux</category><category domain="http://www.blogger.com/atom/ns#">patch</category><category domain="http://www.blogger.com/atom/ns#">心得分享、講座</category><category domain="http://www.blogger.com/atom/ns#">系統研發手札</category><category domain="http://www.blogger.com/atom/ns#">自由軟體</category><category domain="http://www.blogger.com/atom/ns#">開放原始碼</category><title>ibus-chewing 單純注音模式 - OSDC.tw 2011 Lightening Talk</title><description>為了注音輸入法，您是否長久以來也仍在使用 SCIM？每次裝好 Linux 的第一件事就是刪除系統預設輸入法，我們彷彿是外星人一般，一點都沒享有人權。感謝 OSDC.tw 2011（活動期間 3/26 ~ 3/27） 提供好吃的食物和舒適的場地，在活動這兩天筆者花了一點功夫做 patch，修掉了 ibus-chewing 單純注音模式的一些 Bugs，現在基本上 ibus-chewing 單純注音模式已經可用，我們可以丟掉 SCIM + 爛注音 Table 了！&lt;br /&gt;
&lt;br /&gt;
以下是 Lightening Talk 的簡報檔：&lt;br /&gt;
&lt;div style=&quot;text-align: center;&quot;&gt;&lt;object height=&quot;344&quot; width=&quot;425&quot;&gt;&lt;param name=&quot;movie&quot; value=&quot;http://www.youtube.com/v/M8cppExxQGQ?hl=zh&amp;fs=1&quot;&gt;&lt;/param&gt;&lt;param name=&quot;allowFullScreen&quot; value=&quot;true&quot;&gt;&lt;/param&gt;&lt;param name=&quot;allowscriptaccess&quot; value=&quot;always&quot;&gt;&lt;/param&gt;&lt;embed src=&quot;http://www.youtube.com/v/M8cppExxQGQ?hl=zh&amp;fs=1&quot; type=&quot;application/x-shockwave-flash&quot; allowscriptaccess=&quot;always&quot; allowfullscreen=&quot;true&quot; width=&quot;425&quot; height=&quot;344&quot;&gt;&lt;/embed&gt;&lt;/object&gt;&lt;/div&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;後記&lt;/b&gt;&lt;br /&gt;
由於簡報格式的關係，暫時無法轉成 PDF 輸入，所以目前使用錄影的方式公開，待轉檔程式開發完成再來釋出 PDF 檔。</description><link>https://fred-zone.blogspot.com/2011/03/ibus-chewing-osdctw-2011-lightening.html</link><author>noreply@blogger.com (Fred Chien)</author><thr:total>1</thr:total></item></channel></rss>