新井 元基

Copyright(c)2009 Cyworks Inc. All Rights Reserved.
前提

• 以下については理解しているものとして進め
  ます
 Opensocialの仕様
 Javascript, jqueryの文法
 Ajaxの仕組み




            Copyright(c)2009 Cyworks Inc. All Rights Reserved.
オープンソーシャルアプリ?

• Javascript API(PC向け)
  • クライアントサイドでJavascriptをばりばりコーディ
    ングする必要あり
  • 普段サーバサイドでプログラムを書いているWEB
    屋さんにとっては、敷居が高い?過去の資産=
    ソースコードを活用できない?




            Copyright(c)2009 Cyworks Inc. All Rights Reserved.
オープンソーシャルアプリ?

• No!!
  →サーバサイドアプリケーションの土俵に持ち込む




         Copyright(c)2009 Cyworks Inc. All Rights Reserved.
どうやって?

• Iframe
  ○完全にサーバサイド
  ×Javascript APIにアクセスできない
• Ajax
  △一部クライアントサイド
  →クライアントサイドは可能な限りいじらなくて済む
   ように、毎回共通で使うクライアントプログラムを
   用意

           Copyright(c)2009 Cyworks Inc. All Rights Reserved.
具体的に

• アプリ領域全体を<div>で囲み、div領域全体
  をAjaxで書き換える
• アプリ起動時にJavascript APIでOWNER,
  VIEWER, FRIENDS(OWNER)などのSocial Data
  を取得してサーバへPOST
• サーバ側のセッションはURLパラメータでセッ
  ションIDを引き回すことで維持
• アクティビティの送信はサーバレスポンスで
           Copyright(c)2009 Cyworks Inc. All Rights Reserved.
div領域をAjaxで書き換える

• <div id="gadget_container"></div>
• gadgets.io.makeRequest でサーバからHTML
  を取得
• $(“#gadget_container”).html(<取得した
  HTML>);




           Copyright(c)2009 Cyworks Inc. All Rights Reserved.
gadget.xml
<Module>
 <ModulePrefs title="test">
  <Require feature="opensocial-0.8"/>
  <Require feature="views"/>
  <Require feature="dynamic-height"/>
 </ModulePrefs>
 <Content type="html">
  <script src="http://xxxx/jquery-1.3.2.min.js" type="text/javascript"></script>
  <script src="http://xxxx/jquery.mixigadget.js" type="text/javascript"></script>
  <link href="http://xxxx/style.css" rel="stylesheet" type="text/css" />

  <script type="text/javascript">
   (function(){
     $('#gadget_container').mixigadget();
   })();
  </script>

  <div id="gadget_container"></div>
 </Content>
</Module>

                             Copyright(c)2009 Cyworks Inc. All Rights Reserved.
jquery.mixigadget.js
;(function($) {
   var name_space = 'mixigadget';
   $.fn[name_space] = function(config){
     config = jQuery.extend({
        session_id: "",
        session_key: "",
        base_url: "",
        view_name: gadgets.views.getCurrentView().getName()
     },config);

    gadgets.util.registerOnLoadHandler(gadgetInit);

    function gadgetInit() {
      switch(config.view_name) {
      case 'canvas':
        canvasInit(); break;
      }
    }

    function canvasInit() {
      klass.requestContainer('/test.html‘);
    }



                                 Copyright(c)2009 Cyworks Inc. All Rights Reserved.
klass.requestContainer = function (urlPath, urlParams) {
  requestServer(urlPath, urlParams, function(obj) {
     var html = obj.text

          var ScriptFragment = '<script[^>]*>([¥¥S¥¥s]*?)<¥/script>';
          var scripts = html.match(new RegExp(ScriptFragment, 'img'));
          if (scripts) {
             for (var i=0; i<scripts.length; i++) {
                var script = scripts[i].match(new RegExp(ScriptFragment, 'im'));
                if (script) eval(script[1]);
             }
          }

          $("#gadget_container").html(html);
          gadgets.window.adjustHeight();
    });
}




                                   Copyright(c)2009 Cyworks Inc. All Rights Reserved.
function requestServer(urlPath, urlParams, callbackFunction) {
      if (urlParams==null) urlParams = {};
      var url = config.base_url + urlPath;
      var params = {};
      params[gadgets.io.RequestParameters.METHOD] = gadgets.io.MethodType.POST;
      params[gadgets.io.RequestParameters.CONTENT_TYPE] = gadgets.io.ContentType.TEXT;
      params[gadgets.io.RequestParameters.POST_DATA] = gadgets.io.encodeValues(urlParams);
      gadgets.io.makeRequest(url, callbackFunction, params);
    }

    $[name_space] = klass;
    $[name_space.replace(/_([a-z])/g, function () { return arguments[1].toUpperCase() })] = klass;
    return this;
   };
})(jQuery);




                                 Copyright(c)2009 Cyworks Inc. All Rights Reserved.
test.html
<h1>TEST</h1>
<p><a href=“#” onclick=“$.mixigadget.requestContainer('/test2.html');”>test2へのリンク</p>



test2.html
<h1>TEST2</h1>
<p><a href=“#” onclick=“$.mixigadget.requestContainer('/test1.html');”>test1へのリンク</p>




                          Copyright(c)2009 Cyworks Inc. All Rights Reserved.
Social DataをサーバへPOST

• JavaScript APIのPerson & Friends APIからデー
  タを一通り取得
• 取得したデータをJSON化
• サーバへPOST




            Copyright(c)2009 Cyworks Inc. All Rights Reserved.
jquery.mixigadget.js(追加・修正部分のみ)

 function canvasInit() {
   var req = opensocial.newDataRequest();

   var friend_params = {};
   friend_params[opensocial.DataRequest.PeopleRequestFields.MAX] = 1000;

   req.add(req.newFetchPersonRequest(opensocial.IdSpec.PersonId.OWNER), "owner");
   req.add(req.newFetchPersonRequest(opensocial.IdSpec.PersonId.VIEWER), "viewer");

    req.add(req.newFetchPeopleRequest(opensocial.newIdSpec({'userId':opensocial.IdSpec.PersonId.OWNER,
  'groupId':opensocial.IdSpec.GroupId.FRIENDS}), friend_params), "friends");
    req.send(function (res) {
        var params = {
           "owner" : person_to_json(res.get("owner").getData()),
           "viewer" : person_to_json(res.get("viewer").getData()),
           "friends" : people_to_json(res.get("friends").getData())
        };
        klass.requestContainer('/register', params);
    });
 }


                              Copyright(c)2009 Cyworks Inc. All Rights Reserved.
function person_to_json(_person) {
  return gadgets.json.stringify(_person_to_hash(_person));
}

function people_to_json(_people) {
  var people = [];
  _people.each(function(_person) {
      people.push(_person_to_hash(_person));
  });
  return gadgets.json.stringify(people);
}

function _person_to_hash(_person) {
  var fields = {
     'mixi_id' : opensocial.Person.Field.ID,
     'thumbnail_url' : opensocial.Person.Field.THUMBNAIL_URL
  };
  var person = {};
  for (var key in fields) {
     person[key] = _person.getField(fields[key]);
  }
  person['nickname'] = _person.getDisplayName();
  return person;
}



                              Copyright(c)2009 Cyworks Inc. All Rights Reserved.
/register(サーバ側プログラム)
略(POSTされたJSONデータをセッションなりDBなりに保持する)

※Ruby on Railsの場合、以下のようにPOSTされたデータを取得できる
owner_data = JSON.parse(params['owner'])
viewer_data = JSON.parse(params['viewer'])
friends_data = JSON.parse(params['friends'])




                  Copyright(c)2009 Cyworks Inc. All Rights Reserved.
サーバのセッション維持

• /register のレスポンスHTMLでSession IDを
  Javascriptの変数にセットする




           Copyright(c)2009 Cyworks Inc. All Rights Reserved.
jquery.mixigadget.js(追加・修正部分のみ)

 klass.setSession = function(session_key, session_id) {
   config.session_key = session_key;
   config.session_id = session_id;
 }

 function requestServer(urlPath, urlParams, callbackFunction) {
   if (urlParams==null) urlParams = {};
   if (config.session_id) urlParams[config.session_key] = config.session_id;
   (以下同じ)
 }




                              Copyright(c)2009 Cyworks Inc. All Rights Reserved.
/registerのレスポンス
<script type="text/javascript">
$.mixigadget.setSession(‘_sid', ‘xxxxxxxxxxxxxxxxxxxxxxxxxxxxx');
</script>
<p>Hello World!!</p>




                             Copyright(c)2009 Cyworks Inc. All Rights Reserved.
アクティビティの送信

• サーバからのレスポンスHTMLでアクティビ
  ティを登録するJavascriptを実行




       Copyright(c)2009 Cyworks Inc. All Rights Reserved.
レスポンス例
<script type="text/javascript">
var params = {};
params[opensocial.Activity.Field.TITLE] = "Hello!";
var activity = opensocial.newActivity(params);
opensocial.requestCreateActivity(activity, opensocial.CreateActivityPriority.HIGH,
    function(response) { // do something... });
</script>
<p>アクティビティを送信したよ!</p>




                             Copyright(c)2009 Cyworks Inc. All Rights Reserved.
まとめ

• Javascript APIしか提供されていない(mixi)
  オープンソーシャルアプリでも、サーバサイド
  アプリケーションと同じような感覚でつくること
  ができる!




         Copyright(c)2009 Cyworks Inc. All Rights Reserved.
mixiアプリモバイル

• mixiアプリモバイルでは、携帯端末で
  Javascriptが動作しないので、代わりにRESTful
  APIが提供されている




         Copyright(c)2009 Cyworks Inc. All Rights Reserved.
ということは

• モバイル向けには、サーバサイドでプログラ
  ムを組む必要がある
 • PC向けにはJavascriptで、モバイル向けにはサー
   バサイドだと、別々のプログラムを組むことになる


→PC向けをサーバサイドアプリとして作ってい
 れば、モバイル向けにも同じプログラムを使
 える!!

        Copyright(c)2009 Cyworks Inc. All Rights Reserved.
というわけで

• 今回紹介した方法を使えば、一度でPC・モバ
  イル両対応のアプリをつくることができるので
  かなりおすすめ
 • すでにあるWEBアプリをmixiアプリ対応するのに
   もおすすめ




        Copyright(c)2009 Cyworks Inc. All Rights Reserved.
より詳細な情報

• Peeled unshiu
  • http://wiki.unshiu.jp/
  • ドリコム社提供
  • オープンソースのモバイルSNSサイト構築パッ
    ケージ(Ruby on Rails)
  • プラグインとしてmixiアプリの構築基盤を提供
     ユーザー数等の各種効果測定が可能
     ドリコム社提供のポイント広告システム『poncan』対応


             Copyright(c)2009 Cyworks Inc. All Rights Reserved.
より詳細な情報

• mixiapp_base
  • git://github.com/araimotoki/mixiapp_base.git
  • Peeled unshiuのmixiアプリプラグインを改良し、単
    独でmixiアプリを作成できるようなパッケージ
    (Ruby on Rails)




              Copyright(c)2009 Cyworks Inc. All Rights Reserved.
Peeled unshiu/mixiapp_baseの紹介

• クライアント側プログラム(Javascript)がすでに用
  意されており、普段はそれらを意識する必要がな
  い
• helperが充実しており、通常の流れでJavascriptを
  直接書くことはあまりない(リッチUIの実装のため
  にjavascript(jquery)を書くことはある)

→普通にRuby on Railsでアプリケーションをつくるの
 とあまり変わらない

        Copyright(c)2009 Cyworks Inc. All Rights Reserved.
test_controller.rb
class TestController < ApplicationController
 before_filter :validate_session

 def profile
 end
 def friends
 end
end


profile.html.rb
<h1>プロフィール</h1>
<p>
 <img src="<%= current_owner.thumbnail_url %>"><br>
 mixi_id: <%= current_owner.mixi_id %><br>
 nickname: <%= current_owner.nickname %><br>
</p>
<%= link_to_update “友達", :url => { :action => “friends” } %><br>


friends.html.rb
<h1><%= current_owner.nickname %>の友達</h1>
<% current_owner.friends.each do |user| %>
 <p>
  <img src="<%= user.thumbnail_url %>"><br>
  mixi_id: <%= user.mixi_id %><br>
  nickname: <%= user.nickname %><br>
 </p>
<% end %>
<%= link_to_update “戻る", :url => { :action => “profile” } %><br>



                                               Copyright(c)2009 Cyworks Inc. All Rights Reserved.
mixiapp_baseの実用例

• マイミク記念日
 • http://mixi.jp/run_appli.pl?id=7981
 • 記念日のカウントダウン+マイミクの記念日に
   メッセージを送るアプリ
 • PC・モバイル両対応




              Copyright(c)2009 Cyworks Inc. All Rights Reserved.

ソーシャルアプリ勉強会(第一回資料)配布用

  • 18.
    新井 元基 Copyright(c)2009 CyworksInc. All Rights Reserved.
  • 19.
    前提 • 以下については理解しているものとして進め ます Opensocialの仕様 Javascript, jqueryの文法 Ajaxの仕組み Copyright(c)2009 Cyworks Inc. All Rights Reserved.
  • 20.
    オープンソーシャルアプリ? • Javascript API(PC向け) • クライアントサイドでJavascriptをばりばりコーディ ングする必要あり • 普段サーバサイドでプログラムを書いているWEB 屋さんにとっては、敷居が高い?過去の資産= ソースコードを活用できない? Copyright(c)2009 Cyworks Inc. All Rights Reserved.
  • 21.
    オープンソーシャルアプリ? • No!! →サーバサイドアプリケーションの土俵に持ち込む Copyright(c)2009 Cyworks Inc. All Rights Reserved.
  • 22.
    どうやって? • Iframe ○完全にサーバサイド ×Javascript APIにアクセスできない • Ajax △一部クライアントサイド →クライアントサイドは可能な限りいじらなくて済む ように、毎回共通で使うクライアントプログラムを 用意 Copyright(c)2009 Cyworks Inc. All Rights Reserved.
  • 23.
    具体的に • アプリ領域全体を<div>で囲み、div領域全体 をAjaxで書き換える • アプリ起動時にJavascript APIでOWNER, VIEWER, FRIENDS(OWNER)などのSocial Data を取得してサーバへPOST • サーバ側のセッションはURLパラメータでセッ ションIDを引き回すことで維持 • アクティビティの送信はサーバレスポンスで Copyright(c)2009 Cyworks Inc. All Rights Reserved.
  • 24.
    div領域をAjaxで書き換える • <div id="gadget_container"></div> •gadgets.io.makeRequest でサーバからHTML を取得 • $(“#gadget_container”).html(<取得した HTML>); Copyright(c)2009 Cyworks Inc. All Rights Reserved.
  • 25.
    gadget.xml <Module> <ModulePrefs title="test"> <Require feature="opensocial-0.8"/> <Require feature="views"/> <Require feature="dynamic-height"/> </ModulePrefs> <Content type="html"> <script src="http://xxxx/jquery-1.3.2.min.js" type="text/javascript"></script> <script src="http://xxxx/jquery.mixigadget.js" type="text/javascript"></script> <link href="http://xxxx/style.css" rel="stylesheet" type="text/css" /> <script type="text/javascript"> (function(){ $('#gadget_container').mixigadget(); })(); </script> <div id="gadget_container"></div> </Content> </Module> Copyright(c)2009 Cyworks Inc. All Rights Reserved.
  • 26.
    jquery.mixigadget.js ;(function($) { var name_space = 'mixigadget'; $.fn[name_space] = function(config){ config = jQuery.extend({ session_id: "", session_key: "", base_url: "", view_name: gadgets.views.getCurrentView().getName() },config); gadgets.util.registerOnLoadHandler(gadgetInit); function gadgetInit() { switch(config.view_name) { case 'canvas': canvasInit(); break; } } function canvasInit() { klass.requestContainer('/test.html‘); } Copyright(c)2009 Cyworks Inc. All Rights Reserved.
  • 27.
    klass.requestContainer = function(urlPath, urlParams) { requestServer(urlPath, urlParams, function(obj) { var html = obj.text var ScriptFragment = '<script[^>]*>([¥¥S¥¥s]*?)<¥/script>'; var scripts = html.match(new RegExp(ScriptFragment, 'img')); if (scripts) { for (var i=0; i<scripts.length; i++) { var script = scripts[i].match(new RegExp(ScriptFragment, 'im')); if (script) eval(script[1]); } } $("#gadget_container").html(html); gadgets.window.adjustHeight(); }); } Copyright(c)2009 Cyworks Inc. All Rights Reserved.
  • 28.
    function requestServer(urlPath, urlParams,callbackFunction) { if (urlParams==null) urlParams = {}; var url = config.base_url + urlPath; var params = {}; params[gadgets.io.RequestParameters.METHOD] = gadgets.io.MethodType.POST; params[gadgets.io.RequestParameters.CONTENT_TYPE] = gadgets.io.ContentType.TEXT; params[gadgets.io.RequestParameters.POST_DATA] = gadgets.io.encodeValues(urlParams); gadgets.io.makeRequest(url, callbackFunction, params); } $[name_space] = klass; $[name_space.replace(/_([a-z])/g, function () { return arguments[1].toUpperCase() })] = klass; return this; }; })(jQuery); Copyright(c)2009 Cyworks Inc. All Rights Reserved.
  • 29.
    test.html <h1>TEST</h1> <p><a href=“#” onclick=“$.mixigadget.requestContainer('/test2.html');”>test2へのリンク</p> test2.html <h1>TEST2</h1> <p><ahref=“#” onclick=“$.mixigadget.requestContainer('/test1.html');”>test1へのリンク</p> Copyright(c)2009 Cyworks Inc. All Rights Reserved.
  • 30.
    Social DataをサーバへPOST • JavaScriptAPIのPerson & Friends APIからデー タを一通り取得 • 取得したデータをJSON化 • サーバへPOST Copyright(c)2009 Cyworks Inc. All Rights Reserved.
  • 31.
    jquery.mixigadget.js(追加・修正部分のみ) function canvasInit(){ var req = opensocial.newDataRequest(); var friend_params = {}; friend_params[opensocial.DataRequest.PeopleRequestFields.MAX] = 1000; req.add(req.newFetchPersonRequest(opensocial.IdSpec.PersonId.OWNER), "owner"); req.add(req.newFetchPersonRequest(opensocial.IdSpec.PersonId.VIEWER), "viewer"); req.add(req.newFetchPeopleRequest(opensocial.newIdSpec({'userId':opensocial.IdSpec.PersonId.OWNER, 'groupId':opensocial.IdSpec.GroupId.FRIENDS}), friend_params), "friends"); req.send(function (res) { var params = { "owner" : person_to_json(res.get("owner").getData()), "viewer" : person_to_json(res.get("viewer").getData()), "friends" : people_to_json(res.get("friends").getData()) }; klass.requestContainer('/register', params); }); } Copyright(c)2009 Cyworks Inc. All Rights Reserved.
  • 32.
    function person_to_json(_person) { return gadgets.json.stringify(_person_to_hash(_person)); } function people_to_json(_people) { var people = []; _people.each(function(_person) { people.push(_person_to_hash(_person)); }); return gadgets.json.stringify(people); } function _person_to_hash(_person) { var fields = { 'mixi_id' : opensocial.Person.Field.ID, 'thumbnail_url' : opensocial.Person.Field.THUMBNAIL_URL }; var person = {}; for (var key in fields) { person[key] = _person.getField(fields[key]); } person['nickname'] = _person.getDisplayName(); return person; } Copyright(c)2009 Cyworks Inc. All Rights Reserved.
  • 33.
    /register(サーバ側プログラム) 略(POSTされたJSONデータをセッションなりDBなりに保持する) ※Ruby on Railsの場合、以下のようにPOSTされたデータを取得できる owner_data= JSON.parse(params['owner']) viewer_data = JSON.parse(params['viewer']) friends_data = JSON.parse(params['friends']) Copyright(c)2009 Cyworks Inc. All Rights Reserved.
  • 34.
    サーバのセッション維持 • /register のレスポンスHTMLでSessionIDを Javascriptの変数にセットする Copyright(c)2009 Cyworks Inc. All Rights Reserved.
  • 35.
    jquery.mixigadget.js(追加・修正部分のみ) klass.setSession =function(session_key, session_id) { config.session_key = session_key; config.session_id = session_id; } function requestServer(urlPath, urlParams, callbackFunction) { if (urlParams==null) urlParams = {}; if (config.session_id) urlParams[config.session_key] = config.session_id; (以下同じ) } Copyright(c)2009 Cyworks Inc. All Rights Reserved.
  • 36.
  • 37.
    アクティビティの送信 • サーバからのレスポンスHTMLでアクティビ ティを登録するJavascriptを実行 Copyright(c)2009 Cyworks Inc. All Rights Reserved.
  • 38.
    レスポンス例 <script type="text/javascript"> var params= {}; params[opensocial.Activity.Field.TITLE] = "Hello!"; var activity = opensocial.newActivity(params); opensocial.requestCreateActivity(activity, opensocial.CreateActivityPriority.HIGH, function(response) { // do something... }); </script> <p>アクティビティを送信したよ!</p> Copyright(c)2009 Cyworks Inc. All Rights Reserved.
  • 39.
    まとめ • Javascript APIしか提供されていない(mixi) オープンソーシャルアプリでも、サーバサイド アプリケーションと同じような感覚でつくること ができる! Copyright(c)2009 Cyworks Inc. All Rights Reserved.
  • 40.
    mixiアプリモバイル • mixiアプリモバイルでは、携帯端末で Javascriptが動作しないので、代わりにRESTful APIが提供されている Copyright(c)2009 Cyworks Inc. All Rights Reserved.
  • 41.
    ということは • モバイル向けには、サーバサイドでプログラ ムを組む必要がある • PC向けにはJavascriptで、モバイル向けにはサー バサイドだと、別々のプログラムを組むことになる →PC向けをサーバサイドアプリとして作ってい れば、モバイル向けにも同じプログラムを使 える!! Copyright(c)2009 Cyworks Inc. All Rights Reserved.
  • 42.
    というわけで • 今回紹介した方法を使えば、一度でPC・モバ イル両対応のアプリをつくることができるので かなりおすすめ • すでにあるWEBアプリをmixiアプリ対応するのに もおすすめ Copyright(c)2009 Cyworks Inc. All Rights Reserved.
  • 43.
    より詳細な情報 • Peeled unshiu • http://wiki.unshiu.jp/ • ドリコム社提供 • オープンソースのモバイルSNSサイト構築パッ ケージ(Ruby on Rails) • プラグインとしてmixiアプリの構築基盤を提供 ユーザー数等の各種効果測定が可能 ドリコム社提供のポイント広告システム『poncan』対応 Copyright(c)2009 Cyworks Inc. All Rights Reserved.
  • 44.
    より詳細な情報 • mixiapp_base • git://github.com/araimotoki/mixiapp_base.git • Peeled unshiuのmixiアプリプラグインを改良し、単 独でmixiアプリを作成できるようなパッケージ (Ruby on Rails) Copyright(c)2009 Cyworks Inc. All Rights Reserved.
  • 45.
    Peeled unshiu/mixiapp_baseの紹介 • クライアント側プログラム(Javascript)がすでに用 意されており、普段はそれらを意識する必要がな い • helperが充実しており、通常の流れでJavascriptを 直接書くことはあまりない(リッチUIの実装のため にjavascript(jquery)を書くことはある) →普通にRuby on Railsでアプリケーションをつくるの とあまり変わらない Copyright(c)2009 Cyworks Inc. All Rights Reserved.
  • 46.
    test_controller.rb class TestController <ApplicationController before_filter :validate_session def profile end def friends end end profile.html.rb <h1>プロフィール</h1> <p> <img src="<%= current_owner.thumbnail_url %>"><br> mixi_id: <%= current_owner.mixi_id %><br> nickname: <%= current_owner.nickname %><br> </p> <%= link_to_update “友達", :url => { :action => “friends” } %><br> friends.html.rb <h1><%= current_owner.nickname %>の友達</h1> <% current_owner.friends.each do |user| %> <p> <img src="<%= user.thumbnail_url %>"><br> mixi_id: <%= user.mixi_id %><br> nickname: <%= user.nickname %><br> </p> <% end %> <%= link_to_update “戻る", :url => { :action => “profile” } %><br> Copyright(c)2009 Cyworks Inc. All Rights Reserved.
  • 47.
    mixiapp_baseの実用例 • マイミク記念日 •http://mixi.jp/run_appli.pl?id=7981 • 記念日のカウントダウン+マイミクの記念日に メッセージを送るアプリ • PC・モバイル両対応 Copyright(c)2009 Cyworks Inc. All Rights Reserved.