2017-11-12

convert image to Arduino OLED ( Adafruit GFX Library & black white )

大概就是在做奇怪的東西哈哈,首先 imagemagick 轉圖很好用,很多詭異的格式可以轉,其中一個是 xbm (X BitMap),其實就是 xwindow 內跑 C 語言然後載到記憶體內的鬼,格式大概長這樣

#define filename_width 24
#define filename_height 24
static char filename_bits[] = {
  0xFF, 0x3F, 0xFF, 0xFF, 0xDF, 0xFE, 0xFF, 0xEF, 0xFD, 0xFF, 0xF7, 0xFB,
  0xFF, 0xF7, 0xFB, 0xFF, 0xFB, 0xFD, 0xFF, 0xFD, 0xFE, 0xFF, 0xFE, 0xFE,
  0xFF, 0xFD, 0xFD, 0xFF, 0xF3, 0xFD, 0xFF, 0xFB, 0xFE, 0xFF, 0x7B, 0xFF,
  0xFF, 0xBD, 0xFF, 0xFF, 0xDE, 0xFF, 0x7F, 0xDF, 0xFF, 0xFF, 0xBE, 0xFF,
  0xFF, 0xDE, 0xFF, 0xFF, 0xEE, 0xFF, 0x7F, 0xF7, 0xFF, 0x7F, 0xFB, 0xFF,
  0xBF, 0xFD, 0xFF, 0xBF, 0xFE, 0xFF, 0x5F, 0xFF, 0xFF, 0x9F, 0xFF, 0xFF,
  };

因為是記憶體直接對照,所以就是 memory buffer 的格式了,其中的 filename 處為檔案名稱,會直接換成變數名稱,但如果直接出 stdout 所以該處會變成 -

這 code 其實 Arduino 中的 Adafruit GFX Library 可以直接吃了,不過需要加工一下

其中 char 要轉成 uint8_t 型態,而黑白顛倒(對 OLED 而言亮的是黑色),所以要 xor 255 也就是把整個 binary 整個反轉過,而 char 轉 uint8_t 時每個 set 都是顛倒的,要換回去才行

完成的程式長這樣,有需要的人可以拿去用哩

# [[Ruby code]]
# need imagemagick , convert all images(gif) & merge to output.txt

target = File.open('output.txt' , 'w')

filenames = Dir["*.gif"]
filenames.each do |filename|
  var_name = filename.split(/\./)[0..-2].join('_')
  content = `convert #{__dir__}/#{filename} xbm:-`

  #re-define var name & change type to uint8_t
  content.gsub!(/define \-/ , "define #{var_name}")
  content.gsub!(/static char -_bits/ , "static uint8_t #{var_name}_bits")

  #inverse color && swap bitmap buffer
  content.gsub!(/0x[\dA-F]{2}/) do |source|
    source = (source.to_i(16).to_s(2).rjust(8 , '0').reverse.to_i(2) ^ 255).to_s(16).upcase
    "0x#{"0" if source.length < 2}#{source}"
  end
  target.puts(content)
end

target.close

puts "finished , total #{filenames.length} files"

之後把 output.txt 的內容複製貼上到 IDE 內就可以直接使用了呦,超方便的哈哈

2017-06-13

童話鎮的勇者

很久很久以前,有個童話鎮,然而距離童話鎮幾十公里外有個魔王城

童話鎮長期以來被魔王城跑出來的魔物侵擾,破壞莊稼,調戲民女,但缺乏領導人物,村民也不想抵抗,想辦法討好魔物們,雖然有損失但彼此相安無事

有一天童話鎮來了一個外人,穿著非常破爛,看似經歷過非常多的戰鬥,身上充滿著殺意與鬥氣,來到鎮上後看到鎮上的狀況,把所有的魔物都趕跑了,並佇立在鎮門口,魔物看到都嚇跑了

鎮長知道這一切後就邀請那個外人,住進了鎮內,去除破爛的外表,穿著光鮮亮麗有威嚴的服裝,並稱他為勇者,而鎮內因為沒有魔物的侵擾,從童話鎮變成了童話市,最後發展變成了童話城,鎮長也變成了城主

童話城其實不時的還是有魔物的侵擾,所以城主命令勇者建立了護衛軍,希望勇者把技術都傳給手下們,然而城主也希望勇者能褪去殺意與鬥氣,和善的對待村民,城主也希望能有定期報告,得知近況

勇者盡量的符合城主的期待,護衛軍雖然人數少,但盡忠職守不眠不休地防止侵擾,護衛軍內人人都想變成勇者,完成相同偉大的任務,勇者抑制了自己的殺意和鬥氣,學會如何談笑風聲,變得健談,定期彙報狀況給城主,安定了好長一段時間

但勇者漸漸感覺自己已經不再是當初的那個自己,沒有了鬥氣和殺意,長期過著和平的日子,好像少了些什麼 ......

城內過慣了和平,當民眾遠遠的看到了魔物,責怪護衛軍為何不去處理,護衛軍去追魔物時,魔物一溜煙就跑了,平常的訓練常常就這樣空缺了荒廢了,民眾看到多次魔物後,回報給城主,城主問勇者說:『護衛軍不是你在帶的嗎?為何有這種事情發生呢?』

勇者為了滿足城主的需求與旁邊大臣的建議,建立了多個哨點,把所有護衛軍都派到哨點去,連勇者都去哨點了,而城內一個護衛軍也沒有,當飛行系的魔物,如獨眼蝙蝠、獨角翼龍等等的魔物飛到城內作亂時,勇者與護衛軍會接到飛鴿傳書,指示要回城內平亂,但哨點與童話城相隔半天的路程,所以來回一次一天就什麼事情都沒做到了,更別說疏於平常的練習等等

有一天,前線的哨兵帶來一個非常不好的消息,魔王城的等級提升了

城主知道後找來勇者問說該怎麼辦,勇者說:『我們可以把護衛軍全部從哨點叫回來,組成勇者騎士團,去攻打魔王城』,城主說不行,因為週邊還是要顧,勇者說:『那我自己去吧,不過我需要練習和準備的時間』,城主勉強的點頭同意

勇者在找回過去的自我的期間,升級過的魔王城果然不是蓋的,一次就是十多隻的飛行魔物,護衛軍因為都還很遠在當哨兵,所以城主直接找人去勇者家中找勇者來,勇者大劍一輝,沒事了,回家繼續修煉,民眾看到了很崇拜勇者,但卻也養成習慣,看到大隻一點的鳥就會跑到勇者家嚷嚷,勇者到現場定眼一看,品種不對,還要放下剛剛在家的殺意和鬥氣,拿出過往的笑容對民眾解釋該品種不是魔物,不會有任何干擾民眾的行為

城很大,民眾很多,所以勇者多少次忘記自己是正在醞釀屠城的氣魄,反而都在服務著民眾,逐漸地喪失自我 ... 護衛軍每一個人也都想成為另一個勇者,但卻也都在哨塔站哨 ... 不時的還會被怪罪:為何你不會打魔物和攻打魔王城?

有一天,勇者出發了,前往魔王城,出發前要去拿武器,發覺兵器庫沒兵器了,原來是長久以來城內維運用光了,勇者心裡想:『罷了,這次前去,完成這一切,武器應該都用不到了』,一路走經過哨點,哨兵給予著勇者莫大的祝福,勇者信心滿滿,當攻破了第三個魔物的要塞後,準備觸碰到魔王城門,勇者回頭 ... 回到了城內 ......

原來是剛剛勇者收到了飛鴿傳書,上面用紅色的紙,上面寫著三個"急"字,勇者怕出事所以回城一趟,結果是旁邊的大臣說想知道最近的抗魔物花費多少,想知道最近的資料,不管怎樣都要第一個回報,因為要抓到最近的量來做準確評估,另外一方面魔王城再遠一點的地方還有另外兩個魔王城,請『順便打一打』,對你來說很容易的不是嗎?城主聽了很有道理,點頭同意

勇者知道後又花費了一段時間,將裝備和心態升級,重新打了第一個魔王城的三個魔物的要塞,摸到魔王城門時又收到飛鴿傳書,又是紅色的紙,又是三個"急"字,勇者擔心又回頭了,結果是旁邊的大臣提議要增加錄影機來增加城內的安全,防止財庫被偷,要勇者同意才能進行,城主聽了很有道理,點頭同意,並質疑勇者如果沒有辦法攻下魔王城時該怎麼辦的問題

此時,勇者站在一個人生的十字路口,分歧世界分歧劇情

A:勇者選擇了離開這一切,離開童話城,離開前城主說:『你忍心拋下這一切嗎?當初為了你建立這整套制度,如果你離開我會解散護衛軍!!!』,勇者最終離開了,童話城內聚力不足,崩解四散

B:勇者裝傻,拋棄掉殺意與鬥氣,單純的服務民眾,結果幾年後三個魔王城合併成一個超大的魔王國,以武力把童話城納為己有

C1:勇者就此消失匿跡,來到鄉間山上的一間房,不理會任何飛鴿傳書與凡間的侵擾,拋下所有過去,但尚未沉著心就出發了,只消滅兩個魔王城,回到城內,獲得城主和大臣的賞識,不過一旁的大臣小聲碎碎念:『不是說好消滅三個嗎?為何只有兩個?』

C2:勇者就此消失匿跡,來到鄉間山上的一間房,不理會任何飛鴿傳書與凡間的侵擾,拋下所有過去,修得最終無劍的技能,一舉成功消滅三個魔王城,回到城內,獲得城主和大臣的賞識,但城主和大臣心中感覺:『這不是應該的嗎?這就是勇者該做的事情啊,花了那麼多時間不是理所當然的嗎?』

D:勇者就此消失匿跡,另外一個外人來到童話城內,在勇者修煉期間滅了三個魔王城,勇者發覺沒城可滅,遠走他鄉

==========

我不是勇者,但我正做著勇者的事情,沒人可以幫助到勇者,而人人卻都可以是勇者 ... 或是幫助勇者的人 ...... 如果你懂 MIS 和 RD 的分別,請你拆開而非混用,不然再強的勇者都會離開 ...

然後,順便聽個音樂唄:

https://www.youtube.com/watch?v=ijRH_84lIQQ

https://www.youtube.com/watch?v=GlFIfw7kadY

2017-02-10

phantomjs , web crawler 的最終解

大概就是另外一群團隊需要,一直在找尋 web crawler 的最佳解,尤其面對未來 react / angular / vue 之類的 AJAX 的 web content 時,傳統人工光是分析 AJAX 行為模式就花費太多時間,更別說還要處理別的事情之類的

最完美的方式莫過於類似 wkhtmltopdf / wkhtmltoimage,開一個 webkit 的 browser 將網頁完整下載,並執行 CSS / JS / AJAX 後所剩下的 dom tree 轉為 source code,該 HTML 就是真正顯示到使用者螢幕上的資訊,也才是最終想要的成果

之後才看到 phantomjs,稍微練了一下,效果不錯,缺點後述

/*
  指令使用
  ./phantomjs --web-security=false --ignore-ssl-errors=true --load-images=false hello.js
  你可以增加 --debug=true --max-disk-cache-size=1000 來做 debug
*/

console.log('[[initialize]]');

//lib,等待 ajax call,判定 dom 的產生時機
function waitFor(testFx, onReady, timeOutMillis){
  var maxtimeOutMillis = timeOutMillis ? timeOutMillis : 10000, //最長 timeout 時間
    start = new Date().getTime(),
    condition = false,
    interval = setInterval(function(){
      if((new Date().getTime() - start < maxtimeOutMillis) && !condition){
        condition = testFx();
      }else if(!condition){
        console.log("...下載太久timeout或判定來源失誤");
        phantom.exit(1);
      }else{
        //console.log("'waitFor()' finished in " + (new Date().getTime() - start) + "ms.");
        onReady(condition);
        clearInterval(interval);
      }
    }, 250); //0.25一次巡迴
};

//這個來源其實可以從別頁來才是
var source = [
  'http://24h.pchome.com.tw/store/DBAC9R','http://24h.pchome.com.tw/store/DBAC9S',
  'http://24h.pchome.com.tw/store/DBAC9T','http://24h.pchome.com.tw/store/DBAC9U',
  'http://24h.pchome.com.tw/store/DBACA0','http://24h.pchome.com.tw/store/DBACA1',
  'http://24h.pchome.com.tw/store/DBACA2','http://24h.pchome.com.tw/store/DBACG7',
  'http://24h.pchome.com.tw/store/DBACEA','http://24h.pchome.com.tw/store/DBACFP'
];

var page = require('webpage').create();
var start_at = (new Date()).getTime() / 1000;

console.log(start_at);

var process_me = function(url){
  console.log(url);
  page.open(url , function(status){
    //下面這行顯示整頁的HTML
    //console.log(page.content);
    if(status !== "success"){
      console.log("斷線了??或是該網頁連線錯誤");
    }else{
      waitFor(function(){
        return page.evaluate(function(){
          var temp = []
          jQuery('#ProdGridContainer dd').each(function(index,dom){var t = jQuery(dom) ; temp.push([t.find('.prod_img img').attr('alt') , t.find('.price .value').html()])});
          return temp.length > 0 ? temp : false;
        });
      },function(ans){
        for(var i = 0 ; i < ans.length ; i++){
          console.log(ans[i][0] + " : " + ans[i][1]);
        }
        console.log('總數 : ' + ans.length + ', cost(sec) : ' + ((new Date()).getTime() / 1000 - start_at))
        
        start_at = (new Date()).getTime() / 1000;
        
        if(source.length == 0){
          console.log('結束惹')'
          phantom.exit();
        }else{
          process_me(source.pop());
        }
      });
    }
  });
}

process_me(source.pop());


裡面有執行方式,phantomjs 本身包含了 webkit 瀏覽器,所以要去官方網站下載各平台的執行檔才行,好處是相依性都包在裡面了不用另外處理,其餘請看註解

phantomjs 因為是 JS 控制瀏覽器,所以外面大部分的 code 都是針對 webkit 瀏覽器的操作,而 page.evaluate 這個 function 會在 webkit 的網頁內執行,很像是開了 chrome dev tool 然後把 code 貼上的感覺就是,所以腦袋裡面要分開操作瀏覽器的 JS 和在網頁裡面執行的 JS 兩個角色,而對方網頁有載入 jQuery,當然在 page.evaluate 內就可以用 jQuery,但其他地方無法就是(也不需要),然而記得從裡面傳出資訊到外面的 JS 時,一定只能用基本型別(上面的 return temp),否則 phantomjs 會把 prototype 之類的打光光包光光,會非常非常緩慢且肥大,這邊要注意就是

裡面建了一個 waitFor 的 listener,功用是等待 AJAX 後 dom 的生成,所以輸入兩個值,第一個 func 會 return temp || false,當不為 false 時則判定 dom 生成,該 temp 會傳到第二個 func 變成 ans,也就是取得後要做啥事情的意思而已,概念很簡單就是

所以以上,完美的機器人最終解法整套超漂亮的,然而缺點是所有的 JS / CSS 應該都會被下載回來,所以和傳統爬蟲會多 x N 隻的 JS 下載,和執行的等待時間,這花費的基數其實有點高,所以...看似完美解,但其實能不用就不用唄

事實上還有高級做法,類似把對方網址所有的需求先過一層 proxy,把對方的 JS / CSS 先 cache 起來,則可以大幅減少下載和等待的時間才是,不過這邊等待 dom 生成的時間還是省不了,所以保守估計還是會慢一般傳統做法的 20 倍左右唄

okay anyway 這邊大概就這樣而已,也當作自己的 memo 就是了 ...... 然而實際上不用 phantomjs 的話,還有非常多種"邪惡"的爬法,不過我怕我公開後被抓去關,就先略過唄 X"D

2016-09-12

GnuPG 簽署

大概就是最近開始實作加密系統,有一個特殊需求,類似 HSM ( Hardware security module ) 中的多把 key 的實作,舉例來說 master key 總共有 3 把,每一把都能解密,而線上機用其中 1 把,而另外 2 把備存的方式

看了很多演算法,類似 AES ( 對稱加密 ) / RSA ( 非對稱加密 ) 都無法滿足實作需求,最後轉到了 OpenPGP 身上,而 Linux 已經有實作叫做 GnuPG,或稱為 GPG(PGP vs GPG ?? 這一定是某種玩笑 X"D), GPG 本身是非對稱式加密

以下貼一些之前整理的使用方式

  #JokerCatz = key_name
  #產生公+私金鑰
gpg --expert --gen-key
  #匯出公私鑰
gpg --armor --export JokerCatz > public.asc
gpg --armor --export-secret-keys JokerCatz > secret.asc
  #匯入公私鑰
gpg --import secret.asc
  #編輯#裡面很囉唆
gpg --edit-key JokerCatz
  #公私鑰列表
gpg --list-keys
gpg --list-secret-key
  #移除公私鑰(自產金鑰 = 公 + 私,要移除的話要分別移除)
gpg --delete-key JokerCatz
gpg --delete-secret-key JokerCatz
  #加密
  #--trust-model : 信任非第三方公正者發的key,自產大概都要加這個
  #--yes : 同意覆蓋檔案
  #多個 --recipient KEYNAME 則用多把 public key 來 sign,且任何一把 private 都能解)
gpg --trust-model always --yes --encrypt --recipient MEOWs --output temp.txt.gpg temp.txt
  #解密,使用private key(不用指定key)
gpg --decrypt --output temp.txt temp.txt.gpg

實際就大概這樣用而已唄,而不像 AES / RSA 本身是 key string or key file,GPG弄了之後很類似 global key pool 所以使用時都直接指定名稱即可,而 key 轉移到其他主機,則用匯入匯出即可

再來,其實 gpg 支援 pipeline,所以可以用在備份即時壓縮上,類似這邊所說的

https://lists.gnupg.org/pipermail/gnupg-users/2008-December/035168.html

my-pipeline-that-streams-data | gpg -o output-file.gpg --encrypt

嗯,超帥氣,這樣連中介檔案都不用留的 所以 mysql 備份也可以這樣來玩,類似
mysqldump --opt --all-databases | gpg --trust-model always --yes --encrypt --recipient JokerCatz --output mysqldump.sql.gpg
而其實 GPG 在簽章的同時應該有同時使用 gz 壓縮就是,所以不用二次壓縮,而這邊和 HSM 相比只差沒辦法把 private key 丟在破壞即失效的硬體內唄,以上

2016-08-29

Mi Note (Android 6.x) Root from OSX

簡單解釋,首先安裝 ADB 工具,內容包括 adb 指令和 fastboot 指令
類似用 brew 安裝

http://stackoverflow.com/questions/31374085/installing-adb-on-mac-os-x

或是用指令安裝

http://wccftech.com/set-android-adb-fastboot-mac-os/

再來把手機切到 fastboot 模式,然後

$ fastboot devices

會列出連結的手機

之後就可以使用這篇的教學

https://xiaomi.eu/community/threads/how-to-root-the-mi-note-virgo-on-marshmallow-6-2-18-6-2-4.29952/

用 TWRP 開機後使用 TWRP 刷入 SuperSU(建議先下載到手機內)

============

如果是要重刷整隻手機,一樣在 fastboot 內看到該手機後,把線刷包解壓縮,直接執行裡面有的 sh 檔即可

flash_all.sh 全清空,包含所有的內容
flash_all_except_storage.bat 清空 User 但不會清空虛擬 SD 卡的樣子
flash_all_except_data_storage.sh 保留 User 和虛擬 SD 卡

這邊選擇使用,就這樣而已,這邊單純自己用的 memo,以上

2016-08-05

Raspberry Pi as OpenVPN 暫存

此處待整理


新版使用

https://github.com/StarshipEngineer/OpenVPN-Setup


Pi3

#/etc/modprobe.d/raspi-blacklist.conf
#wifi
blacklist brcmfmac
blacklist brcmutil
#bt
blacklist btbcm
blacklist hci_uart

Pi3 remove wlan

sudo apt-get purge wireless* wpasupplicant bluez*
sudo apt-get autoremove

Pi3 rotate screen and remove colorful square

#/boot/config.txt
avoid_warnings=1
display_rotate=2

Pi3 disable screen blank

#/etc/kbd/config
POWERDOWN_TIME=0
BLANK_TIME=0

addon up / down / auth script


2016-06-20

sleep sort...

just code

[[sleep_sort.rb]]
ARGV.each{|e|fork{sleep(e.to_f/10);puts e}}

use

ruby sleep_sort.rb 1 9 2 8 3 7 4 6 5 0
A...... 這不會出事咪X"DDD

來源:http://m.blog.csdn.net/article/details?id=8514088

2016-05-27

Ruby : 有趣的 卍用 trace obj

just...a funny code

module W卍
  def self.method_missing(method_name , *argv , &block)
    puts "======vvvvvv======"
    puts "name : #{method_name}"
    puts "argv : #{argv}"
    puts "block : #{block}"
    puts "======^^^^^^======"
    return W卍
  end
end

# W卍[123].new.map{|i|i}.uniq.join(W卍).each do |i| puts i ; end
#=> ======vvvvvv======
#=> name : []
#=> argv : [123]
#=> block :
#=> ======^^^^^^======
#=> ======vvvvvv======
#=> name : new
#=> argv : []
#=> block :
#=> ======^^^^^^======
#=> ======vvvvvv======
#=> name : map
#=> argv : []
#=> block : #
#=> ======^^^^^^======
#=> ======vvvvvv======
#=> name : uniq
#=> argv : []
#=> block :
#=> ======^^^^^^======
#=> ======vvvvvv======
#=> name : join
#=> argv : [W卍]
#=> block :
#=> ======^^^^^^======
#=> ======vvvvvv======
#=> name : each
#=> argv : []
#=> block : #
#=> ======^^^^^^======
#=>  W卍

2016-05-11

captcha 之必要性與修正

這邊只寫下開發歷經的過程,沒實際 code

在開發時遇到的需求,類似太簡單的 captcha 會被破解( ... 自己破解自己成功 ... ),太困難的 captcha 很好用但其實很擾人,然而這就是辯思之旅,captcha 存在必要的與否

captcha 的存在必要性其實是針對機器人的防止,防止的是對『下個動作的操作』,類似登入或發文系統通常有 captcha,原因是防止登入測試與機器人發廣告信

而目前系統的問題發生在登入時,所以是怕被機器人 try 帳號密碼

分析『 try 帳號密碼 』這件事情,所得到的影響的其實是『頻率』與『對象』這兩個因素,所以只需針對這兩個因素來完成即可

中間過程略過,其實將 IP 與帳號來視為危險來源與保護對象即可,目前所想得到的最完美解為類似
  1. 該 IP 期間內是否登入錯誤太多次,否則直接顯示
  2. 使用者輸入帳號後,Ajax 到後端檢查該帳號期間內是否登入錯誤太多次,而需要顯示 captcha,如果需要則回傳 captcha 資訊並顯示
然後定義『期間』與『峰值』,就可以解決『頻率』與『對象』這回事,然而還有新的問題,類似如果遭受到 DDOS 且無差別攻擊時(無特定對象的帳號)該怎麼辦?也就是在一個來源 IP 無限且對象不定的狀況之下

這個問題其實是總量管制的意思,類似高速公路塞滿了車,則實施高承載管制來提高效率,所以再訂定了一個新的規則,類似
  1. 設定時間區間為 segment counter,每次登入錯誤時就把目前的 counter + 1,目前這個 segment 的初始值是上個 segment x 0.8 來做退火,判斷使用目前和上個 segment 的錯誤總數是否到達峰值
這樣遭到 DDOS 來破解帳號密碼時,也會自動啟動全站防護機制,而退火和判斷有依據上個 segment,原因是當此 segment 退火後,該時段攻擊機器人觀察到後又馬上攻擊,會出現很糟糕的鋸齒狀曲線的攻擊次數,雖然避免不了,但是至少可以平滑很多就是


目前分析至此,希望這樣就夠了,還有變更再另外更新

2016-05-04

產生有向性圖狀關連( MySQL + Graphviz )

DB schema

user_id , from_user_id , kind

過程:先定義 user_id < from_user_id + way ( 1 = 正向 , 2 = 反向 ),進行 GROUP 來做 clear 重複關連的動作,外面的 SELECT 再 sum 一次,如果 1 & 2 都有時變成 3 雙向,即為所求

SELECT kind , user_id , from_user_id , SUM(way) AS way FROM (
  SELECT
    kind ,
    IF(user_id < from_user_id , user_id , from_user_id) AS user_id ,
    IF(user_id < from_user_id , from_user_id , user_id) AS from_user_id ,
    IF(user_id < from_user_id , 1 , 2) AS way
  FROM tracks
  WHERE user_id IS NOT NULL AND from_user_id IS NOT NULL
  GROUP BY
    kind ,
    IF(user_id < from_user_id , user_id , from_user_id) ,
    IF(user_id < from_user_id , from_user_id , user_id) ,
    IF(user_id < from_user_id , 1 , 2)
) AS t GROUP BY kind , user_id , from_user_id

之後 Graphviz 就很簡單,圖要用 digraph(有箭頭)

way(1) : A -> B
way(2) : B -> A
way(3) : A -> B [dir="both"]

最後,類似這種解法一定要 1+ 開頭,因為 0 + 1 = 1 ([0,1] = 2 status) , 1 + 2 = 3 ([1,2,3] = 3 status)

2016-03-28

Ruby method object...

just code ...

class A
  def say(name = "yoo")
    puts name
  end
end
a = A.new
temp = a.method(:say) #get Method obj

class B
  def do_something(me)
    me.call('WTF')
  end
end
B.new.do_something(temp)

#=> WTF

2016-03-16

log trace ...

 gunzip *.gz #decompress log files
 grep -B 10 '\(aaa\|bbb\|ccc\)' * > target.txt #keywords and include 10 back lines and save to target.txt
 #vim's work...

2016-03-13

Google AdWords API + test account 申請流程......

在這邊記下來,因為他喵的這真的是一個超級大坑,看官方 doc 會遺漏一堆動作之類的 

Google AdWords 的帳號分成下面幾種:
  1. 一般 AdWords 帳號:申請頁
  2. AdWords Manager 帳號:申請頁
  3. AdWords Manager test 帳號:申請頁
其中一般的 AdWords 是沒 API 可以用的,而 AdWords Manager 帳號有 API 可用,但不能直接用,它會規定你一定要申請 test 帳號或是申請簽核後才能直接用,而 test 帳號內沒 developer_token,一定要配 AdWords Manager 帳號才能用,而一個 Google Account 同時只能有一種身份,所以......WTF...((請了一票帳號來配這個鬼,害我帳號名稱的梗都快用光了

anyway 如果你要玩的是 API 的話,請用下面的方式來申請,略過一般 AdWords 帳號你最少需要有兩個 Google Account,反正現在申請很快且很方便的切換之類的
  1. 請先去申請 AdWords Manager 帳號,登入後在灰色齒輪項目內可以找到『AdWords API 中心』,然後拿到格式很亂的亂碼『開發人員權杖(developer_token)』(這個項目在其他類型的 AdWords 帳號內"一定不會有")
  2. 申請 AdWords Manager test 帳號,登入後在灰色齒輪左邊可以找到數字中間用 "-" 連接的『客戶編號(client_customer_id)』
  3. 用 Adword Manager test 的帳號開啟 Google Developers Console 開一個新的專案,專案名稱隨便,新增後憑證那邊要先建立,開『OAuth 用戶端 ID』,名稱隨便,Logo 隱私權之類的都可略過,之後是建立用戶端 ID,選其他就好,名稱隨便,之後取得『用戶端 ID(oauth2_client_id)』,還有『用戶端密鑰(oauth2_client_secret)』,之後在總覽內啟用 API,廣告類項目都可以開
okay,所以以上已經取得『oauth2_client_id , oauth2_client_secret , developer_token , client_customer_id』之後打 OAuth2 去換 key,記得在換 key 的 URL 內 login 的是 AdWords Manager test account,之後應該可以換得『oauth2_token 的 access_token & refresh_token』

上面 OAuth2 登入部分可以使用類似官方的 tool,下面付的是 Ruby 的 OAuth2 Setup,其他語言類似 Perl / Python / Java / PHP / .NET / Android SDK / iOS SDK / Windows Phone 8 在同一個作者內的 GitHub 專案內都有

https://github.com/googleads/google-api-ads-ruby/blob/master/adwords_api/examples/v201601/misc/setup_oauth2.rb

這東西可以很方便的來做輔助設定和換 key 的動作,這個第零關打過之後你終於可以進行 API 的開發和測試惹 ......((乾,這票好囉唆的啊啊啊啊啊啊,這是我碰過最囉唆的 API 申請和測試了 Orz"...




繼續後記:

client_customer_id 分成主帳號 test 和次要帳號 sub account,類似 create_account 必須用 test 的 client_customer_id,而類似 add_campaigns 因為有內容,則需要用 sub account 的 client_customer_id

test 主帳號的 ID 在頁面的右上方,而次要帳號 sub account 在帳號列表,每個帳號的下方的數字的部分

用錯主帳號的話會得到類似『OperationAccessDenied.ADD_OPERATION_NOT_PERMITTED』
用錯子帳號的話會得到類似『NOT_AUTHORIZED』

anyway 換一下 client_customer_id 切換一下說不定就可以解掉這些問題




繼續後記:


AdWords Manager 可以樹狀,可以再連結另外一個 Manager 帳號,而 API 打出來做列表時,可列出所有 Manager 帳號和一般帳號(所有子代都一次列出)並且做操作之類的

2016-03-09

mysqld_multi + master / slave replication

這邊在製作一台 MySQL 在 mysqldump 的時候沒有 lock,所以需建立 Master / Slave 然後對 Slave 來做 dump,而希望在同一台這樣速度最快,當然 dump 下來的 sql file 需放在別台機器上來做異地備份,以下是順序

  #增加設定值(/etc/mysql/my.cnf)
[mysqld]
server_id = 1
log_bin = /var/log/mysql/mysql-bin.log # if replication

[mysqld2]
server_id = 1002
pid-file  = /var/run/mysqld/mysqld2.pid
socket    = /var/run/mysqld/mysqld2.sock
port  = 3307
datadir   = /var/lib/mysql2
log_bin = /var/log/mysql/mysql-bin2.log # if replication

  #要建立額外的檔案,包含pid和sock(這邊沒做會開不起來...)
sudo -u mysql touch /var/run/mysqld/mysqld2.pid
sudo -u mysql touch /var/run/mysqld/mysqld2.sock

  #建立空白資料集
sudo mysql_install_db --user=mysql --datadir=/var/lib/mysql2

  #之後就可以測試mysqld_multi
mysqld_multi report   #check status
mysqld_multi start 2  #open 2
mysqld_multi report   #recheck
#mysqld_multi stop 2  #if need stop

  #建立連線2改用
mysql -u root -p -S /var/run/mysqld/mysqld2.sock

#==========[[master]]

sudo /etc/init.d/mysql restart #重開 master server (因為有設定 log_bin)

  #next need 2 session , mysql client + bash

  #設定帳號密碼
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'localhost' IDENTIFIED BY 'PASSWORD_HERE';
  #鎖定整個資料庫
FLUSH TABLES WITH READ LOCK;
  # remember 顯示資訊要記好,類似以下要記下來
  #+------------------+----------+--------------+------------------+ 
  #| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB |
  #+------------------+----------+--------------+------------------+
  #| mysql-bin.000002 |      267 |              |                  |
  #+------------------+----------+--------------+------------------+
SHOW MASTER STATUS;
  #dump所有的資料庫,需加master-data(這邊要另外的session來做,原來的client不能中斷)
mysqldump -u root -p --master-data --all-databases > all_mysql_db.sql
  #解除寫入
UNLOCK TABLES;

#==========[[slave]]

  #dump回去
mysql -u root -p -S /var/run/mysqld/mysqld2.sock < all_mysql_db.sql
  #設定繫結,這邊要填入 File => MASTER_LOG_FILE & Position => MASTER_LOG_POS
CHANGE MASTER TO MASTER_HOST='localhost',MASTER_USER='repl',MASTER_PASSWORD='PASSWORD_HERE',MASTER_LOG_FILE='mysql-bin.000002',MASTER_LOG_POS=267;
  #開始
START SLAVE;
  #check master & slave
SHOW MASTER STATUS;
SHOW SLAVE STATUS;
  #如果是 "Waiting for master to send event" 就ok
打完收工 (rock)

2016-01-16

Rails memcache lock (dalli)

最近在實作 cache & lock,不過這邊不寫一般的作法,單純希望有個方便的lock可以在記憶體中,要防止使用者的重複交易之類的

我可以用DB,但怕有效能影響,所以只能選擇 memory 系列,而我有 Redis & memcache(dalli) 可以選

用 Redis 的話缺點很大,因為 hash 沒有 expire 可選,只有一般的 key 有,所以會多一票垃圾 key 在列表內,非常 dirty ...,所以只剩下 memcache

而 memcache 其實無 lock 選項,而...只有一種作法之類的,叫做 increment,也就是某個變數 + 1 的意思,當 return > 1 時,就代表這個 lock 已經失效,這就非常方便了

最後實作會類似這樣的 code :

  def action_lock(kind , timer: 600 , token: nil , user: current_user)
    return false if !timer || timer < 1
    #key的格式請自己編
    return Rails.cache.increment("ACTION_LOCK_#{user.id}_#{kind}_#{token}" , 1 , :expires_in => timer) == 1
  end

有這個基本後,就可以再製作很多的東西,也就是專案的需求

  1. 封包重送問題
  2. 同BTC wallet和金額的重複交易問題
  3. 發送頻率問題

(1):封包重送問題,可以使用 random post token 來完成,類似每次 post from 都產生一個 random key 來當 token,然後 post 出去後就記錄已使用,下次再接收時則阻擋,這樣可以防止類似手機或筆電的連線中斷而封包重送且重複執行的問題,類似 "#{kind}_#{user_id}_#{random_token}"

(2):重複交易其實就很簡單,想辦法把KEY編寫一樣即可,類似 "#{kind}_#{user_id}_#{wallet}_#{amount}" 的方式,並設定過期時間,就可以輕易完成

(3):發送頻率則更簡單,因為是限制單一使用者,則直接把 user_id 當作 key即可,類似 "#{kind}_#{user_id}"

okay打完收工(rock)

2015-10-19

Ruby : Exception bug??

......just code
require 'thread'

$temp = Thread.new do
  loop do
    puts 'loop me'
    begin
      puts "try thread"
      raise Exception.new('QwQ') if rand > 0.5
      puts "skip try"
    rescue
      puts "QwQ"
    end
    sleep(0.5)
  end
  puts '...WTF'
end

loop do
  puts "runner #{Thread.list.length} #{$temp.status}"
  sleep(2)
end
loop 會 fail loop ... 然後把所有的 rescue 改成 rescue Exception => e 就會成功,like
require 'thread'

$temp = Thread.new do
  loop do
    puts 'loop me'
    begin
      puts "try thread"
      raise Exception.new('QwQ') if rand > 0.5
      puts "skip try"
    rescue Exception => e
      puts "QwQ"
    end
    sleep(0.5)
  end
  puts '...WTF'
end

loop do
  puts "runner #{Thread.list.length} #{$temp.status}"
  sleep(2)
end
......兩個檔案diff只有一行......Orz"......成功... 原因: rescue 預設是 StandardError 而非 Exception ...... 來源: https://robots.thoughtbot.com/rescue-standarderror-not-exception

2015-09-15

Pebble time CJK language pack

just link: http://blog.kuro.ro/pebble-time-chinese-japanese-language-pack/

2015-09-14

mi note 4g LTE KXEMICH root

just url : http://hk.miui.com/thread-3300-1-1.html

2015-08-26

Raspberry Pi Kiosk on Raspbian Debian Wheezy

first here : https://github.com/basdegroot/raspberry-pi-kiosk & fix
@chromium --kiosk --ignore-certificate-errors --incognito --noerrdialogs --disable-restore-session-state "http://127.0.0.1"
& PS : Pi no WebGL support...... & addon : http://www.tweaking4all.com/hardware/raspberry-pi/install-lazarus-pascal-on-raspberry-pi-2/

2015-08-14

Ruby : kill child class

just code ... ha ha ...

  class KillChild
    def self.inherited(child_class)
      Object.send(:remove_const, child_class.to_s.to_sym)
    end
  end
可惜沒有 mixin 的 X"D & part 2

  "".class.object_id
  #=> 70358229142320
  String.object_id
  #=> 70358229142320
  class TempString < String ; end ; Object.send(:remove_const, :String) ; class String < TempString ; end
  "".class.object_id
  #=> 70358229142320
  String.object_id
  #=> 70358232176640
hahaha...換掉了||| & part 3

  # ~/.irbrc

  Object.send(:remove_const , :String)
  Object.send(:remove_const , :Fixnum)
hmm...anyway,單純有趣的部分就是

2015-07-25

Cubietruck NAND images

http://dl.cubieboard.org/software/a20-cubietruck/ lubuntu : linaro/linaro

2015-07-23

Raspberry Pi 2 B + DS3231 (RTC)

source from http://thepihut.com/blogs/raspberry-pi-tutorials/17209332-adding-a-real-time-clock-to-your-raspberry-pi and https://nicegear.co.nz/blog/using-an-i2c-real-time-clock-rtc-with-a-raspberry-pi/ ... first enable i2c / spi

    sudo raspi-config
    #Advanced Options => enable Device Tree / SPI / I2C
fix boot

    sudo vim /boot/config.txt
    #Uncomment
    dtparam=i2c_arm=on
    dtparam=i2s=on
    dtparam=spi=on
fix modules

    sudo vim /etc/modules
    #fix to like
    snd-bcm2835
    i2c-bcm2708
    i2c-dev
    rtc-ds1307
fix init(hwclock -s = read from DS3231)

    sudo vim /etc/rc.local
    #add this before exit 0
    echo ds1307 0x68 > /sys/class/i2c-adapter/i2c-1/new_device
    sudo hwclock -s
    date
check blacklist

    sudo vim /etc/modprobe.d/raspi-blacklist.conf
    #comment spi / i2c module #此處為黑名單,要加上註解,否則無法使用
init hwclock如果你的硬體本身是新的或是要重置(安裝NTPD)(hwclock -w = write to DS3231)

    #sudo service ntp stop
    sudo ntpd -gq #fix system clock to current time from NTP
    sudo hwclock -w #write to hwclock
    #sudo service ntp start
memo : load module if not found hwclock

    sudo modprobe rtc-ds1307
    sudo modprobe i2c-bcm2708

2015-06-30

某字形

http://www.n-plus.jp/download/download.shtml

2015-05-11

Ruby / Sinatra / Rails : Grape & Swagger

這邊盡量簡單的寫這篇,首先Ruby的swagger使用grape這個gem實作,所以看完Grape就完成一大半了,而有gem 的 3 + 1套件(就一個眾星拱月的概念...),分別為


gem 'grape'
gem 'grape-swagger'
gem 'grape-swagger-rails'
gem 'rack-contrib'

rack-contrib直接略過,上rack就用一下唄,有寫swagger的一點都不重要,最多幫忙打view / UI / 顯示doc而已,也直接略過,所以只剩下最重要的grape

grape是建構在rack上,而Ruby / Sinatra / Rails都可以使用,還可以上ActiveRecord,裡面有完整的route / helper / filter...機制,所以README寫得超複雜,不過以下簡單講,畢竟能做測試才能走第一步

Rails要這樣安裝,類似在config/application.rb內加上


config.paths.add File.join('app', 'api'), glob: File.join('**', '*.rb')
config.autoload_paths += Dir[Rails.root.join('app', 'api', '*')]

這樣你的專案啟動時就會自動載入app/api/*.rb,而Kawaii就是我的API,它寫在app/api/kawaii.rb內,再來是route.rb


mount Kawaii::API => '/'

它會先跑Rails的middleware,去找有沒有該path才會到Rails端,這邊注意的是通常API的path都要在最上面才是

再來是主體部分,先顯示我的code,然後來做導讀

require 'grape'
require 'grape-swagger'
module Kawaii
  class APIv1 < Grape::API
    version :v1 , using: :path
    helpers do
      def current_user
        123
      end
    end
    desc 'Update a spline.'
    params do
      requires :id, type: Integer, desc: 'Spline id.'
      optional :reticulated, type: Boolean, default: true, desc: 'True if the spline is reticulated.'
    end
    get ':id' do
      { splines_url: params[:id] }
    end
  end
  class APIv2 < Grape::API
    version :v2 , using: :path
    helpers do
      def current_user
        234
      end
    end
    desc 'Update a spline.'
    params do
      requires :id, type: Integer, desc: 'Spline id.'
      optional :reticulated, type: Boolean, default: true, desc: 'True if the spline is reticulated.'
    end
    get ':id' do
      { splines_url: current_user }
    end
  end
  class API < Grape::API
    format :json
    prefix :api
    add_swagger_documentation
    mount Kawaii::APIv1
    mount Kawaii::APIv2
  end
end

從最後開始看,外面包一層Kawaii略過,API繼承Grape::API略過

format = redner :format,也就是這整票API都會用json的方式傳出,官方支援一票奇怪的格式可以玩,還包括邪惡的jsonp超讚的

prefix 全等於root path,所以這樣寫會變成"/api/..."開頭的網址

add_swagger_documentation ......還沒研究後補

mount Kawaii::APIvN 這邊來拆版本的API,建議一開始就保留讓自己有後路X"D

=======再來是 Kawaii::APIvN

version :v1 , using: :path 等於此行以下的code都是v1,很像ruby的private的用法,而using是辨認版本使用path,所以此行以下的網址加上述會是"/api/v1/...",而還有一票東西可以用,類似放在header內有的沒的

helpers do ... end 這邊當作 global method 來看就好,和application_controller / application_helper的method的作用一樣,單純拿來共用的,且注意,version :v1之下的就只屬於v1的methods能用,和v2有相同名稱的和此無關且不會覆寫


[desc , params , get]這些是一整套,可以缺但不能分開看,請記得

desc 其實就是 doc

params do ... end 所有有需要傳入的 params
    requires 必要值
    optional 非必要值
    //其實還有group,可能是任一或多數重複,還沒玩過不知道它是啥,請訪客提示X"D
        type , default ... 這類就是過濾之類的,而且還能上regex,格式錯誤會直接吐錯誤訊息回去{error:'xxx error'}超帥氣的

get,這是最後的,其實就是當controller來使用,而最後吐回的值直接用hash不用xxx.to_json,因為會自動幫你做掉,而類似 '/yoo/:id/hoo/:kind' 的path的寫法,這就是傳統的params的寫法了,配上面的params使用,不然就要用get / post的方式傳入了(網址後綴或是post body)

上面網址弄好後你就有這樣的網址可以玩"/api/v2/123"

okay,而rack系列的另外一個特點,不會有log,因為log都要另外做,不會有route所以rails route打到死連個屁都噴不出來,有以上基本之後再來看這份超級長的README教學,應該就看得懂了才是 & 建議看一次,真的啥鬼都支援,活性超高的,以後都拿這個鬼來打API X"D

https://github.com/intridea/grape

而先不要管grape-swagger / grape-swagger-rails了,先把API打對唄,而上面裝可愛的v1 & v2的code其實可以不用另外mount,可以全部打在一起(複製貼上就好),且全部不會亂掉呦,用類似private定義method的方式來想一切就都順暢了(該行以下全都是private methods) 介紹到此,以上

2015-05-04

Ruby : General Delimited Input

這邊...是邪惡的開始||||

from : http://ruby-doc.org/docs/ruby-doc-bundle/ProgrammingRuby/book/language.html

其中只解釋


  %q = '' Single-quoted string
  %Q, % = "" Double-quoted string
  %w = [] Array of tokens (split by ' ')
  %r = // Regular expression pattern
  %x = `` Shell command


後面還寫了,此標記後面一碼為包圍碼除了[0-9A-Za-z],所以以下都是合用的


  %q!\n!   #=>  "\\n"
  %?\n?    #=>  "\n"
  %w%123 234% #=> ["123","234"]
  %r/test/  #=> /test/
  %r\test\  #=> /test/
  %x.ls.    #=> call system `ls`
  %x#ls#    #=> call system `ls`
  %!%!      #=> '%'
  ...


嘛...寫code要小心了X"D或是小心別人的code

Ruby + Rails ActiveRecord timezome settings


    #Ruby
    ActiveRecord::Base.default_timezone = :utc
    #Rails
    #do nothing
(rock!)

2015-03-17

Ruby : Integer Obfuscator

最近在做數字攪亂器,類似 user_id : ATM(虛擬帳號互轉),簡單的來說要1:1的攪亂,找了幾篇文章才找到學理解釋,類似這篇

http://ericlippert.com/2013/11/14/a-practical-use-of-multiplicative-inverses/

大概就是需要三個數字,MAX[int] & 質數A[int:coprime] & inverse(MAX & 質數A)[int:coprime],最後一個數字可以算出來,MAX則為需求,類似99999999,質數A就到處亂找了,類似2147483647 (2^32 - 1)

so~ 以上,先列出inverse的寫法
http://en.wikipedia.org/wiki/Extended_Euclidean_algorithm
  MAX = 99999999
  COPRIME = 2147483647

  def inverse(a, n)
    t = 0 ; newt = 1
    r = n ; newr = a
    while newr != 0
      quotient = r / newr
      t , newt = newt , t - quotient * newt
      r , newr = newr , r - quotient * newr
    end
    return "a is not invertible" if r > 1
    return t < 0 ? t + n : t
  end

  COPRIME_INVERSE = inverse(COPRIME , MAX) #=> 52892335
再來其實就是使用了,使用的話就非常簡單,類似
  def source_encode(source)
    return source.to_i * COPRIME % MAX
  end
  def source_decode(source)
    return source.to_i * COPRIME_INVERSE % MAX
  end

  source_encode(1) #=> 47483668
  #...

當然你可以用小數字去測試這是否是正確的,類似 MAX = 10

  MAX = 10
  COPRIME = 2147483647
  COPRIME_INVERSE = inverse(COPRIME , MAX) #=> 3
  (MAX * 2).times do |source|
    puts "#{source} : #{source_encode(source)} : #{source_decode(source_encode(source))}"
  end
  #=> 0 : 0 : 0
  #=> 1 : 7 : 1
  #=> 2 : 4 : 2
  #=> 3 : 1 : 3
  #=> 4 : 8 : 4
  #=> 5 : 5 : 5
  #=> 6 : 2 : 6
  #=> 7 : 9 : 7
  #=> 8 : 6 : 8
  #=> 9 : 3 : 9
  #=> 10 : 0 : 0
  #=> 11 : 7 : 1
  #=> 12 : 4 : 2
  #=> 13 : 1 : 3
  #=> 14 : 8 : 4
  #=> 15 : 5 : 5
  #=> 16 : 2 : 6
  #=> 17 : 9 : 7
  #=> 18 : 6 : 8
  #=> 19 : 3 : 9

這邊其實多了示範在 MAX * 2 在顯示如果超過會怎樣,你會發現會循環,其實某種程度上這非常方便,類似前面再加上英文字母就可以多軌同時進行且自動切割好好,而如果你怕人破解,請隨機找個質數之類的,會建議大點,anyway以上 & (rock)

2015-03-03

RVM Rails Sidekiq as Daemon : /etc/init.d/sidekiq

source from : http://cdyer.co.uk/blog/init-script-for-sidekiq-with-rbenv

首先,找到bundle wapper,類似: /home/user/.rvm/wrappers/ruby-2.1.5/bundle
然後Project path : /home/user/project/my_rails_project

再來,sudo vim /etc/init.d/sidekiq,輸入以下內容

!/bin/bash
# sidekiq    Init script for Sidekiq
# chkconfig: 345 100 75
#
# Description: Starts and Stops Sidekiq message processor for Stratus application.
#
# User-specified exit parameters used in this script:
# Exit Code 5 - Incorrect User ID
# Exit Code 6 - Directory not found

# You will need to modify these
APP="my_rails_project"  #fix this
AS_USER="user"  #fix this
APP_DIR="/home/user/project/my_rails_project"  #fix this

APP_CONFIG="${APP_DIR}/config"
LOG_FILE="$APP_DIR/log/sidekiq.log"
LOCK_FILE="$APP_DIR/${APP}-lock"
PID_FILE="$APP_DIR/${APP}.pid"
GEMFILE="$APP_DIR/Gemfile"
SIDEKIQ="sidekiq"
APP_ENV="production"
BUNDLE="/home/user/.rvm/wrappers/ruby-2.1.5/bundle"  #fix this

START_CMD="$BUNDLE exec $SIDEKIQ -e $APP_ENV -P $PID_FILE"
CMD="cd ${APP_DIR}; ${START_CMD} >> ${LOG_FILE} 2>&1 &"

RETVAL=0

start() {

  status
  if [ $? -eq 1 ]; then

    [ `id -u` == '0' ] || (echo "$SIDEKIQ runs as root only .."; exit 5)
    [ -d $APP_DIR ] || (echo "$APP_DIR not found!.. Exiting"; exit 6)
    cd $APP_DIR
    echo "Starting $SIDEKIQ message processor .. "

    su -c "$CMD" - $AS_USER

    RETVAL=$?
    #Sleeping for 8 seconds for process to be precisely visible in process table - See status ()
    sleep 8
    [ $RETVAL -eq 0 ] && touch $LOCK_FILE
    return $RETVAL
  else
    echo "$SIDEKIQ message processor is already running .. "
  fi


}

stop() {

    echo "Stopping $SIDEKIQ message processor .."
    SIG="INT"
    kill -$SIG `cat  $PID_FILE`
    RETVAL=$?
    [ $RETVAL -eq 0 ] && rm -f $LOCK_FILE
    return $RETVAL
}

status() {

  ps -ef | grep 'sidekiq [0-9].[0-9].[0-9]' | grep -v grep
  return $?
}


case "$1" in
    start)
        start
        ;;
    stop)
        stop
        ;;
    status)
        status

        if [ $? -eq 0 ]; then
             echo "$SIDEKIQ message processor is running .."
             RETVAL=0
         else
             echo "$SIDEKIQ message processor is stopped .."
             RETVAL=1
         fi
        ;;
    *)
        echo "Usage: $0 {start|stop|status}"
        exit 0
        ;;
esac
exit $RETVAL

之後就輸入一些指令讓它可以開機執行
sudo update-rc.d sidekiq defaults 99

然後就可以開心的這樣做
service sidekiq start
service sidekiq status
service sidekiq stop

2015-02-16

Rails STI type be int

just code
from : http://lorefnon.me/rails/2014/07/27/optimizing-sti-columns.html

class ThirdOrder < ActiveRecord::Base
  ALLOWED_CLASSES = %w(ThirdOrder FamiOrder) #can't Array , it Circular dependency

  #STI addon (parent only)
  def self.find_sti_class type_name
    ALLOWED_CLASSES[type_name.to_i].constantize
  rescue NameError, TypeError
    super
  end
  def self.sti_name
    return ALLOWED_CLASSES.index(self.name)
  end
end
class FamiOrder < ThirdOrder ; end

2014-11-28

Rails : multi_paginate

source at : https://github.com/mislav/will_paginate/issues/351

and fix to multiple type :P

just source code (add to like ** application_controller.rb ** )

def multi_paginate(models, page, per_page)
  WillPaginate::Collection.create(page, per_page) do |pager|
    models.keep_if {|m| m.count > 0 }
    pager.total_entries = 0
    counts = []
    offsets = []
    limits = []
    for model in models
      pager.total_entries += model.count
      counts << model.count
      offset = pager.offset - offsets.sum
      offset = offset>model.count ? model.count : offset 
      offsets << (offset < 0 ? 0 : offset)
    end
    result = []
    for i in 0...models.count
      limits << pager.per_page - result.length
      result += case models[i]
      when ActiveRecord::Relation
        models[i].limit(pager.per_page - result.length).offset(offsets[i]).to_a
      when Array
        models[i][offsets[i]..(offsets[i] + pager.per_page - result.length)].to_a
      when Hash
        models[i].to_a[offsets[i]..(offsets[i] + pager.per_page - result.length)].to_a
      end
    end
    pager.replace(result)
  end
end

then use at controller like

 #source , page , per_page
 @orders = @paginate = multi_paginate(
   [@incoming_order , @outgoing_order , @redis_roder] ,
   params[:page].to_i.zero? ? 1 : params[:page].to_i.abs ,
   30
 ) 

then view

<% @orders.each do |order| case order ; when IncomingOrder %>
  ...view for IncomingOrder
<% when OutgoingOrder %>
  ...view for OutgoingOrder
<% when Array %>
  ...view for Array ... (Ruby or custom class)
<% end %>

hmmm... it's all & fave fun :)