2011年2月11日星期五

Path Finder 整合 Dropbox 功能

一般安裝 Dropbox 之後, Finder 中會自動整合其功能
像是在資料夾上按右健可以直接在 Dropbox 網頁中打開、取得 Public Path 等等功能
但是我使用 Path Finder 他並沒有幫我把外掛安裝好

要自己從 Dropbox.app/Contents/Resources/DropboxPlugin.plugin
複製到 ~/Library/Contextual Menu Items

複製後重新啟動 Path Finder 就可以看到整合的功能了

2011年1月13日星期四

在 Rails3 中使用 jQuery

Rails3 預設也是使用 Prototype.js
在產生 rails 專案時,輸入 rails your_project -J 則不會產生 Prototype.js 的檔案

用官方提供的 jQuery js driver : http://github.com/rails/jquery-ujs/blob/master/src/rails.js
取代 /public/javascripts/rails.js 這個檔案

在需要使用的 layout header 中使用
<%= javascript_include_tag "http://ajax.googleapis.com/ajax/libs/jquery/1.4.1/jquery.min.js" %>
<%= javascript_include_tag 'rails' %>
或是自己導入路徑中的 jQuery 檔案

2011年1月5日星期三

使用 rvm 管理 ruby 版本

$ bash < <( curl http://rvm.beginrescueend.com/releases/rvm-install-head ) # 安裝 rvm
rvm list # 查看目前安裝的 ruby 版本
rvm info # 查看目前使用的環境訊息
rvm list known # 列出可以安裝的版本
rvm install ruby-1.9.2-head # 安裝指定版本
rvm use ruby-1.9.2-head # 目前環境使用指定版本
rvm use ruby-1.9.2-head --default # 預設使用指定版本
rvm system # 使用回系統預設版本
rvm reset # 預設使用系統版本
安裝後要在 .bashrc 或是啟動 shell 會執行的檔案中加入
[[ -s "$HOME/.rvm/scripts/rvm" ]] && source "$HOME/.rvm/scripts/rvm"
確保每次進入 shell 都會讀取 rvm 設定的狀態

2010年12月25日星期六

Java 時間轉換的時區問題

其實是在寫 Android 軟體時發現的問題
因為把時間在 SQLite 中以字串儲存,取出來的時候要轉換成 Date 物件。我是使用 SimpleDateFormat 的物件來做轉換,之前因為只把轉換出來的時間物件拿來計算時間差距,所以沒發現轉換出來的時間和字串表示的時間不同!今天用的時候著實嚇一跳…

原來 SimpleDateFormat 在轉換的過程會考慮到系統目前的時區,所以
"2010-12-25T08:00:00+08:00" 會轉換成 "2010-12-25 00:00:00 CST"
我的時區是 Asia/Taipei GMT+8,所以就這樣活生生被吃了八個小時
所以之後再用 Date.getTime() 等 function 時取得的時間就和字串表示的時間不同了!

我目前最簡單的解決方式就是指定 SimpleDateFormat 的時區,來得到和字串表面相同的 Date 物件

SimpleDateFormat dateFormat;
dateFormat = new SimpleDateFormat("yyyy-MM-dd"); // 指定格式
dateFormat.setTimeZone(TimeZone.getTimeZone("GMT")); // 指定時區
dateFormat.parse("2010-12-25");
這樣就沒問題了,否則上述範例中,被吃了八小時日期會來到 2010-12-24 !

2010年12月19日星期日

設定 Apache 簡易密碼認證

久久設定一次就會忘記 (汗…)
設定 Apache 簡易密碼認證,這樣要先輸入帳號密碼才能看到網頁內容
基本設定不贅述,在設定文件中找到想要設定的目錄

<Directory /var/www/html>
    AllowOverride FileInfo AuthConfig Limit 
    Options MultiViews SymLinksIfOwnerMatch
    <Limit GET POST OPTIONS PROPFIND>
        Order allow, deny 
        Allow from all 
    </Limit>
    <Limit PUT DELETE PATCH PROPPATCH MKCOL COPY MOVE LOCK UNLOCK>
        Order deny, allow 
        Deny from all 
    </Limit> 
</Directory>
注意一定要 AllowOverride AuthConfig
如果要設定某些 request 的限制,也要 AllowOverride Limit

再來到要限制存取的目錄中新增 .htaccess 檔
AuthName "Section Name" 
AuthType Basic 
AuthUserFile /var/www/html/.htpasswd 
Require valid-user
AuthUserFile 路徑檔名可以隨便取,也可以放在其它地方

最後是新增 AuthUserFile,路徑就是上面設定的
使用指令:htpasswd filename username

2010年12月18日星期六

解決 Android 模擬器無法上網 - 更改 DNS 設定

也不知道為什麼,好像是升級了新的 SDK 之後模擬器就突然無法上網
其實之前發生過,但是今天有人突然問我我又忘記怎麼設定了,趕緊筆記一下!
我自己遇到的問題是因為模擬器中的 DNS 設定錯誤,所以當然對應不到 IP
所以把 DNS 設定成正確的就可以了!

使用 SDK 中附的工具: adb

./adb shell
$ getprop  # 查看屬性設定
[net.dns1]: [192.168.2.1]  # 某一筆是 net.dns1 就是目前的設定
$ setprop net.dns1 192.168.2.1  # 設定成可以用的 DNS
                                # 不知道就用 Google 的吧 "8.8.8.8"
$ exit
設定完成應該就可以直接上網了

2010年12月9日星期四

Google App Engine 簡易筆記

最近在開發手機軟體,背後需要有資料儲存的支援,這時候就覺得 Google App Engine 算蠻不錯的,因為免費且給的資源夠多!如果用 Python 或是 Java 開發沒有問題,也可以接受它特有的 Datastore ,那 GAE 真的是不錯的選擇,來記一些之後還可能會用到的筆記。

Datastore:
其實 GAE 也是遵從 MVC 的架構來開發,加上有豐富的說明文件開發上並不困難,比較難以適應的就是 Datastore 的部份,因為它不是關聯式資料庫,所以一開始資料庫的規劃上就要有不同的規劃方式,像是沒有關聯鍵這種東西,查詢語法無法 join 、查詢結果無法 count 、無法大量刪除資料等等…,這邊只筆記使用方式,其他關於 GAE Datastore 的介紹很多了,就不贅述

from google.appengine.ext import db

class Pet(db.Model):
    name = db.StringProperty(required=True)
    type = db.StringProperty(required=True, choices=set(["cat", "dog", "bird"]))
    birthdate = db.DateProperty()
    weight_in_pounds = db.IntegerProperty()
    spayed_or_neutered = db.BooleanProperty()
    owner = db.UserProperty(required=True)
和一般資料庫的使用不同,不是先建立 Table ,而是先將 Model 建立出來,當第一筆資料輸入資料庫後 Table 就會自動產生了。上面的是官方文件的範例,比較要注意的是資料型態的部份,可以先查一下有哪些可以用的資料型態,有一些比較特別的資料型態其實很好用,像是 GeoPtProperty 表示地理位置等等…
Model 定義好之後,當需要建立資料時只要產生相對的實體就可以指定內容,如果定義時有加上 required=True 的參數則為必填的欄位,要在建立實體時就輸入。建立完成後使用 put() function 就會輸入到資料庫中了。
from google.appengine.api import users

pet = Pet(name="Fluffy",
          type="cat",
          owner=users.get_current_user())
pet.weight_in_pounds = 24
pet.put()
比較重要的是可以利用 ReferenceProperty 建立 One-to-One 的關係,下面範例假設一個人可以有很多隻寵物:
class Owner(db.Model):
    name = db.StringProperty(required=True)

class Pet(db.Model):
    name = db.StringProperty(required=True)
    owner = db.ReferenceProperty(Owner, collection_name='pets')

jeffean = Owner(name='Jeff Tsai')
jeffean.put()

pet1 = Pet(name="Jimmy", owner=jeffean)
pet1.put()

pet2 = Pet(name="Anfa", owner=jeffean)
pet2.put()

# 列出某個人所有的寵物
for pet in jeffean.pets:
    print 'name: %s' % pet.name
另外可以利用 ListProperty 來做 Many-to-Many 的關係,下面是用文章和標籤的關係當範例:
class Post(db.Model):
    title = db.StringProperty(required=True)
    tags = db.ListProperty(db.Key)

class Tag(db.Model):
    name = db.StringProperty(required=True)
   
    @property
    def posts(self):
        return Post.gql('WHERE tags = :1', self.key())

# 建立 entity
tag1 = Tag(name='food')
tag1.put()
tag2 = Tag(name='travel')
tag2.put()

post1 = Post(title='Blah')
post1.tags.append(tag1.key())
post1.tags.append(tag2.key())
post1.put()

# 從 Post 取得它所有標籤:
tags = db.get(post1.tags)
for tag in tags:
    print 'Tag: %s' % tag.name

# 查看某個標籤中有什麼文章:
for post in tag1.posts:
    print 'Title: %s' % post.title
雖然上面有用到查詢,但是還是寫一下查詢的說明。下面的範例展示如何對資料庫做查詢以及放入篩選條件,比較特別的是刪除資料也得像這樣做,無法像以往下 SQL 的 DETET 語法可以做大量刪除。
class Song(db.Model):
  title = db.StringProperty()
  composer = db.StringProperty()
  date = db.DateTimeProperty()

query = db.Query(Song)
query = Song.all() # 與上一行意思其實相同
query = GqlQuery("SELECT * FROM Song WHERE composer = 'Lennon, John'") # 也可以這樣
query = Song.gql("WHERE composer = 'Lennon, John'") # 這樣也可以
query.filter('title =', 'Imagine')
query.order('-date')
query.ancestor(key)
results = query.fetch(limit=5)
for song in results:
  print song.title
  
for song in query: # 和上一個迴圈相同意思但是沒限制數量
  print song.title
  song.delete()
另外每筆資料都有自己獨特的 key ,但是無法排序,雖然也會自動有 id 的欄位但是數字也不是壘加的,可以用來做選擇單一筆資料時的查詢依據,但要取得最新的 N 筆資料來排序,我自己的作法是用一個 modify = db.DateTimeProperty(auto_now = True) 的欄位,這樣輸入資料會自動記下現在的時間,方便我要取得最新資料時的篩選條件,或許有更好的方法但是我目前是這樣做啦。
最後來筆記一下取得 key 和 id 的方式:
query = Song.all()
for song in query:
  print song.key()
  print song.key().id()

Template 系統:
因為一個站內的網頁常常有很多重複的部份,像是 footer 或是選單,沒有 tempalte 每一頁都重複寫的話,一來麻煩二來要改的時候更麻煩!而 App Engine 支援的 template 系統有很多種,而 gae 預設的 webapp 架構中本來就包含了 django 的 template 系統了,官方的說明文件也是以此為範例:
import os
from google.appengine.ext.webapp import template

class MainPage(webapp.RequestHandler):
  def get(self):
    greetings_query = Greeting.all().order('-date')
    greetings = greetings_query.fetch(10)

    if users.get_current_user():
      url = users.create_logout_url(self.request.uri)
      url_linktext = 'Logout'
    else:
      url = users.create_login_url(self.request.uri)
      url_linktext = 'Login'

    template_values = {
      'greetings': greetings,
      'url': url,
      'url_linktext': url_linktext,
      }

    path = os.path.join(os.path.dirname(__file__), 'index.html')
    self.response.out.write(template.render(path, template_values))
後端程式碼的重點就是把資料裝入 dictionary 的資料結構裡面,並且用 template.render() 這個 function 把整個資料結構送給 template 的檔案就可以了,而不是直接用 response.out.write() 寫出。

按照以上範例,則要另外產生一個 index.html 的 template 檔案。基本上 template 也是一個 html 檔,但是在裡面可以用 {{ var }} 的方式將後方傳送來的變數給印出,也可以配合 django template 的 loop 和 filter 使用。和 PHP 不一樣的是,這裡插入的不是 Python 的程式碼,而是 django template 規範出來的語法,可以參考 django 的官方文件。另外 template 中可以自行定義出 block ,並且在其他的樣板檔案中繼承以及修改:

base.html:
<html>
  <body>
    {% for greeting in greetings %}
      {% if greeting.author %}
        <b>{{ greeting.author.nickname }}</b> wrote:
      {% else %}
       An anonymous person wrote:
      {% endif %}
      <blockquote>{{ greeting.content|escape }}</blockquote>
    {% endfor %}

    <form action="/sign" method="post">
      <div><textarea name="content" rows="3" cols="60"></textarea></div>
      <div><input type="submit" value="Sign Guestbook"></div>
    </form>

    <a href="{{ url }}">{{ url_linktext }}</a>

  </body>
</html>
繼承以上 template 並且修改部分區塊:
{% extends "base.html" %}

{% block title %}My amazing blog{% endblock %}

{% block content %}
{% for entry in blog_entries %}
    <h2>{{ entry.title }}</h2>
    <p>{{ entry.body }}</p>
{% endfor %}
{% endblock %}