如何將 logdown 文章轉成 Facebook Instant Article

如果你希望 logdown 上的文章讓 Facebook Instant Article(以下簡稱 IA ) 也能吃,目前需要一些手動修改,我把我遇到的問題列舉如下:

貼圖

當圖片拖拉到文章裡之後,如果直接讓 IA 吃,會出現這樣的錯誤:

HTML 元素未包含任何文字: 請避免包含空白的 HTML 元素

(此處錯誤,更新於下方)解決方法很簡單,只要在貼圖連結的前後用<figure></figure>包起來即可

<figure>
![oooo.jpg](http://user-image.logdown.io/ooo.jpg)
</figure>

正確貼圖方式
根據 IA 有關圖片的說明

圖像的指定方式是使用標準的 HTML5 <figure> 元素,該元素用於包裝 <img> 元素及其相關註解。代表圖像的 <figure> 元素必須獨立包含在文章正文內,且不得包含在 <p> 元素內。

不過 logdown 現在被 IA 抓取的時候,會被<p>給包住:

<p><figure><img src="http://user-image.logdown.io/9IcQi1dTl6iq70zAyulA_%E6%94%BE%E7%94%9F.jpg" title=""></figure></p>
HTML 元素未包含任何文字: 請避免包含空白的 HTML 元素

即便我看文章本身的 HTML 長這樣

<figure><img src="http://user-image.logdown.io/9IcQi1dTl6iq70zAyulA_%E6%94%BE%E7%94%9F.jpg" title=""></figure><p></p>

所以現在文章裡的圖都要手動去把前後的<p></p>刪掉才能正常發布

更新

這裡指的更新是說當文章在 logdown 發佈,並且被 IA 抓到之後,如果你做了修改,要怎麼讓 FB 知道並且更新?我們需要加底下這段 code 在 header 中:

<header>
    <!-- The published and last modified time stamps -->
    <time class="op-published" dateTime="2017-4-14T13:00"></time>
    <time class="op-modified" dateTime="2017-10-1T14:49"></time>
</header>

我們要修改的就是 op-modified 這一行,只要更新時間比 IA 那邊紀錄的晚, IA 就會重抓,如果你等不及記得去粉專上面手動更新 RSS feed,很快,在 logdown 更新完,十秒不到 IA 就能抓到了

另外要注意的是,IA 只會抓取 24 小時內的更新,擷取 說明 如下:

如果對現有即時文章的更新已超過 24 小時(依據 op-modified 時間計算),則提取會將其忽略。舉例來說,如果在 10 月 8 日中午 12:00 提取摘要,而摘要中某篇文章的 op-modified 時間為 10 月 7 日上午 11:59,則提取會忽略這篇文章。

對 Array of Hash 做 Sort/Pagination

如果今天很不巧有一些設定不在 DB 而是在 yml 檔裡,不能用 model 操縱,拿出來的只有一個 array of hash,這時候要怎麼做 order/pagination/search/ 呢?

  1. 先做 search 假設你要搜尋的 string 叫做 filter,同時要找 idname 這兩個欄位
    filter = filter.downcase # 先轉小寫
    array_of_hash = array_of_hash.select {|hsh|
    hsh["col_1_id"].include?("#{filter}") ||
    hsh["col_2_name"].downcase.include?("#{filter}") # 通通用小寫比對
    }
    
  2. 再把搜尋結果排序 假設你要排序的欄位 叫做 order
    array_of_hash.map {|hsh| hsh["id"] = hsh["id"].to_i} #先把 id 轉成 integer 才會正確排序
    array_of_hash.sort_by! { |hsh| hsh["#{order}"] }
    
  3. 如果還要 descendent/ascendent 這裏假設傳進來的參數叫 desc,1 代表 desc,0 代表 asc,上面的 order 就要改為
    array_of_hash.sort_by! { |hsh| hsh["#{@order}"] }.reverse! if @desc == 1
    array_of_hash.sort_by! { |hsh| hsh["#{@order}"] } if @desc == 0
    
  4. 再做 pagination
    • 安裝 will_paginate
    • config/initalizers/ 底下新增一個檔案 config/initalizers/will_paginate_extensions.rb 裡面 require 'will_paginate/array' 這樣才可以對 array 做 pagination
    • 對剛才的 array_of_hash 做 pagination(注意,我剛才用的是 sort_by! 有個 ! 所以可以直接用 array_of_hash )
    • 一般 offset 從前端來的是 0,記得要加 1 回去

所以就會是

array_of_hash.paginate(page: offset, per_page: limit)

收工。

參考資料:
http://stackoverflow.com/questions/5483889/in-ruby-how-sort-an-array-of-hashes
http://stackoverflow.com/questions/13187076/paginating-an-array-in-ruby-with-will-paginate
http://stackoverflow.com/questions/2244915/how-do-i-search-within-an-array-of-hashes-by-hash-values-in-ruby

如何在執行 .rb 檔的時候跑 pry

首先你需要 gem install pry
然後在程式碼裡面塞入你最愛的 binding.pry
最後執行的時候加上 -r pry, 也就是 ruby -r pry test.rb
就可以開開心心的 debug 了

Whenever is Set in UTC Timezone

whenever 是一個可以安排排程工作的 gem,告訴系統多久/每天/何時 要執行某些動作,不過其中的每天幾點是按照 UTC 時間去排的,所以需要做點轉換,在 whenever 自己的 github issue 裡就有現成解法:

原本的

every 1.day, :at => '4:30 am' do
  command 'echo hello'
end

改成

every 1.day, :at => Time.zone.parse('4:30 am').utc do
  command 'echo hello'
end

就可以了。

Enter = ^M

今天在 terminal 輸入 ssh key 的時候要認證:
Are you sure you want to continue connecting (yes/no)?
輸入 yesenter 之後 變成
Are you sure you want to continue connecting (yes/no)?yes^M
enter 變成 ^M 了....

查了一下應該是 stty terminal line setting 的問題,只要輸入 stty sane 就可以解決。

Install rmagick Issue

今天試著把 redmine 裝到 Heroku 上,遇到了一個問題,做下記錄:

我在照著 Redmine 官方指示 操作的時候,第一個遇到的問題是沒裝 pg,直接執行 gem install pg -v '0.18.1' 發生了錯誤,需要執行 brew updatebrew install postgresql 才能順利執行 gem install pg -v '0.18.1',接著則是安裝 rmagick 發生錯誤:

Gem::Ext::BuildError: ERROR: Failed to build gem native extension.

    /Users/Mac/.rvm/rubies/ruby-2.1.2/bin/ruby -r ./siteconf20150319-41698-m580g2.rb extconf.rb 
checking for Ruby version >= 1.8.5... yes
checking for gcc... yes
checking for Magick-config... yes
checking for ImageMagick version >= 6.4.9... yes
/usr/local/bin/Magick-config: line 41: pkg-config: command not found
/usr/local/bin/Magick-config: line 47: pkg-config: command not found
/usr/local/bin/Magick-config: line 50: pkg-config: command not found
/usr/local/bin/Magick-config: line 53: pkg-config: command not found
checking for stdint.h... yes
checking for sys/types.h... yes
checking for wand/MagickWand.h... no

Can't install RMagick 2.13.4. Can't find MagickWand.h.
*** extconf.rb failed ***
Could not create Makefile due to some reason, probably lack of necessary
libraries and/or headers.  Check the mkmf.log file for more details.  You may
need configuration options.

Provided configuration options:
    --with-opt-dir
    --without-opt-dir
    --with-opt-include
    --without-opt-include=${opt-dir}/include
    --with-opt-lib
    --without-opt-lib=${opt-dir}/lib
    --with-make-prog
    --without-make-prog
    --srcdir=.
    --curdir
    --ruby=/Users/Mac/.rvm/rubies/ruby-2.1.2/bin/ruby

extconf failed, exit code 1

Gem files will remain installed in /Users/Mac/.rvm/gems/ruby-2.1.2/gems/rmagick-2.13.4 for inspection.
Results logged to /Users/Mac/.rvm/gems/ruby-2.1.2/extensions/x86_64-darwin-13/2.1.0-static/rmagick-2.13.4/gem_make.out
An error occurred while installing rmagick (2.13.4), and Bundler cannot continue.
Make sure that `gem install rmagick -v '2.13.4'` succeeds before bundling.

找了一些解法,只有 這個連結 解決問題 ,依序執行底下步驟即可:

$ brew remove imagemagick
$ brew install imagemagick
Make sure pkg-config is correctly linked:

$ brew uninstall pkg-config
$ brew install pkg-config
$ brew unlink pkg-config && brew link pkg-config

$ gem install rmagick

Refactoring: Ruby Edition, Chapter 1

最近開始讀 Refatoring: Ruby Edition,記錄有趣的部分。

1.1.1

突然一看到 attr_reader 竟然忘記是在幹嘛的...趕快查了下恢復記憶,其實就是設定 getter & setter

1.3

  • case 看起來就很適合提煉出來專門放到一個 method 裡
  • 「提煉沒做好,就會引入 bug」,這個引入實在是太貼切了,程式沒寫好就會變成吳三桂
  • 首先看 method 內部的變量,包括 local variable 和 parameter
  • 這裡開始重構,說要用到測試來檢查,不過還沒看到測試的 code,難道是要自己寫?
  • 「代碼必須能展示自身的用途」,變量的名字則是關鍵
  • 「一邊閱讀代碼,一邊重構,在理解程序的同時將它們嵌入到代碼裡,以防止將來忘記從中領悟到的東西」

1.3.1

  • 觀察 method 所使用的信息來源,一個 method 應該放在它所使用數據所在的對象裡。
  • 這裡的 UML 圖還滿有趣的,可以圖像化剛才切斷了哪裡冗長的程序,分門別類重新擺放了哪些 method
  • Replace Temp with Query

1.3.3

臨時變量看起來滿被作者討厭的,不停地被移除,這邊有個臨時變量 total_amount 的位置在 iteration 裡,所以被移出來的時候連 iteration 都要一起帶入

  def statement
    total_amount, frequent_renter_points = 0, 0
    result = "Rental Record for #{@name}\n"
    @rentals.each do |element|
      frequent_renter_points += element.frequent_renter_points
      result += "\t" + element.movie.title + "\t" + element.charge.to_s + "\n"
      total_amount += element.charge
    end
    # add footer lines
    result += "Amount owned is #{total_amount}\n"
    result += "You earned #{frequent_renter_points} frequent renter points"
    result
  end

移到 private method 裡,反正算完之後只有在 result += "Amount owned is #{total_amount}\n" 出現

  private
  
  def total_charge
    result = 0
    @rentals.each do |element|
      result = element.charge
    end
    result
  end

這個簡化讓我覺得舒服

  def total_charge
    result = 0
    @rentals.each do |element|
      result = element.charge
    end
    result
  end

可以用 inject 這個處理總和的方法再縮成

  def total_charge
    @rentals.inject(0) { |sum, rental| sum + rental.charge }
  end

1.4

看到目前為止的感覺,refactor 很重要的一件事就是判斷 method 調用的變量和 class 的關係,凡是抵觸一律搬走,需要的變量再另外傳進去,總之自己的數據自己操作,不是自己的數據就還給人家去操作

試用期

其實在看這篇文章的當下我想到的標題是「殺雞取卵」,不過今年二月已經用過了,而且講的還是同一間公司:時代華納有線(Time Warner Cable,以下簡稱TWC)

但是沒有想到的是,要價美金 103 元的第四台月費被說成是殺雞取卵簡直就是太小看他們了,因為這還是為期兩年的試用期的價格吶!相信 TWC 財報的我也真是好傻好天真,以為這個價格會依照過去成長的幅度繼續慢慢變貴,殊不知在兩年之後,原本 USD 98 的不含稅價格就會跳到 USD 190!直接突破天際!

原文作者是知名的電信產業分析師,在他貼出這篇文章之後,TWC 的回覆節錄如下:

Thus, he received "a roughly 40 percent discount during the promotional period—which is designed to give customers a chance to sample our services at a discounted price for a certain amount of time, then decide what package of services matches their interests and budget."

是的,TWC 完全站得住腳,而且當時的兩年約也白紙黑字的寫明了:

"After 24 months, regular rates in effect at that time apply,"

這就是壟斷事業的好處,價格隨你喊,等到 Comcast 和 TWC 合併之後,相信喊起價來會更加的肆無忌憚--如果沒有網路的話。

因為有了網路,TWC 的第一段話的最後一句就無法成立:

"...decide what package of services matches their interests and budget."

網路時代是一場爭取「注意力」的大亂鬥,一場不分產業的大亂鬥,身處電視產業的 TWC 要對抗的不是「看哪台」或「選哪個方案」,而是「不看」和「不選」,因為你 TWC 不再是唯一或唯二的選項,而是要被擺在和 Netflix,Amazon 和 Hulu同一個平台競爭的產品,如果你還在想著要維持高利潤,不肯作出投資,那麼 TWC 將會再次印證一句老話:「當巨人倒下時,身體都還是溫的。」

UNBUNDLE

你家第四台一個月要繳多少錢?

這不是詰問法,我是真的不知道現在的第四台月費,因為三年前開始我就不看電視了,但我知道台灣的第四台月費一定沒有美國貴,美國 Time Warner 一個月的有線電視要價平均是 USD 106.03,當然,羊毛出在羊身上,如果用台灣第四台的收費肯定換不來「陰屍路」和其他許多知名的美劇,但美國有線電視這一路上漲的費用已經讓很多用戶「斷線」,也就是core cut,停止訂閱有線電視,轉而投向 Netflix,Amazon Prime,現在連 HBO 都要脫離有線電視的魔掌,獨立推出純線上服務了,而且很巧的,昨天才說 HBO 的這個舉動會加速整個產業的移動,今天 CBS 就宣布要推出純線上訂閱服務,連續提了兩次「純線上」,是因為 HBO 之前已經有了 HBO GO 這個線上觀賞的服務,但卻只提供給有訂閱有線電視的用戶,只能算是附加價值而已,這一次 HBO 和 CBS 要推出的都是完全基於網路觀賞的訂閱服務,CBS 預計的每月收費是 USD 6,比 Netflix 的 USD 8-9 便宜一些,HBO 的服務價格則尚未公佈。

照這些服務的價格推算,一百塊美金的有線電視月費大概只能訂閱10個網路服務,遠比不上動輒上百個頻道的有線電視,這中間的差距在哪?差距就來自于透過「打包」累積的觀眾數量換來的廣告收入,所謂「打包」就是把大大小小的頻道捆綁起來一起出售,要嘛都買,不單賣,用這種方式來告訴廣告主說:「我有多少多少頻道加起來有多少多少的觀眾,你來我這邊打廣告才有人看~」,但是,除了有線電視的訂戶在減少,更多的觀眾在廣告時間看的是手機跟平板,這些廣告的錢就會被拉走一部分,有線電視商為了要賺錢,自然得繼續拉高月費,但這樣做卻只會趕跑更多的用戶,於是陷入一個惡性循環,當然這個循環的速度目前非常非常的慢,因為上述電視產業的金三角(電視 / 廣告 / 觀眾)生態圈累積了這麼多年的實力不是一天兩天會被改變的,但是其中一個改變的關鍵就是這些被「打包」的節目。

CBS 就是很好的一個例子,以前你要看 CBS 的影集,除了訂第四台沒有別的方法,但現在卻可以單獨訂閱 CBS 的影集線上看,也就是所謂的「unbundle」,把被打包販賣的頻道拆開來賣,這種趨勢只要一開始,就很難停下來,也只有可能加快。

為什麼?因為我們根本不需要也看不完這麼多的頻道,愛看影集電影有 Netflix 就夠了,非要看電視不可的大概也只剩新聞和體育這兩大類需要直播的節目,雖然今年 ESPN 就說要把 NBA 免費線上播...

電視圈的改變暗潮洶湧,實在很有趣啊!

沒有廣告的 HBO

也要推出純線上的服務了。

首先幫 Netflix 跌掉的 23.57% 股價默哀十秒。(請不要數出聲來)

接著幫除了紅牛,海尼根,可口可樂...等很會拍形象廣告之外的廠商再默哀十秒。(我都快笑出來)

內容本身的價值 / commercial free 的價值姑且不論,光是在互聯網年代,廣告本身就應該當作產品來經營,而不是繼續抱著「老子有的是錢打廣告你們就乖乖看吧」的心態,因為這個時代的資訊管道真的太多了,無法讓人產生美好情緒的廣告放在電視之外的管道,就是垃圾

廣告必須就是內容。

絕大部分的廠商還沒有這個體認,這種急迫感,但是如果線上版的 HBO 會加速更多訂閱服務(which is commercial freeeee)的產生,這個勢態就會越來越明顯。

回過頭來,這也是我不擔心 Netflix 的原因之一,因為 HBO 進來這個串流市場之後,「把餅做大」的效益一定會大過於「搶走 Netflix 的起司」,想想看,如果能在線上隨時選擇 HBO 的 「Game of Thrones」,一定會讓更多人產生「我還要訂第四台幹嘛?」的想法。

而且有遠見的 Hasting 在幾年前喊出「在 HBO 成為我們之前成為他們」的策略成效已經出來了,「紙牌屋」,「Orange is the New Black」一點都不輸給 「Game of Thrones」,觀眾要做的只有選其所愛,至於那些爛廣告?

洗洗睡吧。