小B的技術筆記

解決過的問題備忘錄 (外部記憶體?)

2010年7月11日星期日

Ruby Multi-threading simple tutorial

正體中文版在這裡。(Traditional Chinese version is here.)


Thread Lifecycle

Create a new thread:
puts "main thread start"
t1=Thread.new{
  puts "new thread"
}
puts "main thread end"

Remember, if you didn't join the thread instance. The new thread will be interrupt when the main thread ended. Try following code:
puts "main thread start"
t1=Thread.new{
  sleep 5
  puts "new thread"
}
# sleep 10 # uncomment this line and try again
puts "main thread end"

Join the thread t1:
puts "main thread start"
t1=Thread.new{
  sleep 5
  puts "new thread"
}
t1.join
puts "main thread end"


But if you want 2 or more threads run at the same time. Don't join right after new it:
puts "main thread start"
t1=Thread.new{
  sleep 5
  puts "new thread 1"
}
t1.join # main thread will wait here until thread t1 finished
t2=Thread.new{
  sleep 5
  puts "new thread 2"
}
#t1.join # should place here
t2.join
puts "main thread end"


Exceptions

If main thread throw an exception. The script will exit and print error message. But if exception in new threads. It exit only the thread and keep others running, by default. If you want they interrupt the script, than you can set t1.abort_on_exception to true.

But if you use t1.join or t1.value, they interrupt the script by default. Because the main thread will wait at that 2 commands. (t1.value is that return value of the thread block.)
puts "main thread start"
t1=Thread.new{
  sleep 5
  puts a # Exception: undefined
  puts "new thread"
}
t1.abort_on_exception = true # Default is false,If you uncomment this line. You will find out that it doesn't run the line: puts "new thread"
sleep 10
puts "main thread end"

But if it throws exception on the first line in the thread block. It can throw before setting abort_on_exception. So we can use like this:
puts "main thread start"
t1=Thread.new{
  Thread.current.abort_on_exception=true # Thread.current is the thread object itself
  puts a # Exception: undefined
  puts "new thread"
}
sleep 10
puts "main thread end"

Variable scope

In new threads can get/set and shared variables at same scope. But local variables in Thread.new {} block are not shared with other threads. And there is a special usage: Use Thread instance like hash:
outside = "DDDDD"
t1=Thread.new{
  puts outside # get DDDDD
  local_var = "AAAAA"
  Thread.current["var"] = "BBBBB"
}
puts t1["var"] # get BBBBB
puts local_var # Exception: undefined




Hope this simple ruby multi-threading tutorial have helped you :-)

Reference (More detail)

2010年6月20日星期日

Gets undefined method error when calling parse method?

irb(main):001:0>  Time.parse("16:30")
NoMethodError: undefined method `parse' for Time:Class

Also rfc2822(), httpdate() and xmlschema(date) raises this error.


Solution: require the time.rb:
require 'time'

This is not a bug. It's because they are not core (But Time class is). Some implementation may auto require for you. But if you get this error, just try to require it.

Reference 12



==Traditional Chinese version==

呼叫Time.parse時得到undefined method錯誤?

irb(main):001:0>  Time.parse("16:30")
NoMethodError: undefined method `parse' for Time:Class

rfc2822(), httpdate() and xmlschema(date) 也會爆這個錯誤


解決方法: require time.rb:
require 'time'

這不是bug,這是因為這些methods不是core(但Time class本身是),有些implementation可能會自動幫你require,但如果你遇到這個error,就試試看require 'time'

參考來源 12

2010年6月12日星期六

(Ruby) Write service with daemons gem

How to make a ruby script run only one instance at a time? and with start/stop command?

Actually, it's very simple, just use Daemons gem.

installation (ubuntu):
sudo gem install daemons

Simplest example: test.rb
require 'rubygems'
require 'daemons'

Daemons.run_proc('myproc.rb') do
  loop do
    sleep(5)
    puts "Hello World"
  end
end

Than you can use following commands:
ruby test.rb [start|stop|restart|run|zap]

(Run 'ruby test.rb' will get command usage.)

And when you try to run 'test.rb start' twice. you will get:
ERROR: there is already one or more instance(s) of the program running



==Traditional Chinese version==


如何讓一支ruby程式一次只有一個instance?並且有start/stop等指令呢?

其實,這非常簡單,只要用Daemons gem就可以了。

安裝 (ubuntu):
sudo gem install daemons

最簡單的範例: test.rb
require 'rubygems'
require 'daemons'

Daemons.run_proc('myproc.rb') do
  loop do
    sleep(5)
    puts "Hello World"
  end
end

然後你就可以用以下指令了:
ruby test.rb [start|stop|restart|run|zap]

(執行 'ruby test.rb' 會提示你指令用法)

當你嘗試執行 'test.rb start' 兩次,你會得到以下錯誤:
ERROR: there is already one or more instance(s) of the program running

2010年4月29日星期四

Problem when add the facebook new Like Box to google Blogger?

For English reader please read this post instead


我想要新增Facebook新版的Like Box (原本的Fan Box)到google blogger,但是他的iframe一直跟我說找不到頁面。後來找到了這個(英文),解法如下:

先在Facebook給你的code裏面,找到這段(YOUR_FACEBOOK_PAGE_ID是一串數字):

...profile_id=YOUR_FACEBOOK_PAGE_ID...

把他改成:

...profile=1&id=YOUR_FACEBOOK_PAGE_ID...

就搞定了。收攤!

2010年3月14日星期日

sikuli在存檔之後就失效 (問題:中文路徑)

在windows xp上試玩sikuli,但是很奇怪,明明一切都很順利,怎麼突然一個都偵測不到,出現紅色背景?

得到錯誤訊息:
edu.mit.csail.uid.FindFailed: FindFailed: 1268625522234.png can't be found.
ps: 那個xxxxxx.png就是截圖的檔名

如果點圖片preview,會得到以下訊息
0 matches found
ps: 但是正常情況時,會有50 matches,一次掉到0也太奇怪了

經過幾次測試,我發現在存檔之後才出現失效狀況,猜測是因為存檔路徑有中文,移到別的目錄後,發現一切正常了。

vgod的plurk也有討論到這個狀況(我google「sikuli 路徑 中文」時才找到的...)

2010年2月23日星期二

Make acts_as_versioned know target model belongs_to association

acts_as_versioned is a great plug-in for Rails. It can keep track of all modifications to your target model with only a little setup. But it seems not support has_*/belongs_to association by default. So let's see how to enable this.

Assume that you are going to keep track of the 'Post' model. and this is probably your model looks like:
class Post < ActiveRecord::Base
  acts_as_versioned 
  belongs_to :user
end

But version.user will not work (at least, for this version).


(1) For newer version of acts_as_versioned, try this:
class Post < ActiveRecord::Base
  acts_as_versioned do
    belongs_to :user
  end
end

(2) And for older version, try this:
class Post < ActiveRecord::Base
  acts_as_versioned 
  belongs_to :user
end

Post.versioned_class.class_eval do
  belongs_to :user
end


solution 2 works for me! but solution 1 not (ruby 1.8.6, rails 2.3.2, acts_as_versioned 0.3.1)


==Traditional Chinese version==


acts_as_versioned 是個好套件。只要一點設定他就可以幫你紀錄model的版本變動。可是他預設沒有支援has_*/belongs_to的資料庫關聯,所以讓我們來看看要如何打開這個功能。

假設你要追蹤的是Post這個model,那你的model檔大概長這樣:
class Post < ActiveRecord::Base
  acts_as_versioned 
  belongs_to :user
end


但這個時候 version.user 是會掛掉的(至少這版套件如此)


(1) 套件版本較新者可以試試這個:
class Post < ActiveRecord::Base
  acts_as_versioned do
    belongs_to :user
  end
end


(2) 舊版者,則試這個:
class Post < ActiveRecord::Base
  acts_as_versioned 
  belongs_to :user
end

Post.versioned_class.class_eval do
  belongs_to :user
end



我用第2個方案OK,但是第1個方案則不行... (ruby 1.8.6, rails 2.3.2, acts_as_versioned 0.3.1)

2010年2月20日星期六

Rails sending emails via gmail not working in heroku server but ok in localhost

I've encountered a problem that my rails app can send email via gmail properly in my computer but not working in heroku server (get an error: "Must issue a STARTTLS command first").

Following by this post:

1. download tls_smtp.rb. (found from this post)
2. put it into your app lib directory
3. import it in config/environment.rb
require 'tls_smtp'

Then upload to heroku, it's done!

I didn't add additional config in config/environments/production.rb as this post said. But it still work.

This is my mailer config (I put it in config/initializers/mail.rb):
ActionMailer::Base.smtp_settings = {
  :enable_starttls_auto => true,
  :address => 'smtp.gmail.com',
  :port => 587,
  :domain => 'gmail.com',
  :authentication => :plain,
  :user_name => 'xxxxxx@gmail.com',
  :password => '******'
}

Hope this helps


==Traditional Chinese version==

我剛才遇到一個問題,我用gmail寄信,在localhost也測試OK,但是丟上heroku後卻壞掉(得到「Must issue a STARTTLS command first」錯誤)

後來找到了一篇文章,依照指示:

1. 下載 tls_smtp.rb. (在這篇找到的)
2. 放到app的lib資料夾
3. 引入到 config/environment.rb
require 'tls_smtp'

然後上傳到heroku,完成了!

我沒有依照這篇所說要再增加mailer設定到config/environments/production.rb,不過他還是成功。

這是我的mailer設定 (我放在 config/initializers/mail.rb):
ActionMailer::Base.smtp_settings = {
  :enable_starttls_auto => true,
  :address => 'smtp.gmail.com',
  :port => 587,
  :domain => 'gmail.com',
  :authentication => :plain,
  :user_name => 'xxxxxx@gmail.com',
  :password => '******'
}

希望這篇有幫助