2018/9/4

在 rvm 上使用 ruby 2.5.1 跑 rails 5.2.1 時會發生的警告:already initialized constant FileUtils::VERSION

在 rvm 上使用 ruby 2.5.1 跑 rails 5.2.1 時會發生的警告:already initialized constant FileUtils::VERSION

詳細警告訊息如下:

/Users/etrex/.rvm/rubies/ruby-2.5.1/lib/ruby/2.5.0/fileutils.rb:90: warning: already initialized constant FileUtils::VERSION

/Users/etrex/.rvm/gems/ruby-2.5.1/gems/fileutils-1.1.0/lib/fileutils.rb:92: warning: previous definition of VERSION was here

/Users/etrex/.rvm/rubies/ruby-2.5.1/lib/ruby/2.5.0/fileutils.rb:1188: warning: already initialized constant FileUtils::Entry_::S_IF_DOOR

/Users/etrex/.rvm/gems/ruby-2.5.1/gems/fileutils-1.1.0/lib/fileutils.rb:1267: warning: previous definition of S_IF_DOOR was here

/Users/etrex/.rvm/rubies/ruby-2.5.1/lib/ruby/2.5.0/fileutils.rb:1446: warning: already initialized constant FileUtils::Entry_::DIRECTORY_TERM

/Users/etrex/.rvm/gems/ruby-2.5.1/gems/fileutils-1.1.0/lib/fileutils.rb:1541: warning: previous definition of DIRECTORY_TERM was here

/Users/etrex/.rvm/rubies/ruby-2.5.1/lib/ruby/2.5.0/fileutils.rb:1448: warning: already initialized constant FileUtils::Entry_::SYSCASE

/Users/etrex/.rvm/gems/ruby-2.5.1/gems/fileutils-1.1.0/lib/fileutils.rb:1543: warning: previous definition of SYSCASE was here

/Users/etrex/.rvm/rubies/ruby-2.5.1/lib/ruby/2.5.0/fileutils.rb:1501: warning: already initialized constant FileUtils::OPT_TABLE

/Users/etrex/.rvm/gems/ruby-2.5.1/gems/fileutils-1.1.0/lib/fileutils.rb:1596: warning: previous definition of OPT_TABLE was here

/Users/etrex/.rvm/rubies/ruby-2.5.1/lib/ruby/2.5.0/fileutils.rb:1555: warning: already initialized constant FileUtils::LOW_METHODS

/Users/etrex/.rvm/gems/ruby-2.5.1/gems/fileutils-1.1.0/lib/fileutils.rb:1650: warning: previous definition of LOW_METHODS was here

/Users/etrex/.rvm/rubies/ruby-2.5.1/lib/ruby/2.5.0/fileutils.rb:1562: warning: already initialized constant FileUtils::METHODS

/Users/etrex/.rvm/gems/ruby-2.5.1/gems/fileutils-1.1.0/lib/fileutils.rb:1657: warning: previous definition of METHODS was here

Rails 5.2.1

排除方法:

gem uninstall fileutils

2018/6/13

Ruby Kaigi 2018 會後心得

這是我第一次參加 Ruby Kaigi,Ruby Kaigi 是一個 Ruby 的大型研討會,參加者約 1000 人左右,在日本仙台國際中心舉辦。

我們公司(五倍紅寶石)免費提供了機票、住宿、研討會門票和生活津貼等資源給想要參加 Ruby Kaigi 的員工,目的是避免員工成為邊緣人、促進員工多參與社群,我們的員工福利真的hen好,感恩五倍、讚嘆五倍。

我們搭乘虎航的飛機,從桃園機場前往日本仙台。

抵達仙台後再搭從仙台空港站 to 仙台站的鐵路交通,類似台灣的捷運,但不確定要怎麼稱呼。

出仙台站後發現我們的飯店竟然就直接蓋在車站旁邊大約 50 公尺的距離。

後來發現我們甚至可以從車站內直接走到飯店,不需要走出車站。

抵達飯店後,同事就直接在大廳擺出了一個帥爆的姿勢。

為了之後的 Party 做準備,第一天的晚餐就是燒肉喝到飽。在日本,似乎很盛行喝酒交朋友的文化。

燒肉喝到飽是指飲料(包含酒類)可以無限點(如果你喝得夠快的話,就可以趨近於無限),但是燒肉的部分是類似套餐,是固定的份量。

飲料的 Menu 上可以看見各種酒名以日文表示。因為我比較喜歡喝調酒,所以我的策略就是在調酒的分類按照順序點。點餐時基本上我就是隨便指一個,即使 Google 加上翻譯,還是很難知道自己喝到的是什麼,畢竟調酒名稱本來就都很詭異。

第二天是 Remote 工作日,據說是因為發現早一天到達仙台可以獲得比較便宜的總支出。

第二天的晚上,也就是 Ruby Kaigi 的前一天晚上,有一個由 Speee 主辦的 Preparty。

Speee 是 Ruby Kaigi 的贊助廠商之一,由於 Ruby Kaigi 的 Party 之多,我無法判斷到底哪些 party 才是所謂「官方」所舉辦。

現場看起來大概像這樣:

中間有一整條的食物區,旁邊則是大型方桌提供酒類。

壽司是主食,只可惜我不吃壽司。

旁邊有一個調酒區,可以跟 NPC 點酒,這一罐紫色的酒(Crème de cassis)是我有興趣的酒,所以特地拍了一張。

在這個會場有個舞台,Party 中途有一些活動,其中一個活動是清酒盲測。這一罐看起來很高級的清酒會被倒在A杯或B杯中,而另一個杯子就會是普通清酒。

主持人在現場尋找自願參加者,有興趣的人就可以上去玩。

參加者需要戴上眼罩,隨後工作人員就會開始倒酒,此時只有參加者不知道哪一杯酒是高級清酒,現場所有的旁觀者都看得一清二楚。

參加者喝完之後需要舉牌表示意見,主持人也會詢問他們為何選擇這樣的答案,這是一個還蠻有趣的環節。

這是在遊戲說明時介紹高級清酒的部分。

在這個場合,我們需要多嘗試接觸其他人。這對我來說難度很高,因為即使是台灣人我都不知道該跟對方說什麼,更何況是外國人。

我覺得我就像是一隻被動怪,如果有人來跟我對話的話,我是OK的。但是我不會主動攻擊。

所以我後來就發展出一個策略:跟著一個主動怪同事(以下簡稱主動怪),由他發動攻擊,其他人就圍上去這樣。

主動怪教我們如何發動攻擊:「你眼睛就四處看,有時候會剛好跟某人四目相對,這時候就直接走過去打招呼就行惹。」

我是覺得這就像神奇寶貝遊戲裡的路人訓練師,當你路過他面前時,他就會走過來找你PK。

在 Party 結束回到飯店後,一群人休息後移動到主動怪的房間聽同事試講,因為我們同行的同事包含兩位講者。

第三天,Kaigi 終於開始。

午餐

他們提供了多樣性的便當選擇,但因為我不吃素,不吃壽司,也不吃醃漬類食物,所以我三天都拿同一款便當,也就是這款。

我們帶著便當到附近的公園(似乎是古蹟)野餐,看起來像這樣:

下午茶

下午茶提供各式日本茶點,有銅鑼燒和各種我講不出名字的當地名產還有一些水果拼盤。水果的部分還是台灣的比較多樣化一點。

若要論好吃程度的話,在每種都已經吃過的情況下,如果現場有鳳梨酥,我會選擇吃鳳梨酥。

各種 Party

除了官方辦的 Party 之外,其他 Party 都是免費的,但我認為免費的 Party 都表現得比較好。我想應該是因為免費,所以感覺比較好吧。

所有的 Party 都提供無限量的啤酒跟清酒,我比較喜歡清酒,因為啤酒有氣泡,而我不太能喝含有氣泡的飲料。

贊助商

我在贊助商區發現了一個很有趣的攤位。

使用不同顏色的樂高來區分,可以即時生成對應的室內設計圖。

他們的贊助商區提供了各式各樣的小禮物,藉此吸引你過去他們的攤位。有些則是會要求你在 Twitter 上發文或者要求你做一些事情才會送。

有手提袋、資料夾、貼紙、扇子、徽章、入浴劑、香皂、溜溜球、杯墊、包包掛環、筷子、眼鏡布、pokey、筆記本、詐神筆記本等。

古蹟

趁中午吃完飯去看了一下博物館和仙台城跡,因為博物館不能拍照,所以就給大家看看仙台城跡上有什麼。

議程

Matz 的開場 Keynote

  • 命名很重要

  • 命名會給予概念,而人們使用概念來進行交流。

  • 當你覺得命名很難,表示你還沒正確理解那些概念。

  • 命名專案的時候,可被 Google 很重要

  • 不要使用已經存在的單字,像是 Go、Swift,而是使用兩個單字的組合,像是 TensorFlow,或者是修改單字中的幾個字母,藉此獲得可被 Google 的特性

  • 時間很重要

  • 時間就是錢,時間就是價值,但是人類經常浪費時間

  • 如何增加開發者的產能很重要

  • Ruby 超棒

  • Ruby 有很強大的內建函數

  • Ruby 有很強大的套件和框架

  • Ruby 有友善的社群

  • Ruby 很簡潔

  • Ruby 目前著重於效能和 Concurrency 的優化。

  • 時間就是錢:如果效能變成3倍,就等於可以使用1/3的雲端平台成本支撐目前的使用量

  • Ruby 已死!? Ruby 每年都在死

Hijacking Ruby Syntax in Ruby

  • 做了一些套件可以用來強迫檢查繼承後的 class 有沒有好好寫

  • Finalist:override 同名方法時會 raise

  • Overrider:當父類別沒有同名方法時會 raise

  • Abstriker:當繼承的 class 沒有實作抽象方法時會 raise

  • ImplicitParameter

  • With Resource:確保資源只會在block 內使用,結束後會被安全釋放

  • Deferred:With Resource 有多重資源使用時的波動拳問題,考慮 Go 語言的 defer,所以在 Ruby 也幹一個

  • 很多東西是使用神奇 der TracePoint 來實作

  • 提到 Ruby 的 undef_method:直接跳 NoMethodError 不問父類別

  • 提到 Ruby 的 remove_method:會檢查父類別有沒有同名方法

Fast Numerical Computing and Deep Learning in Ruby with Cumo

  • 他做了一個叫 Cumo 的套件,可以使用 CUDA 在 NVidia 顯示卡上做計算的加速。

  • 介紹了 Ruby 跟機器學習有關的套件

  • DNN : Red-chainer

  • Tensor : Numo/NArray, Cumo

  • CUDA binding : RbCUDA

  • PyCall : binding to python

  • tool : Rubyx

  • CUDA 的記憶體配置很慢,所以 Cumo 有實作 Memory Pool 避免重複配置

  • 目前在顯卡記憶體管理上還未完善,無法有效利用

  • 未來想串接 cuDNN,cuDNN 是 NVidia 的針對 DNN (深度類神經網路)計算加速的另一款 Lib。

Scaling Teams using Tests for Productivity and Education

  • 我們無法要求所有員工都能夠在「事前」記住所有該注意的事項,但「事後」才發現就已經太遲,最佳的教育時機就是在當下

  • 提出 JIT 教育的概念

  • 將 Rubocop 視為一種自動進行的 JIT 教育

上圖說明了這樣的教育方式也能實踐到環境架設上

在其他的場次中沒有獲得值得寫下來的內容,或者大多數內容是聽不懂的。

會場周邊活動

在會場有一個小區域做了 Pair Programming 的直播,主題是使用 Ruby 寫出一個 Game of Life

在這個活動進行的同時,議程也在進行中,所以在這邊看他們 Pair Programming 就無法看議程。

Game of Life 是假設這個世界就像棋盤,而每一個格子裡面可能有生物或沒有生物。

如果一個格子的周圍 8 格有其他生物,稱為鄰居,而鄰居的數量會影響生物出生或者死亡。設定不同的出生和死亡條件會讓結果不同。

本次實作的出生條件:某個空格剛好有 3 個鄰居,就會在某格生成 1 個生物

本次實作的死亡條件:某個生物的鄰居 < 2 或者 > 3,就會死亡

如果不屬於出生或死亡條件,格子的狀態就維持原樣。

看著看著覺得他們實作得很慢,於是就在旁邊也實作了個:https://gist.github.com/etrex/9999836faa433046d44ab5af9e7c01fe

結果自己實作也很慢。

三角棋(Triangular Nim)

在會場寫了 Game of Life 之後,找到了一些小時候寫 Console 小遊戲的手感,於是想要嘗試把小時候沒作完整的三角棋 AI,趁在聽不懂的議程中和回到飯店時。用 Ruby 再實作一次。

這次考慮了強化學習的世界觀,把遊戲分成 Environment 和 Agent,Environment 代表遊戲以及遊戲的主持人,而 Agent 則代表玩家。

目標是實作出強化學習的 AI,希望可以做到像 AlphaGo Zero,不提供人類的知識,而是讓 AI 透過自己跟自己玩,在遊戲經驗中變強。

目前實作了三角棋的 Environment 以及亂選的傻逼 AI 和人類玩家這兩個 Agent。

這是程式碼:https://github.com/etrex/ai/tree/master/triangular_nim

整體心得

在某些議程中也許不能獲得什麼,但是可以引發自己的想法。因為它提供了一些時間和環境,讓你去思考一些平常不會去思考的問題。

我跟主動怪討論了為什麼 Ruby Kaigi 的議程選題都是這麼硬的主題之類的問題,我得到了一個結論。

網路效應不只發生在應用程式或服務上,同時也發生在程式語言上,也就是說強勢語言可以獲得較多的使用者,導致該語言更強勢。

Ruby 的優點是快速建置服務,從 Ruby 圈的人努力的方向來看,目前的主要發展是效能、Concurrency 和機器學習來看,主要的假想敵應該是有 TensorFlow 可以用的 Python。

如果在 Ruby 這邊搞一個 Gem,讓你只需要實作問題以及演算法,就能夠直接變成一個網路服務的形式存在的話,我認為應該會很有搞頭。

2018/1/23

只要有心,人人都可以作卡米狗 - 完賽心得

參加感想

其實一開始參加的時候是想說反正隨時棄坑都沒關係,至少我有開始過。但沒想到讀者比我預想的還要多,情況有點不受控制,我似乎不得不把質跟量都作出來,不然就會辜負這些讀者。不過也感謝大家的支持,我才能順利完賽,在沒有任何文章存稿的情況下參賽,連我都不相信我能完賽。

讀者群的設定

因為卡米狗粉都是沒有接觸資訊領域,不會寫程式的人,所以在我寫文的一開始,就把讀者群設定在電腦只有開過 IE、只安裝過 MMORPG 的等級。要從檔案總管和記事本教起,這件事比我一開始想像中的還要累。在講到任何知識之前,我都得要先想一下,我應該要假設讀者已經學過了嗎?如果我這裡跳過不講,讀者會不會放棄治療,一輩子卡關在這裡呢?還是說我不應該講這麼細節的東西,應該讓讀者用肌肉記憶就好?我是覺得如果我不講,讀者放棄治療的機率很高啦。

對於 iT邦幫忙既有的讀者來說,我設定的讀者群程度可能就太淺了,抱歉占用到你們的版面。不過,我從一開始就不是打算寫給你們(工程師們)看的。

關於選題

只要有心,人人都可以作卡米狗,這個選題已經說明了讀者群的設定就是麻瓜。而聊天機器人說穿了就是個只有後端的網站,製作難度肯定低於架網站,我只需要確保每個讀者都懂 HTTP 協定,並且會架 HTTP Server 即可。主要目標是讓讀者看完之後能夠有基礎的網站概念,開始能看得懂工程師寫的技術文章,以及知道遇到問題時要在 GOOGLE 輸入什麼關鍵字的能力。

關於文章內容的編排

我首篇先講什麼是聊天機器人,並以卡米狗舉例說明,當然也是為了置入一波卡米狗。

在我作任何教學之前,我會希望讀者能夠先知道為什麼他要學這個,所以我選擇採用從上而下的講解方式,先講最大的框架是由什麼構成,接下來再去認識細節和實作的部分,而每一個實作的部分都是遇到才教。我就是怕我一教難的你們就跑了。

如果我今天第一篇開頭就說,我們要用 sublime、ruby、rails、git、heroku 哦~先安裝吧,然後前面10篇都在安裝,這樣的編排真的有人讀得下去嗎?我很懷疑。我認為要讓讀者能夠在初期就取得巨大的成就感,讀者才會有信心能夠跟著文章走下去。所以我在第三篇就讓讀者建立一個 Line chatbot 帳號,而且可以講一些廢話。後面花了20篇的篇幅在教怎麼作出跟 Line@ 提供的後台一模一樣的東西。

不過這樣的篇排有個缺點,就是不能跳著讀。

目錄

大致的切分如下:

基本觀念的建立

從聊天機器人帶到 Webhook,再帶到 HTTP 協定以及 Web Server。
第一天:認識聊天機器人
第二天:認識卡米狗
第三天:作一隻最簡單的 Line 聊天機器人
第四天:認識 Webhook
第五天:認識 Line Messaging API Webhook
第六天:認識網站
第七天:認識網頁伺服器

開發環境的建立

從 Web Server 帶到 Rails,再帶到 Command Line、Sublime Text
第八天:安裝 Rails 和認識小黑框
第九天:作一個最簡單的 Rails 網站
第十天:認識文字編碼
第十一天:認識文字編輯器

HTTP 協定的深入了解

從各個角度了解 HTTP,從瀏覽器發送和接收、也從網站伺服器發送和接收
第十二天:從瀏覽器認識 HTTP 協定
第十三天:認識 Ruby 的資料型態
第十四天:最基本的 Rails 運作流程
第十五天:從 Rails 認識 HTTP 協定
第十六天:做一個最簡單的爬蟲

發布環境的建立

介紹發布環境,帶到 Heroku 和 Git
第十七天:怎麼讓別人連到我作好的網站?
第十八天:發布網站到 Heroku
第十九天:發布網站到 Heroku (續)

LINE API 的串接

基礎知識備齊,終於來到正題。讀者設定為一般工程師的話,第一篇大概會從這邊開始寫起。
第二十天:串接 Line Messaging API Webhook
第二十一天:讓 Line Bot 回覆訊息
第二十二天:用 Line Messaging API 實作關鍵字回覆

資料庫的操作

缺乏的一塊基礎知識,因為得在這個階段才能感受到為什麼需要資料庫,所以選擇在這個時候才講。寫給工程師看的話,這兩篇大概就略過了。
第二十三天:認識資料庫
第二十四天:認識資料庫(續)

學習成果的應用

這是大家想看的部分
第二十五天:卡米狗學說話
第二十六天:卡米狗推齊
第二十七天:卡米狗見人說人話,見鬼說鬼話
第二十八天:建立管理後台
第二十九天:卡米狗發公告
第三十天:卡米狗查天氣

關於開發環境

選擇在 windows 上開發 rails,而不是選在 macbook 上開發,是因為我認為大多數一般人家裡沒有 macbook,為了降低進入障礙,所以選擇在 windows 上開發,我的卡米狗從一開始就是在 macbook 上開發的,而在我寫文之前,我沒有用過 windows 開發過 rails。選擇用 windows 開發,在後期確實是導致比較多的障礙。不過讀者們會因為這樣而去安裝 linux 或者買一台 macbook 嗎?

關於瀏覽量

老實講,最前面的三篇文章我有在卡米狗上面發公告宣傳,成效不錯。但每次發公告,好友人數就掉1%是蠻傷的,應該要作個訂閱機制,針對那些有在 LINE 上訂閱系列文的人,我再每天 PUSH 就好。不過文章寫到一半也沒那個心力去加功能就是了。不過後面有兩篇莫名4千多,我是懷疑有別人在洗我的瀏覽量。

最後

在這裡感謝那些留言給我的人,不論你們是提出問題,或回報錯誤,或感謝我,你們都能幫助到我。之後可能會把在這三十篇裡面沒提到的,關於 Line Messaging API 部分也講一講,像是 imagemap messagetemplate message 這種比較酷炫的功能。

以下開放許願,我考慮有時間的時候再回來講講。

2018/1/18

第三十天:卡米狗查天氣

今天就是最後一天惹,有些事情想跟你們講一下,那就是我們前幾天到底在幹嘛。

以下是一些示意圖,說明我們的 HTTP request 傳遞的路徑。

回覆訊息

Line app 指的是手機或PC版的 Line,Line server 在收到訊息後會透過 webhook url 傳遞給我們。接著我們會打 line.reply_message 傳訊息給 Line server,最後再由 Line server 傳給 Line app (最後這段可能不是 HTTP request)。

發公告

我們透過後台管理介面填入公告訊息,用 line.push_message 傳訊息給 Line server。

排程公告

有觀眾說想知道鬧鐘怎麼作,這裡再說明一下。

我們會用到 worker 來處理工作排程。首先是先在後台設定預約發訊息,然後將訊息儲存到工作清單,每個工作可以指定執行時間,接著就等時間到,worker 就會用 line.push_message 去打 Line server。

查天氣

查天氣就更複雜了,我們收到查天氣指令後,要先去氣象局取得圖片檔,然後再把圖上傳到 imgur,最後把圖片連結傳回給 Line server。

為什麼不是直接把氣象局的圖片傳給 Line server 呢?因為 Line server 要求圖檔必須是 https 開頭的網址,但是氣象局的圖檔連結卻是 http 開頭。

那為什麼不是我們自己保存圖片就好呢?因為存圖片要占空間跟頻寬,所以我選擇用 imgur 的空間放圖。imgur 有一個蠻好的地方是,你可以直接把圖片網址給他,他就會幫你備份圖片了,所以我們不用真的把圖檔抓回來再上傳到 imgur。

查天氣的運作流程

我們作簡單一點,當有人說到天氣的時候就傳回一張雷達回波圖。我們需要作的所有事情是:

調查階段:

  • 學會怎麼抓到最新的雷達回波圖網址
  • 學會怎麼把圖檔弄到 imgur

實作階段:

  • 在主程式呼叫查天氣
  • 增加一個查天氣函數
  • 增加一個取得最新雷達回波圖的函數
  • 增加一個上傳圖片到 imgur 的函數
  • 傳送圖片到 line 的函數

一步步來吧。

學會怎麼抓到最新的雷達回波圖網址

當然,如果我們是用瀏覽器下載,那麼很簡單直接網頁打開右鍵->另存圖片就載好了。可是我們是要用程式去載圖,不是人工載圖。

所以我們要用程式去開啟網頁,然後從網頁原始碼裡面找到圖片連結就行了。

先開這個網頁:http://www.cwb.gov.tw/V7/observe/radar/

然後按下 Ctrl+U,就可以看到網頁原始碼了,把他認真的讀完之後會發現第 234~237 行很可疑,點進去看就會發現全都是圖檔連結,像這樣:http://www.cwb.gov.tw/V7/js/HDRadar1000n_val.js

要能發現第 234~237 行很可疑,你必須要能看懂大部分的 html 跟 js,所以你得學會 html 跟 js。

如果你還沒學過 html 的話,可以參考看看:深入淺出立即上手的 HTML 網頁設計

如果你還沒學過 js 的話,也可以參考看看:JavaScript & jQuery 前端開發入門實戰

var HDRadar_1000_n_val=new Array(
new Array("2018/01/18 01:20","/V7/observe/radar/Data/HD_Radar/CV1_1000_201801180120.png"),
new Array("2018/01/18 01:10","/V7/observe/radar/Data/HD_Radar/CV1_1000_201801180110.png"),
new Array("2018/01/18 01:00","/V7/observe/radar/Data/HD_Radar/CV1_1000_201801180100.png"),
new Array("2018/01/18 00:50","/V7/observe/radar/Data/HD_Radar/CV1_1000_201801180050.png"),
...

這是 js 程式碼,我們需要的部分在第二行後半段:/V7/observe/radar/Data/HD_Radar/CV1_1000_201801180120.png,這是網頁路徑,省略了網域的寫法。

把網域加回去就會是 http://www.cwb.gov.tw/V7/observe/radar/Data/HDRadar/CV11000_201801180120.png

這就是我們要的圖片連結。

小結

http://www.cwb.gov.tw/V7/js/HDRadar1000n_val.js 的原始碼,然後取出第二行的網頁路徑,最後在前面補上 http://www.cwb.gov.tw 就會是我們要的網址。

學會怎麼把圖檔弄到 imgur

imgur 有提供 api,這是說明文件:https://apidocs.imgur.com/#4b8da0b3-3e73-13f0-d60b-2ff715e8394f

使用 api 需要 Client-ID,這東西就跟 Line channel secret 那些東西差不多。

你可以透過這個網址:https://api.imgur.com/oauth2/addclient 取得你的 Client-ID。

照著填就可以。

小結

透過使用 imgur 提供的 api,我們可以很容易就上傳圖片到 imgur。

接下來是實作階段的部分。

在主程式呼叫查天氣

  def webhook
    # 查天氣
    reply_image = get_weather(received_text)

    # 有查到的話 後面的事情就不作了
    unless reply_image.nil?
      # 傳送訊息到 line
      response = reply_image_to_line(reply_image)

      # 回應 200
      head :ok

      return 
    end

    # 紀錄頻道
    Channel.find_or_create_by(channel_id: channel_id)

    # 學說話
    reply_text = learn(channel_id, received_text)

    # 關鍵字回覆
    reply_text = keyword_reply(channel_id, received_text) if reply_text.nil?

    # 推齊
    reply_text = echo2(channel_id, received_text) if reply_text.nil?

    # 記錄對話
    save_to_received(channel_id, received_text)
    save_to_reply(channel_id, reply_text)

    # 傳送訊息到 line
    response = reply_to_line(reply_text)

    # 回應 200
    head :ok
  end

我在最前面加入了這段程式碼:

# 查天氣
reply_image = get_weather(received_text)

# 有查到的話 後面的事情就不作了
unless reply_image.nil?
  # 傳送訊息到 line
  response = reply_image_to_line(reply_image)

  # 回應 200
  head :ok

  return 
end

我們要作一個查天氣函數 get_weather 如果輸入的文字包含天氣,就傳回 https 的雷達回波圖網址,然後就將圖片傳回給 line,這裡因為之前都是傳文字而已,所以還要多作一個函數 reply_image_to_line 來傳圖片。

增加一個查天氣函數

  def get_weather(received_text)
    return nil unless received_text.include? '天氣'
    imgur(get_weather_from_cwb)
  end

第一行是說如果輸入的文字不包含天氣,就傳回 nil。

第二行呼叫了兩個函數,第一個函數是 get_weather_from_cwb,這是取得雷達回波圖的函數,會得到一個網址,再把這個網址傳給 upload_to_imgur 這個上傳圖片到 imgur 的函數。

增加一個取得最新雷達回波圖的函數

第十六天:做一個最簡單的爬蟲學到的在 rails 發 HTTP request 跟在第二十五天:卡米狗學說話學到的字串處理又要派上用場了,就跟你說前面的文章都是在打基礎吧,漏掉一篇你就做不出來了。

  def get_weather_from_cwb
    uri = URI('http://www.cwb.gov.tw/V7/js/HDRadar_1000_n_val.js')
    response = Net::HTTP.get(uri)
    start_index = response.index('","') + 3
    end_index = response.index('"),') - 1
    "http://www.cwb.gov.tw" + response[start_index..end_index]
  end

前兩行就是第十六天講過的,後三行就是第二十五天講過的。比較難懂的可能會是第三行跟第四行,先看一下這張圖:

總而言之就是網址的開頭前面是 "," 後面是 "), 如果你有學過 js 應該就會知道,這個開頭跟結尾應該是不會錯的,所以我們決定取出介於這中間的字。

這行是在抓起點:

start_index = response.index('","') + 3

這是在抓終點:

end_index = response.index('"),') - 1

增加一個上傳圖片到 imgur 的函數

  def upload_to_imgur(image_url)
    url = URI("https://api.imgur.com/3/image")
    http = Net::HTTP.new(url.host, url.port)
    http.use_ssl = true
    request = Net::HTTP::Post.new(url)
    request["authorization"] = 'Client-ID be2d83405627ab8'

    request.set_form_data({"image" => image_url})
    response = http.request(request)
    json = JSON.parse(response.read_body)
    begin
      json['data']['link'].gsub("http:","https:")
    rescue
      nil
    end
  end

我們設定好 request header 和 request body 之後打一個 post request 出去,他會返回一個 json,接著我作了 json 的解析,並且在解析失敗時傳回 nil,確保程式不會隨意掛點。

request["authorization"] = 'Client-ID be2d83405627ab8'

這行是要填入你自己的 Client-ID,be2d83405627ab8 是我亂打的。

傳送圖片到 line 的函數

  # 傳送圖片到 line
  def reply_image_to_line(reply_image)
    return nil if reply_image.nil?

    # 取得 reply token
    reply_token = params['events'][0]['replyToken']

    # 設定回覆訊息
    message = {
      type: "image",
      originalContentUrl: reply_image,
      previewImageUrl: reply_image
    }

    # 傳送訊息
    line.reply_message(reply_token, message)
  end

其實跟傳文字幾乎一樣,只差在 message 裡面不一樣而已。

上傳實測

成功!

本日重點

如果你原本是完全不會寫程式,你從第一篇一直看到這篇,最後有作出東西的話,請在底下留言:「感恩卡米,讚嘆卡米」,讓我能證明只要有心,人人都可以作卡米狗是真的。

2018/1/17

Rails - Windows 上會遇到的 LoadError (cannot load such file -- bcrypt_ext) 問題

什麼時候會遇到這個問題?

當你使用任何需要加密功能的套件時,比方說 Devise。

成因

安裝了不能在 windows 下正常執行的 bcrypt 套件。

解法

先解除安裝所有 bcrypt

gem uninstall bcrypt-ruby
gem uninstall bcrypt

再安裝正確版本

gem install bcrypt --platform=ruby

你的 Gemfile 應該加入這行

gem 'bcrypt', '~> 3.1.11'

參考連結

https://github.com/codahale/bcrypt-ruby/issues/142#issuecomment-291345799