2015年2月23日月曜日

Xcodeでgithubを利用

ネットでよく見る話題ですが、サクッといくこともあれば、苦労している記事もあります。自分の場合は苦労しました ^^;) どうもsshキーの登録を手動でしないといけないようなんですが、その辺が詳しく書いてあるものがありません。基本的にはコマンドラインで一回手動でやれば自動的にキーは記録してくれますが、ちょっとメモしておきます。(まあ、こんなのは昔のeclipseでもよくありました。少し前から全部GUIでできるようになったと思います。)

1.Xcodeでプロジェクトの作成
とりあえず何かプロジェクトを作成しましょう。あと重要なことですが、最初にローカルでGit管理するかという問い合わせがひっそりとあります。(デフォルトがオフです)これのチェックを入れておくことを忘れずに。(後からGit管理する方法もあるようですが、Xcodeのバージョンがあがるとどうなるか分からないので、面倒くさければ新規に作り直した方が早いと思います)

2.remoteリポジトリの登録
まずgithubに管理用のリポジトリを作成しておきます。(これ重要。いろんなツールを見てきましたが、新規に作成するリポジトリまで作成してくれる統合ツールは見た事ありません。おそらくgithubが新規リポジトリ作成のAPI(URL経由のね)は作ってないんでしょう。そうしないと、いたずらされかねませんしね。)
XcodeのSourceControlからConfigureで、remoteとしてgithubの先ほど作成したリポジトリを登録しておきます。

3.最初のcommitをローカルにする
とりあえず、ローカルのリポジトリにcommitしておきます。

4,remoteにpushする
ここが一番の難関です。Xcodeから行おうとすると、いかにもIDとパスワードを聞いてきて、うまくいきそうに見えますが失敗します。最初はプロジェクト以下の.gitにsshパスワードが入っていませんから。
ツールを使ってもいいんですが、最初だけは手動でgitコマンドでpushします。

$ git push -u origin master

このときgithubのユーザID、パスワードを聞かれますがここでsshキーが.git以下に登録されますから、あとはXcodeでも自由に操作できるようになります。

毎回思うことですが、gitって便利だけどどうしてもコマンドラインからの入力が避けれないな〜。(コマンドラインでbranchの操作とか、差分見ようと思うと地獄なんですが、どうして公式にはGUI作ってくれないのか。そのあたりはLinusさんの主義なんですかね。GUIあたりは周りに任して、自分は本質の部分だけに集中するとか。自分はコマンドライン、そんなに気にならないほうだからいいけど、若いもんは使ってくれないよ。emacsで編集、make、debugまでするのは会社ではもう自分だけじゃなかろうか。今時のは、統合開発ツールあるのが当たり前と思ってるし。自分としてはツールの裏で何動いているのかわからない方が怖いんだが。)

2015年2月22日日曜日

c++でcurlを使う:couchDBへのアクセス

c++でcouchDBにアクセスしようと思ったら、curlライブラリを組み込んでやりましょう。今回はとりあえず自分のメモ用にサンプル載せておきます。

#include <string> 
#include <iostream> 
#include <curl/curl.h> 

using namespace std;

size_t callbackWrite(char *ptr, size_t size, size_t nmemb, string *stream)
{
  int dataLength = size * nmemb;
  stream->append(ptr, dataLength);
  return dataLength;
}

int main()
{
  CURL *curl;
  CURLcode ret;

  curl = curl_easy_init();
  string chunk;

  if (curl == NULL) {
    cerr << "curl_easy_init() failed" << endl;
    return 1;
  }

  curl_easy_setopt(curl, CURLOPT_URL, "http://localhost:5984/environment/");
  curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, callbackWrite);
  curl_easy_setopt(curl, CURLOPT_WRITEDATA, &chunk);
  ret = curl_easy_perform(curl);
  curl_easy_cleanup(curl);

  if (ret != CURLE_OK) {
    cerr << "curl_easy_perform() failed." << endl;
    return 1;
  }

  cout << chunk << endl;

  return 0;
}

couchDBにenvironmentというデータベースを事前に作っておきます。これでうまくレスポンスが返ってきました。JSON形式で返ってきますので、後は前回のpicojsonでハンドリングするだけです。

2015年2月21日土曜日

JSONデータのハンドリング:c++

さてこれまでブラウザ上でJSONデータの扱いを調べてきましたが、これらは入力データの編集用と思って調べてきました。こっからが本題のc++によるJSONデータの扱いです。

もともと、本職はc++プログラムがほとんどなんで、いつも入出力のGUIに悩まされていました。それをCouchDBで楽したいというところで、色々調べていましたがだいたい目処がついてきたので、さてそれをc++プログラムでどうやってハンドリングしたらいいのか?というところまできました。

c++でJSONのパーサはいくつか見つかりましたが、picojsonがヘッダファイル一つというシンプルさでなかなか秀逸です。早速これを試してみました。

まず試験データとしてのJSONファイル(test3.json)です。

{"message":"success",
  "number":[1, 2],
  "person":{"name":"hoge", "age":38},
  "follow":[
 {"name":"hoge", "age":38},
 {"name":"huga", "age":42}
  ],
  "returnCode":0}

わざと複雑な階層構造を持たせています。このファイル名を引数として動くサンプルが以下です。

#include 
#include 
#include "picojson.h"

int main(int argc, char** argv)
{
  picojson::value v;
  std::ifstream infile;
  std::string inStr = "";

  infile.open(argv[1]);
  while (!infile.eof()) {
    std::string work = "";
    infile >> work;
    inStr += work;
  }
  infile.close();

  std::string err = parse(v, inStr);

  if (!err.empty()) {
    std::cerr << picojson::get_last_error() << std::endl;
    return 1;
  }
  
  // dump json object
  std::cout << "---- dump input ----" << std::endl;
  std::cout << v << std::endl;

  // accessors
  std::cout << "---- analyzing input ----" << std::endl;
  if (v.is()) {
    std::cout << "input is an object" << std::endl;
    //const picojson::object& o = v.get();
    picojson::object& o = v.get();
    for (picojson::object::const_iterator i = o.begin(); i != o.end(); ++i) {
      std::cout << i->first << "  " << i->second << std::endl;
    }
    
    std::cout << std::endl;

    // 一番外側のobjectの取得
    std::string& s1 = o["message"].get();
    //picojson::array& s1 = o["number"].get();
    std::cout << s1 << std::endl;

    std::cout << std::endl;

    // 値の変更
    o["message"] = (picojson::value)std::string("fail");

    // データの追加
    o.insert(std::map::value_type("piyopiyo", 10.0));

    for (picojson::object::const_iterator i = o.begin(); i != o.end(); ++i) {
      std::cout << i->first << "  " << i->second << std::endl;
    }
    
    std::cout << std::endl;

    // 内部のobject値の取得
    picojson::object& o1 = o["person"].get();
    std::cout << o1["name"].get() << " " << o1["age"].get() << std::endl;

    std::cout << std::endl;

    // array値の取得
    picojson::array& a1 = o["follow"].get();
    o1 = a1[1].get();
    std::cout << o1["name"].get() << " " << o1["age"].get() << std::endl;

    std::cout << std::endl;

    // serialize(JSON形式の文字列に戻す。JavaScriptのstringify()と同じ。)
    std::string str = picojson::value(o).serialize();
    std::cout << str << std::endl;

  }
  
  return 0;
}

実行すると以下の様な出力をだしてきます。

---- dump input ----
{"follow":[{"age":38,"name":"hoge"},{"age":42,"name":"huga"}],"message":"success","nu\
mber":[1,2],"person":{"age":38,"name":"hoge"},"returnCode":0}
---- analyzing input ----
input is an object
follow  [{"age":38,"name":"hoge"},{"age":42,"name":"huga"}]
message  "success"
number  [1,2]
person  {"age":38,"name":"hoge"}
returnCode  0

success

follow  [{"age":38,"name":"hoge"},{"age":42,"name":"huga"}]
message  "fail"
number  [1,2]
person  {"age":38,"name":"hoge"}
piyopiyo  10
returnCode  0

hoge 38

huga 42

{"follow":[{"age":38,"name":"hoge"},{"age":42,"name":"huga"}],"message":"fail","numbe\
r":[1,2],"person":{"age":42,"name":"huga"},"piyopiyo":10,"returnCode":0}

うまく動いていますね。階層構造の扱いも、値の変更も思い通りになります。
ちょっと面倒くさいのが、いちいちpicojson::value形式で読みこんだ後、データの操作のためにpicojson::object形式にしないといけないこと。JavaScriptでも同じような考え方でしたが何でですかね?picojsonの実装はヘッダファイル一つなので、少し読んでみると、picojson::valueではvector形式で読み込んでいるようですが、すぐにmap形式にしてしまいます。確かにこれならアクセスするのが簡単で、処理速度も早いんですがデータ項目の順番がkeyでソートされてしまい順番が変わってしまいます。JSONとしては問題ないんですが、デバッグ時に少し悩みそうです。

後、このサンプルではparse()関数を使っていますが、picojson::valueは、>>を使って直接読み込むことができます。わざわざサンプル用に、stringでファイルを読み込み、parse()関数を使うように変更しました。こっちの方がエラーの扱いがわかりやすいからなんですがね。

picojsonなかなかいいんですが、一つ難点を言えば、c++のtemplate機能を使い倒しているため、エラーが出てくると、わけのわからないエラーメッセージを出してくるのがつらいです。c++の機能なんで、しかたないんですが。


2015年2月14日土曜日

JSON形式のハンドリング

couchdbとアクセスするためJSONを扱えるよう、JavaScript/jQueryによるJSONの処理方法を調べてます。

$.ajax()でGETすると、JSON形式でcouchdbからの応答をObjectの型式で読み込みます。データにアクセスするのは楽なのですが、デバッグ用に文字列で全体を表示しようと思うとすごいめんどくさい。調べてみると、stringify()という関数がよくでてきます。以下の様なスプクリプトをstringify.jsとか保存しておいてjQueryを拡張して使います。

(function($) {
 $.extend({
  stringify: function stringify(obj) {
   
   var t = typeof (obj);
   if (t != "object" || obj === null) {
    // simple data type
    if (t == "string") {
     obj = '"' + obj + '"';
    }
    return String(obj);
   }
   else {
    var n, v, json = [];
    var arr = (obj && $.isArray(obj));
     
    for (n in obj) {
     v = obj[n];
     t = typeof(v);
     if (obj.hasOwnProperty(n)) {
      if (t == "string") {
       v = '"' + v + '"';
      }
      else if (t == "object" && v !== null) {
       v = jQuery.stringify(v);
      }
      json.push((arr ? "" : '"' + n + '":') + String(v));
     }
    }
    return (arr ? "[" : "{") + String(json) + (arr ? "]" : "}");
   }
  }
 });
})(jQuery);

皆んな困ってるようで、ググってると結構いろんな所ででてきます。今度は、$.ajax()でPUTしようとすると、送信データの形式がObjectではなくJSON形式の文字列で渡さないといけません。 これではjQueryにstringify()がなぜ実装されていないのか訳がわかりません。 さらにググると、JavaScript1.7からJSON.stringify()というのが実装されているのがわかり、納得しました。結局、ライブラリとして作ったファイルは一瞬で必要なくなったかの様に思えたのですが、話はこれで終わりませんでした。

ところがどっこい、IE8だとこのJSONというオブジェクトが実装されてません。ブラウザに実装されているJavaScriptのバージョンをチェックするスクリプトがあったので、調べてみると1.3です!(IEだと正確にはJScriptですが)会社関係だとシステムが対応してるのがまだIE8というところが多いと聞いています。(XPがダメになった時に、IE6からやっとあがったばっかりという所が多いと思います) 結局、当面はstringify.jsを使うようにしたほうがいいようです。
ちなみに以下に、そのスクリプトを示します。


<SCRIPT Language="JavaScript">
// Java Script 1.0以降で動作する関数
function funcGetVer() { return "1.0" ; }
</SCRIPT>

<SCRIPT Language="JavaScript1.1">
// Java Script 1.1以降で動作する関数
function funcGetVer() { return "1.1" ; }
</SCRIPT>

<SCRIPT Language="JavaScript1.2">
// Java Script 1.2以降で動作する関数
function funcGetVer() { return "1.2" ; }
</SCRIPT>

<SCRIPT Language="JavaScript1.3">
// Java Script 1.3以降で動作する関数
function funcGetVer() { return "1.3" ; }
</SCRIPT>

<SCRIPT Language="JavaScript1.4">
// Java Script 1.3以降で動作する関数
function funcGetVer() { return "1.4" ; }
</SCRIPT>

<SCRIPT Language="JavaScript1.5">
// Java Script 1.3以降で動作する関数
function funcGetVer() { return "1.5" ; }
</SCRIPT>

<SCRIPT Language="JavaScript1.6">
// Java Script 1.3以降で動作する関数
function funcGetVer() { return "1.6" ; }
</SCRIPT>

<SCRIPT Language="JavaScript1.7">
// Java Script 1.3以降で動作する関数
function funcGetVer() { return "1.7" ; }
</SCRIPT>

<SCRIPT Language="JavaScript1.8">
// Java Script 1.3以降で動作する関数
function funcGetVer() { return "1.8" ; }
</SCRIPT>

<SCRIPT Language="JavaScript1.9">
// Java Script 1.3以降で動作する関数
function funcGetVer() { return "1.9" ; }
</SCRIPT>

<SCRIPT Language="JavaScript2.0">
// Java Script 1.3以降で動作する関数
function funcGetVer() { return "2.0" ; }
</SCRIPT>

<SCRIPT Language="JavaScript">
// Java Script のバージョンを取得して表示する関数
  function funcVerDisp()
{
var strVersion = funcGetVer() ;
alert( 'このブラウザーの Java Scriptのバージョンは\n\t' + strVersion + ' です。\n' ) ;
}
</SCRIPT>

なんか同じ関数を何度も定義してますが、SCRIPTでのlanguage指定がキモです。ここで対応するバージョンを指定していますが、同じ関数定義をすると最後のものが有効になります。そこでhtml内のbuttonからonclickでfuncVerDisp()をCallしてやるようにすれば、バージョン名をダイアログで表示してくれます。ちなみに私が普段使いしているchromeは1.7でした。(最初にこのテクニックを紹介していたサイトでは、SCRIPTの中で、Type="javascript"というtype属性をつけていましたが、それではうまく動きませんでした。調べてみるとtype属性はスクリプトバージョンには関係しないので、返ってそれ以降の指定が無効になってしまうようです。)

結局、stringify()の使い方ですが、以下のように使います。

var jsonStr = '{"message":"success", "returnCode":0}';
json = $.parseJSON(jsonStr);

var str = $.stringify(json);

最初にJSON形式の文字列をjQueryの関数でObject形式にしていますが、JavaScript1.7だと、JSON.parse()という同様のものがあります。しかしこれもIE8では使えないので、色んな環境で動かしたいならjQueryにまかせた方がいいようです。