2010年6月22日火曜日

iPhoneのCode Signingエラーを何とかする

三森です。今日はさっき遭遇したiPhone開発のトラブル・シューティングについて書きます。
他の人にもらったiPhoneのプロジェクトファイルを自分の実機にインストールするとき

Code Signing Identity 'Erica Sadun' does not match any valid, non-expired, code-signing certificate in your keychain.

のようなエラーが出ることがあります。ちなみに上のエラーは、iPhoneハックの女王エリカ様のサンプルコードをコンパイルしようとした時のものです。
このエラー対策をググると、XCodeのメニューでコード署名IDを自分のものに直せばいいのだという情報を見つけました。しかし自分の場合はそこを変えただけではうまくいかなかったので、直接プロジェクトファイルを見てみました。
プロジェクトファイルは拡張子.xcodeprojですが、これは実はディレクトリーになっていて、その中のproject.pbxprojというファイルにビルド設定などが含まれているようです。右クリックして「パッケージの中身を表示」で以下のように開けます。



project.pbxprojはテキストエディットなどで見ます。すると以下のような行があります。


"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer: Erica Sadun";

このCODE_SIGN_IDENTITYというところがエラーの原因でした。自分の実機に入れるときはここを

"CODE_SIGN_IDENTITY" = "iPhone Developer";

に変更すれば動くようになります。本来はメニューから変えられるはずなのでXCodeのバグかもしれません。

ところで、エリカ様のサンプル・プロジェクトは実に沢山あるので、いちいち直していくのは面倒です。なのでバッチを書きました。別にエリカ様のサンプルでなくても使えます。

USAGE:

$ sh modify_code_sign.sh <ディレクトリー>

として使うと、<ディレクトリー>以下全てのproject.pbxprojファイルのCODE_SIGN_IDENTITYを書き換えます。元ファイルに上書きするので一応使用前にバックアップしてください。もし壊れても責任は負えません:-)

以下、ソースです。

#!/bin/sh
#This file is modify_code_sign.sh

SPACE_ESCAPE="\*SPACE\*" #ファイル名の一時変更(for対策)
REPLACE=CODE_SIGN_IDENTITY
FILES=$(find $1 -name project.pbxproj | sed "s/ /$SPACE_ESCAPE/g")
for FILE in $FILES; do
FILE=$(echo "$FILE" | sed "s/$SPACE_ESCAPE/ /g")
echo $FILE
CONTENT=$(cat "$FILE" | awk 'BEGIN {c=1} /'$REPLACE'/ {if (c==1){c=0; print "CODE_SIGN_IDENTITY = \"iPhone Developer\";";}} !/'$REPLACE'/ {print $0}')
echo "$CONTENT" > ${FILE}
done

2010年6月15日火曜日

GAEとjavascriptでjsonをやり取り

2週間ぶりの三森です。
今日はjsonでのデータやり取りについてちょこっと書きたいと思います。
環境は、Google App Engine(python)とjQueryの使用を想定して書きます。

json形式はjavascriptのオブジェクトをほぼそのまま表現でき、かつコンパクトなので、
クライアントからサーバーに何か階層の深いデータを送る時に使うと楽です。
json形式で送るときは、普通POSTメソッドを使うと思います。
jQueryのajaxメソッドでは、dataTypeを"application/json"に設定すれば、
urlエンコードなどをしないで生のjsonを入れて送信できます。
オブジェクトをjsonの文字列にするにはjson2.jsを使うのが簡単です。
JSON.stringifyはエンコード、JSON.parseがデコードです。


var json = JSON.stringify({"a": {"b": {"c": ["d", "Hello json!"]}}}); //objをエンコードする

$.ajax({
async: true,
complete: function(){},
dataType: "application/json",
data: json, //JSON
processData: false,
error: function(XMLHttpRequest, textStatus, errorThrown){
//httpエラーの場合処理
}
success:function(data, dataType){
//成功した場合の処理
alert(JSON.parse(data)["b"]["c"][1]);
}
type: "POST",
url: "http://使っているドメイン/json"
});


GAEの方では組み込みで入っているsimplejsonモジュールを使えます。
pyhtonオブジェクトとjsonとの変換は、デコードがsimplejson.loads(str),
エンコードがsimplejson.dumps(obj)です。

下のコードでは、JsonApiというクラスにurlをルーティングしてそのなかでjsonを読み込み、
responseに返します。うまくいけば"Hello json!"というアラートが出るはずです。


import cgi

from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app
from django.utils import simplejson

class JsonApi(webapp.RequestHandler):
def post(self):
str = self.request.body
try:
loaded = simplejson.loads(str)
self.response.headers['Content-Type'] = "text/plain"
self.response.out.write(simplejson.dumps(loaded["a"]))
except:
self.response.set_status(404); #parse errorなどの場合

def main():
application = webapp.WSGIApplication([('/json', JsonApi)])
run_wsgi_app(application)

if __name__ == "__main__":
main()


以上です。記事にするほどの内容か分かりませんが、ドキュメントを読まないで
勘でやっていると意外に時間がかかってしまったりするので、ここにまとめておきました。

2010年6月11日金曜日

議事録

キクチです。

我々 Sphia メンバーは毎週土曜日に全体会議を実施しています。

しかし自分は今週来週と土曜に本業での仕事があるため、参加することができません。
アメリカにいる長谷も当然参加することができません。

しかし、会議内容は後から追いたいし、不在メンバーへ情報共有もしたい。
ここで議事録が重要になってきます。

今日は議事録についての話をしようと思います。


議事録を書く目的としては、2種類あります。

-----------------------
1. 情報の整理・共有
2. エビデンス
-----------------------

土曜の全体会議は内部会議なので、1 が目的となりますね。
アジェンダ、決定事項、決定経緯、残課題等が誰が見ても見やすくまとめられている必要があります。

会議に出席しなかった人が読んで理解できる書き方が重要だと思います。
途中でプロジェクトに参加したメンバーが後から見て分かるようにとか。

逆に、今の Sphia ではまだありませんが、クライアントとの会議の場合は、2 の議事録となってきます。
ここでの目的はエビデンス(証拠)ですので、後から言った言わないの問題にならないようにするために書きます。

究極のエビデンスとしては、一字一句逃さないテープおこしがありますね。
物凄い労力がかかりますけど。。


議事録書きも簡単にはできない重要なスキルだと思います。
見せ方、事実と意見の違いの整理、構成力等のスキルが必要となります。

今の Sphia の土曜の全体会議では、毎回当番制で議事録をとっていますので、全員でスキルを上げていきたいですね。

2010年6月10日木曜日

ファイナンシャルプランナー

先日、実家に帰ると、FP(ファイナンシャルプランナー)の参考書がありました。

母が「ファイナンシャルプランナーという資格を取ることにした~」と意気揚々と言っていました。

昨年はDSの英語脳?か何かにハマっていた母ですが、
今度はFPの勉強に勤しんでいるようです。
仕事とは全く違う分野の知識を吸収しようとする姿は見習いたいものです。

私も一緒にFPの勉強をしようかな~。

2010年6月4日金曜日

スタートアップとは

こんばんは。キクチです。

今日は企業のスタートアップに関する記事をいくつか紹介したいと思います。
ポジティブな話もネガティブな話もあります。


スモールビジネスを興して成功するための9条件
http://fukui.livedoor.biz/archives/3234527.html

立ち上げた途端にダメになるスタートアップ企業
http://www.seojapan.com/blog/立ち上げた途端にダメになるスタートアップ企業


検証することを忘れずに事業を進めていきたいと思います。

2010年6月2日水曜日

大プレゼン大会

企画部的場です!

早いものでもう水曜日ですね。最近一週間が本当に充実していて、飛ぶように過ぎていきます。


さて、先日キクチがブログに書いたように、土曜日はSphia全員によるプレゼン大会でした!

プレゼンを本気で作って、本気で発表するっていうのが、本当にビジネスコンテスト以来だったので緊張し、緊張を上回るほど楽しかったです。

前日はもちろん、夜が白むまで全員のスカイプが緑色でした(笑)

徹夜明けのハイテンションでした。


やってみて思ったのは

一つのものを作ろうとしている仲間でも、こんなにも考えていることが違うのか

と言う事でした。

コンセプトの解釈、想定する問題、ターゲット。。。

あらゆるものが、その人の”オリジナル”

でした。

非常に有意義な時間で、また、全員が一歩成長したような一日でした。

2010年6月1日火曜日

Wikipediaで言葉の距離を測る

火曜の三森です。僕は一応自分のブログもやっていますが、こんなに毎週続けるのは初めてです。もはや自由研究と化していますが、前回のWikipediaネタの続きをやってみたいと思います。

前回は、適当に選んだ単語のWikipediaのページにあるキーワードリンクを全て拾ってくる事をやってみました。今回はそのアイディアを使って、単語同士がどれぐらい関係しているのかを調べてみました。「友達の友達が6人先まで行くと世界中の人がつながる」というのは有名な話ですが、単語の場合はどうなっているのか?というのは興味深いところです。方法は単純で、スタートの単語とゴールの単語を決め、その間に必要なジャンプの数を数えます。ただし

・スタート、ゴールの単語にあたるWikipediaのページがなければアウト
・ページ・リクエスト回数の上限を越えてもゴールに着かない、またはどこにも行けなくなったらアウト
・「7月15日」、「1600年」のように数字を含むものはキーワードと見なさない(多過ぎるから)

という設定にしてあります。ジャンプの回数はAPIを叩きすぎないようにMAX_COUNTSで決めてあります。
この条件でいかに単語間距離を稼げるか・・・を対戦してみたら面白いかもしれません。
やり方は、ブログの下に貼ったソースコードをwikisurf.pyで保存し、コマンドラインから


$ ./wikisurf.py 単語1 単語2

または

$ python wikisurf.py 単語1 単語2

のように実行します。すると単語1から単語2に至るまでの距離が分かります。例えば「相対性理論」から「川端康成」までの距離は3ワードでした。

$ ./wikisurf.py 相対性理論 川端康成
No.1 Distance: 0 Word: 相対性理論
No.2 Distance: 1 Word: 翻訳
No.3 Distance: 1 Word: 相対性原理
...
No.243 Distance: 2 Word: ユダヤ人
No.244 Distance: 2 Word: フリッツ・ハーバー
No.245 Distance: 2 Word: コロンビア大学
川端康成 found!
245 words searched.
Distance from 相対性理論 is 3

このように245単語のリンクを調べた結果、コロンビア大学と関係していることが分かりました。また、Graphvizというソフトで読めるDOT言語形式で、単語の経路をグラフ化する機能も付けてみました。このグラフを作るにはコマンドを

$ python wikisurf.py 相対性理論 川端康成 ファイル名.dot

にします。

ただ、これは取得したデータのほんの一部分しか表示していません。全てをグラフ化すると、情報量が多すぎてGraphvizが落ちてしまうからです。このグラフは「川端康成」に「相対性理論」との最短距離と同じ距離で到達するノードだけを表示したものです。実際には「相対性理論」からあらゆるノードへの経路があります。

今回作ったのは、以下のプログラムです。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from xml.dom import minidom
import re, urllib, urllib2, sys, time

def encode(str):
return urllib.urlencode({"": str})[1:]

def quote(str):
return str.replace('"', '\\"')

SPECIAL = encode("特別:データ書き出し")
USER_AGENT = "Mozilla/5.0"
MAX_COUNTS = 1000

class Timer:
def __init__(self):
self.time = time.time()
def wait(self, sec):
diff = time.time() - self.time
if diff < sec:
time.sleep(sec - diff)
self.time = time.time()

class Counter:
def __init__(self):
self._count = 0
def count(self):
self._count += 1
return self._count
def getCount(self):
return self._count

TIMER = Timer()
COUNTER = Counter()

class KWQueue:
def __init__(self):
self.queue = []
def enqueue(self, elt):
self.queue.append(elt)
def dequeue(self):
if len(self.queue):
elt = self.queue[0]
del self.queue[0]
return elt
else:
return None

class KWMap:
def __init__(self):
self.map = {}
def hasKey(self, key):
return (key in self.map)
def getMap(self):
return self.map
def get(self, key):
if key in self.map:
return self.map[key]
else:
return None
def set(self, key, value):
if key:

class KWLinkMap(KWMap):
def add(self, key, value):
if self.hasKey(key) and key: #keyが空でない場合
self.map[key].add(value)
else:
self.map[key] = set([value])

def getWordsForKeyword(keyword):
opener = urllib2.build_opener()
opener.addheaders = [('User-agent', USER_AGENT)]

TIMER.wait(1)
try:
data = opener.open("http://ja.wikipedia.org/wiki/" + SPECIAL + "/" + encode(keyword))

lines = data.read()
dom = minidom.parseString(lines)
textDom = dom.getElementsByTagName("text")

if textDom: #記事がある場合
textData = textDom[0].firstChild.data
#print "Text data length: " + str(len(textData))
return set([w.split("|")[0].split("#")[0].encode("utf-8") for w in KW_PAT.findall(textData)])
except: #urlアクセスが失敗した場合
pass

return set() #記事がない場合

#GraphVizで表示できるdot形式のファイルを作る
def createDotFile(file, map, start, goal):

def loop():
key = queue.dequeue()
if not key:
return False

kid = nodeIds[key]

for word in map[key]:
if word: #空文字の場合は除く
if word in nodeIds:
wid = nodeIds[word]
else:
wid = "node" + str(len(nodeIds))
nodeIds[word] = wid

file.write("%s -> %s\n" % (wid, kid))
if (word not in words) and (word != start):
queue.enqueue(word)
words.add(word)

if start in map[key]: #このif文を省くとグラフが複雑になりすぎる
return False

return True

words = set([goal])
nodeIds = {goal:"node0"}
queue = KWQueue()
queue.enqueue(goal)

file.write("digraph g {\n")
file.write("graph [rankdir=LR]\n")

while loop():
pass

for key in nodeIds:
prop = "label=\"%s\"" % (quote(key))
if key == start:
prop += " color=red style=filled"
elif key == goal:
prop += " color=blue style=filled"
file.write("%s [%s]\n" % (nodeIds[key], prop))

file.write("}")


def main():

def loop():
word = kwQueue.dequeue()
if word:
level = kwLevelMap.get(word)
if cycle(word, level + 1):
return False
else:
print "A dead end!"
return False
return True

def cycle(searchWord, level):
print "No.%d Distance: %d Word: %s" % (COUNTER.count(), level-1, searchWord)
words = getWordsForKeyword(searchWord)
if COUNTER.getCount() >= MAX_COUNTS:
print "Reached to maximum access counts %d" % (MAX_COUNTS)
if goalWord in words:
kwLevelMap.set(goalWord, level)
kwLinkMap.add(goalWord, searchWord)
return True
else:
for word in words:
if word and (not kwLevelMap.hasKey(word)): #空白文字は除外
kwQueue.enqueue(word)
kwLevelMap.set(word, level) #最も早くでたlevelを採用
kwLinkMap.add(word, searchWord)
return False

file = None # dot file

try:
if len(sys.argv) > 2:
startWord = sys.argv[1]
goalWord = sys.argv[2]
if len(sys.argv) == 4: # When create dot file
file = open(sys.argv[3], "w")
else:
raise Exception
except Exception, e:
if e:
print e.message
print "USAGE: " + sys.argv[0] + " start-word goal-word [dot-file-name]"
exit()

kwLevelMap = KWMap()
kwLinkMap = KWLinkMap()
kwQueue = KWQueue()

kwLevelMap.set(startWord, 0)
kwLinkMap.add(startWord, None)
kwQueue.enqueue(startWord)

if len(getWordsForKeyword(goalWord)) == 0:
print "Cannot find " + goalWord + " on Wikipedia"
else:
# main loop
while loop():
pass

if kwLinkMap.hasKey(goalWord):
print goalWord + " found!"
print str(COUNTER.getCount()) + " words searched."
print "Distance from " + startWord + " is " + str(kwLevelMap.get(goalWord))

map = kwLinkMap.getMap()
if file:
createDotFile(file, map, startWord, goalWord)
else:
print "You Lose!"


if __name__ == "__main__":
main()


結局テーマの結論はまだ出ていないのですが、スタートとゴールの両方から攻めていった方がずっと効率的なことに後で気づきました。