2010年10月31日日曜日

MacBook Air 11にぴったりなインナーケース

2010年10月21日 日本時間午前2時に催された「Back To The Mac」にて発表された新MacBook Air。初代MacBook Airが発表された時の感動を上回る衝撃に、やっぱりポチってしまいました。
オンラインのアップルストアにて11インチモデルをCTO(1.6GHz/4GB RAM/ 128GB)にて入手しましたが、最適なインナーケース(またはケース)がなくて結構困ってました。
(仕方ないので裸のままバッグにInしていました。。。)

基本的にいつでもマシンを持ち歩いているので、MacBook Pro 13(2.53GHz/8GB RAM/500GB-7200RPM)の代わりにうってつけなAirを早く外でも安心して使い倒したい!と思い、いろいろと探しました。

そんな中で見つけたのが今回ご紹介するEasternShape社の「E.S. InnerCase for MacBook 13"」です。
もともとMacBook Pro 13インチおよびMacBook 13インチ用のものなので、MacBook Air 11に完全対応というわけではないのですが、画面サイズの割に横幅のあるAirには(自分としては)ぴったりだと思いました。


原宿にあるアシストオンさんで購入。7,350円でした。
スペーサーとしてオレンジ色の生地で包まれたスポンジ(?)がついてます。
このケースは前・後ろ共に樹脂ボードが仕込まれているため衝撃に強そう!
MacBook Airは薄いため、たくさん荷物を入れたバッグで
心配になる「圧力で本体折れないかな?...」という不安が

お店でこの板の硬さを確認してみて完全に拭えました。
(本当に大丈夫かどうかはこれから試していきます!)


横幅は約25cm弱くらい


縦幅は36cmほどでしょうか。


MacBook Air 11と並べるとこんな感じです。


MacBook Air11をそのまま入れるとちょっとゆるい。


MacBook Air11を入れてみました。底の方に押し込まずに撮影。
右側に隙間ができるので.......


付属のスポンジ(?)素材をはめ込んでみました。なんとぴったり!


底の方に押し込んでみます。うん。これでゆるくならない。
でも底の方に押し込んでフタをすると上部に隙間ができるので
何か適当なスポンジをまた探してみようかと思います。


ちなみにMacBook Air11との厚さ比較はこんな感じです。


思いがけず素晴らしいケースに出会いました。ありがとう!アシストオンさん




ちなみに普段MacBook Pro 13を運ぶのに使用しているバッグがコレ
これに
・MacBook Pro 13 (Core2Duo 2.53GHz / 8GB RAM / 500GB-7200RPM)
・iPad(32GB/3G&Wi-Fi)
・Android HTC Desire
・E-mobile Pocket Wi-Fi
Western Digital My Passport 
(FW800/400/USB 2.5インチ外付けハードディスク)
・その他、FeliCaカードリーダ、USBケーブル、FireWire800ケーブル、など
を突っ込んでいます。
MacBook ProがMacBook Air 11になるだけでもかな〜り軽くなるので本当に助かります。


さて次はパームレストに貼るフィルムだなぁ〜。
パワーサポートさんとかMicroSolutionさんとか早く出さないかな〜。
よろしくお願いします!

2010年8月24日火曜日

IE対策 - クリックEventの取得について

JavaScriptを使用して、イベント(Event)を取得し、そのイベントの動作を
制御したい場合に以下の点でハマることがあります。

・確認画面(windows.confirm)やモーダルウィンドウ($.modal)などを使用して
ダイアログを開いた状態のまま処理を停止したい時に、停止すべき"Event"を取得することができない

実際のソースコードでは以下のような場合にInternet Explorer 7および8で上手く動きません。

【例1】

            function showDetail( event , kwds , argv ){
                event.preventDefault();    //イベントを取得してそのイベントを一時的に止めておくための処理
            }

            <button onclick="showDetail(event,message,value)">クリック</button>


上記のソースで上手く動かないポイントは「event.preventDefault()」を実行した際に、
引数で「event」が渡っているにも関わらず、IEのJavaScript判定ではこのeventを取得
出来ない、というところです。

これを正しく動作するようにするためには以下のように書き換える必要があります。


【例1改】

            function showDetail( event , kwds , argv ){
                $("#clickme").click(function(event){
                    event.preventDefault();    //イベントを取得してそのイベントを一時的に止めておくための処理
                });
            }

            <button id="clickme" onclick="showDetail(event,message,value)">クリック</button>


上記のように「あるidをクリックした」という事実を「.click」や「.submit」を使用して
その引数にeventを渡してあげることでIE7&8でもSafariでもFirefoxでもChromeでも正しく動くようになります。

2010年8月13日金曜日

Google Calender API | feedの取得

Google Calendar の予定などをApp Engine上に読み込むためには
Google Data Library(GDATA)というものを使用します。

【GDATA Python Client Library】のダウンロード
http://code.google.com/p/gdata-python-client/
2010年8月13日現在、最新版は2.0.11です。

→ファイルをダウンロードしたら展開し、「src」フォルダ内の
「gdata」「atom」の2つのフォルダを、自身のアプリケーションの
プロジェクトフォルダのルートにドラッグ&ドロップで移動してください。


このデータの取得方法において、まず始めにやらなくてはならないことが、
「ユーザの許可を得る」ということです。

このユーザの許可を得る方法としては

・アクセスするユーザのアカウント名/パスワードをあらかじめ
    ソースにハードコーディングで仕込んでおく「Client Login」

・ユーザのデータへアクセスする必要がある場合にユーザに対して
    直接得る「AuthSub」

の2つの認証方法があります。※1

アクセスするユーザが特定されている場合には「Client Login」を使用したほうが
実装は簡単です。
しかし複数のユーザが存在することが前提となるGoogle Apps環境などに合わせて開発を
行う場合には「AuthSub」を使用しなくてはなりません。

ここでは
「AuthSubによる認証トークンの取得とデータ(Feed)の取得」
について説明します。




【AuthSubによる認証トークンの取得とデータの取得】


# -*- coding: utf_8 -*-
#!/usr/bin/env python

############################# Import  Library ##############################
from google.appengine.ext import webapp
from google.appengine.ext.webapp import template
from google.appengine.ext.webapp import util
try:
  from xml.etree import ElementTree # for Python 2.5 users
except ImportError:
  from elementtree import ElementTree
import gdata.calendar.service
import gdata.service
import atom.service
import gdata.calendar
import gdata.auth
import gdata.alt.appengine
import atom
import getopt
import sys
import string
import time
import cgi
import logging
import codecs
import os
############################# Import Library ##############################


################################ Class #################################
#url:/
class MainHandler(webapp.RequestHandler):
    def get(self):
        # Googleサービス(Scope)への接続許可を求めるためのリンクを生成する。
        authSubUrl = GetAuthSubUrl();
        a_url = u'Googleサービスへアクセス' % authSubUrl
        self.response.out.write(str(a_url.encode('utf-8')))

#url:/calendar_view
class CalendarViewer(webapp.RequestHandler):
    layout_file = 'calendar.html'
    
    def __init__(self):
        # calendar_clientにGoogleカレンダーサービスをセットし、初期値の設定を行う。
        self.calendar_client = gdata.calendar.service.CalendarService()
        gdata.alt.appengine.run_on_appengine(
            self.calendar_client,store_tokens = True, single_user_mode = True
        )
        
    def get(self):
        # Googleサービス(Scope)への接続許可を求めるためのリンクを生成する。
        authSubUrl = GetAuthSubUrl();
        a_url = u'Googleサービスへアクセス' % authSubUrl
        
        token_request_url = None
        cal_list          = []
        cal_list_detail   = []
        # URLからトークンを取得する
        auth_token = gdata.auth.extract_auth_sub_token_from_url(self.request.uri)
        if auth_token:
            try:
                # 取得したトークンをセッショントークンにアップグレードして
                # AuthSubTokenとしてセットする
                self.calendar_client.SetAuthSubToken(
                    self.calendar_client.upgrade_to_session_token(auth_token)
                )
            except gdata.service.TokenUpgradeFailed:
                self.redirect('/')
                return
            
            # アップデートされたトークンがセットされたcalendar_clientで
            # カレンダーの予定を取得するGetCalendarEventFeed()を実行する
            feed = self.calendar_client.GetCalendarEventFeed()
            
            # feed.entryにカレンダーのデータの配列が入っているのでループで取得する
            # ここでは例としてキー・バリューを取り出します。
            #(予定のタイトル開始日時・終了日時が含まれた配列がcal_listにセットされます。    
            cal_list = [[i.title.text,i.when[0].start_time,i.when[0].end_time] for i in feed.entry]
     
        ValueList = {
        'a_url':a_url,
        'cal_list':cal_list        # html上でループして値を取得・表示できる
        }

        fpath = os.path.join(os.path.dirname(__file__),'layouts',self.layout_file)
        html = template.render(fpath,ValueList)
        self.response.out.write(html)

################################ Class #################################



############################# Custom Method ############################

# Googleサービス(Scope)への接続許可を求めるためのリンクを生成する。
def GetAuthSubUrl():
    next = 'http://calender-retrieve-sample.appspot.com/calendar_view'
    scope = 'http://www.google.com/calendar/feeds/default/allcalendars/full'
    secure = False
    session = True
    calendar_service = gdata.calendar.service.CalendarService()
    return calendar_service.GenerateAuthSubURL(next, scope, secure, session);

############################# Custom Method ############################


################################# main #################################
def main():
    application = webapp.WSGIApplication([
        ('/', MainHandler),
        ('/calendar_view',CalendarViewer)
    ],debug=True)
    util.run_wsgi_app(application)

if __name__ == '__main__':
    main()
################################# main #################################

2010年8月8日日曜日

iPhone/iPad向けWebアプリ開発時のローカルファイルキャッシュ

iPhone/iPad向けのWebアプリケーションを開発する際、SenchaやjQTouchなどの
ライブラリーを活用してWebアプリケーションをあたかもネイティブアプリのように
振舞わせるわけだが、その際に避けて通れないのが
「ライブラリ・CSSなどのロード時の速度低下」です。

ライブラリやCSSなどのファイルのロードがかなり時間を食ってしまい、
利用者に「このページ遅いな」と思われてしまいます。

それを防ぐためには
「iPhone/iPadのフラッシュメモリ内にJavaScriptやCSSなどのファイルを保存させる」
という設定を行ないます。

この設定には「manifest」ファイルをサーバ側で用意し、manifestファイルには、
「どのファイルをローカルキャッシュさせるか」を記述してあげる必要があります。


以下に記述例を記載します。

使用するファイルの構成は以下の通りとなります。

【プロジェクトフォルダ内】
CSSファイル
/css/js/sencha_icons/mystyle.css
(元々app.yamlに以下の
- url: /css
  static_dir: css
という静的ファイルディレクトリ指定の設定をしています。)

JavaScirptファイル
/css/js/sencha_icons/ext-touch.js
/css/js/sencha_icons/index.js

作成したmanifestファイル
/css/js/sencha_icons/sitefile.manifest


1.まずはじめに、使用するHTMLファイル内のソースに


と記述します。
(example.manifestはexampleという名前のマニフェストファイルとなります。)


2.app.yamlに以下の内容を記述します。




- url: /css/js/sencha_icons/sitefile.manifest                        →所在URL
  static_files: /css/js/sencha_icons/sitefile.manifest          →ファイルの在処
  mime_type: text/cache-manifest                                        →mime_typeの指定
(↑これがないとマニフェストファイルとして認識されません。)
  upload: /css/js/sencha_icons/sitefile.manifest                →ファイルのアップロード先



3.sitefile.manifestファイル(ファイル名はsitefileでなくてもOK)を作成し、以下の内容を記述します。

CACHE:
mystyle.css
ext-touch.js
index.js

4.デプロイしてiPhone/iPadのブラウザで再読込する。


これで上記の3つのファイルがiPhone/iPadのローカルに保存され、
ページのロードが高速になります。



参考URL: http://www.html5rocks.com/tutorials/appcache/beginner/

2010年7月2日金曜日

【Python】デコレータをやってみる

Pythonを始めてから早9ヶ月。
最近ものすごくPythonが楽しい。

そこで今日は、ちょっと前から気になっていた「デコレータ」に挑戦してみようかな。


まず「デコレータ」(decolator)について調べてみると、言葉の意味から
分かるように「装飾する」ものとして動くらしい。
数カ月前に知り合いのプログラマが「デコレータいいよ」って言ってた
ときは全然分からなかった(何に使うのか/自分にとって必要なのか)が、
やってみるとこれは凄い!


僕は今回以下のサイトで勉強させてもらいました。
ありがとうございますm(_ _)m

http://www.ianlewis.org/jp/appengine-maintenance-page
http://www.gesource.jp/weblog/?p=3498
http://satoshi.blogs.com/life/2009/11/python入門デコレータとは.html
http://jutememo.blogspot.com/2008/10/python-1_09.html



前々から自分の書くコードはなんとなく他の人のものよりも長い!
おんなじようなことをいろんなところでやってる!
もっとまとめられないのか??

そんな悩みを解決できそうなのが「デコレータ」。


というわけで早速やってみる。



今回は、
「ログイン状態をチェックし、ログインしていない場合にはログインページへ、admin権限でログインしていない場合にはログイン画面へリダイレクト」
というのをやる。

書いてみたのが以下のメソッド


#ログイン状態をチェックするデコレータ
def check_login_status(req):
 def wrapped(request, *args, **kwargs):
 #ログインしていない場合はログイン画面へリダイレクトする
 if not user_account:
  request.redirect('/login')
  return
 #管理者権限を持っていない場合はユーザ向けページへリダイレクトする
 if not users.is_current_user_admin():
  request.redirect('/user')
  return
 return req(request, *args, **kwargs)
return wrapped




実際には以下のように使用している。

class AdminMainHandler(webapp.RequestHandler):
 layout = 'admin.html'
 @check_login_status
 def get(self):


ポイントは

・クラス内のメソッドの直前に「@check_login_status」という形で
デコレータを呼び出す。

・デコレータは「def get(self)」自体を受け取り(reqとして使用)、
 直後の「def get(self)」の「self」を受けて(requestとして使用)、
 ログイン状態を判定し、リダイレクトなどの処理を行う。

という感じだと思う。




まぁここで気になったのはデコレータの中で使っているwrappedの
*args, **kwargs」だが、これについても別途書き綴ってみたいと思う。



今日は疲れたのでちょっと休む。