いしいたけるのプログラミングメモ
仕事中に気づいたプログラミング上のTipsや備忘録をメモしておくための場所です。
[PHP][Android]など
ブログ主:いしいたける
メインのWebサイト(マンガ) : http://loveandcomic.com/
2017年1月18日水曜日
2017年1月13日金曜日
【Ruby】PHPでやっていたように変数や配列の内容があるかどうかをチェックしたい
転職が決まり、今後は Ruby on rails での仕事が中心になりそう。
転職するまでの間にすこし Ruby を勉強しておこうと思い、PHPとの違うところ・気になるところをいくつか調べた。
PHPではよく、「ちゃんと値が入っているか」のチェックをした。
私のお決まりの書き方は以下のようなものだった。
これをRubyでできないかなと思って調べていた。
ちなみに、以下の場合すべて「値がない」としたい。
◆変数が定義されていない
◆配列の中で指定したキーが定義されていない($array['undefined_key'] みたいなもの)
◆null
◆false
◆"" (カラの文字列)
◆[] (カラの配列)
◆0 (数字の0)
わかったのは、RubyとPHPとの大きな違いがあるので上記の発想そのままではできないということだった。
PHP : オブジェクト指向ではあるがゆるい。上記の「isset()」や「empty()」といったグローバルに使える関数がたくさんある。
Ruby : きっちりしたオブジェクト指向なのでそもそもグローバル関数のようなものがほとんどない。関数は必ず主語(オブジェクト)を持つ。
なので、「中身がちゃんとあるかどうかを確認するためのお決まりの処理」を覚えておくのではなく、オブジェクトに合わせて判定方法を変えないといけないということだ。
Webシステムを作るのにおいて、結局、中身があるかどうかのチェックが必要な主要なケースはふたつだと思う。
1.Modelでのデータベース検索結果があるかどうか
2.GETやPOSTでの送信値があるかどうか
[1.Modelでのデータベース検索結果があるかどうか]
検索結果がない場合、検索結果オブジェクトはnil(PHPでいうnull)になる。
[2.GETやPOSTでの送信値があるかどうか]
送信値を取り出す時、コントローラーでは「 params[:キー名] 」と書く。
この時、送信されていないキー名でも、変数(というか多分連想配列の該当キーなんだろうけど)の定義自体はされている。ただし中身がnilになっている。
/test/index?id=test&name=ishiitakeru
というとき、GET送信値で以下のキーのものは定義されているのは判る。
params[:id] 中身は「test」
params[:name] 中身は「ishiitakeru」
このとき、例えば以下のものは未定義に思える。
params[:undefined_key]
Rubyで定義済みかどうかを調べるには defined?(変数名) と書く。
そこで、上記の未定義キーのパラメータを調べる
ところがこれは、TRUEになる。定義済み扱いになる。
どうも、Rubyでのdefined?() をハッシュ(連想配列)に使う場合、その連想配列自体があればTRUEになって、キーの指定まで見てないらしい。
ハッシュを主語にしてキーが定義されているかどうかを調べる場合は「ハッシュ.has_key?(キー名)」を使う。
[連想配列の・定義されていないキーにアクセスしようとした時]
PHP : エラーになる。「Notice: Undefined index: キー名」
Ruby : エラーにならない。nil が取得できる。
結局、Rubyでは連想配列から定義されていないキーを取り出そうとしてもその動作自体はエラーにならないので、値がカラでないかだけをチェックすればいい。
------------------------------------------------------
結論
------------------------------------------------------
1.Modelでのデータベース検索結果があるかどうか
2.GETやPOSTでの送信値があるかどうか
→どちらも、Ruby on rails では、「.present?」「.blank?」でチェックできる。ただし数字の0もTRUE扱いになる。
もし、レイルズではないRuby自体を使う場合、レイルズで拡張された関数である「.present?」「.blank?」が使えないので、自作でチェッカークラスを作ったほうがいいように感じる。以下が自作したもの。これだと数値の0もfalse扱いになる。
こんな感じで使う。
転職するまでの間にすこし Ruby を勉強しておこうと思い、PHPとの違うところ・気になるところをいくつか調べた。
PHPではよく、「ちゃんと値が入っているか」のチェックをした。
私のお決まりの書き方は以下のようなものだった。
//php if( (isset($value))&& (empty($value) === false) ){ //値がある }//if ---------------------------
これをRubyでできないかなと思って調べていた。
ちなみに、以下の場合すべて「値がない」としたい。
◆変数が定義されていない
◆配列の中で指定したキーが定義されていない($array['undefined_key'] みたいなもの)
◆null
◆false
◆"" (カラの文字列)
◆[] (カラの配列)
◆0 (数字の0)
わかったのは、RubyとPHPとの大きな違いがあるので上記の発想そのままではできないということだった。
PHP : オブジェクト指向ではあるがゆるい。上記の「isset()」や「empty()」といったグローバルに使える関数がたくさんある。
Ruby : きっちりしたオブジェクト指向なのでそもそもグローバル関数のようなものがほとんどない。関数は必ず主語(オブジェクト)を持つ。
なので、「中身がちゃんとあるかどうかを確認するためのお決まりの処理」を覚えておくのではなく、オブジェクトに合わせて判定方法を変えないといけないということだ。
Webシステムを作るのにおいて、結局、中身があるかどうかのチェックが必要な主要なケースはふたつだと思う。
1.Modelでのデータベース検索結果があるかどうか
2.GETやPOSTでの送信値があるかどうか
[1.Modelでのデータベース検索結果があるかどうか]
検索結果がない場合、検索結果オブジェクトはnil(PHPでいうnull)になる。
[2.GETやPOSTでの送信値があるかどうか]
送信値を取り出す時、コントローラーでは「 params[:キー名] 」と書く。
この時、送信されていないキー名でも、変数(というか多分連想配列の該当キーなんだろうけど)の定義自体はされている。ただし中身がnilになっている。
/test/index?id=test&name=ishiitakeru
というとき、GET送信値で以下のキーのものは定義されているのは判る。
params[:id] 中身は「test」
params[:name] 中身は「ishiitakeru」
このとき、例えば以下のものは未定義に思える。
params[:undefined_key]
Rubyで定義済みかどうかを調べるには defined?(変数名) と書く。
そこで、上記の未定義キーのパラメータを調べる
#Ruby if defined?(params[:undefined_key]) #定義済み end
ところがこれは、TRUEになる。定義済み扱いになる。
どうも、Rubyでのdefined?() をハッシュ(連想配列)に使う場合、その連想配列自体があればTRUEになって、キーの指定まで見てないらしい。
ハッシュを主語にしてキーが定義されているかどうかを調べる場合は「ハッシュ.has_key?(キー名)」を使う。
# Ruby test_hash = { 'id' => 666, 'name' => 'ishiitakeru', } if defined?(test_hash["undefined"]) print "defined(定義されてないハッシュのキー) true \n" #→こっちが表示される else print "defined(定義されてないハッシュのキー) false \n" end if test_hash.has_key?("undefined") print "has_key?(定義されてないハッシュのキー) true \n" else print "has_key?(定義されてないハッシュのキー) false \n" #→こっちが表示される end
[連想配列の・定義されていないキーにアクセスしようとした時]
PHP : エラーになる。「Notice: Undefined index: キー名」
Ruby : エラーにならない。nil が取得できる。
結局、Rubyでは連想配列から定義されていないキーを取り出そうとしてもその動作自体はエラーにならないので、値がカラでないかだけをチェックすればいい。
------------------------------------------------------
結論
------------------------------------------------------
1.Modelでのデータベース検索結果があるかどうか
2.GETやPOSTでの送信値があるかどうか
→どちらも、Ruby on rails では、「.present?」「.blank?」でチェックできる。ただし数字の0もTRUE扱いになる。
もし、レイルズではないRuby自体を使う場合、レイルズで拡張された関数である「.present?」「.blank?」が使えないので、自作でチェッカークラスを作ったほうがいいように感じる。以下が自作したもの。これだと数値の0もfalse扱いになる。
# Ruby # custom_checker.rb # 自作チェッカークラス class Checker ############################################# # 値チェック # # empty? が使えないものが注意が必要 # 数字型 : FixNum # nil : NilCass # false : FalseClass # true : TrueClass # # empty? が使えるもの # 文字列クラス : String # 配列 : Array # 連想配列 : Hash # # @param 値をチェックする対象 # @return bool 有効な値があればtrue # def self.content_exist?(value) # データ型確認 # print value.class # print "\n" # empty が使えるならそれで判定 # empty?判定 文字列・配列・連想配列がチェックできる # obj.respond_to?(:メソッド名) で、 # そのメソッドがそのインスタンスで使用可能かが取得できる if value.respond_to?(:empty?) #返したいbool値はempty?と逆の値になる return !(value.empty?) # true elsif value == true return true # false elsif value == false return false # nil elsif value == nil return false #数値型 #数値型であればすべてここの処理を通したいので # 数値型判定だけでifを独立させる elsif value.kind_of?(Numeric) #数値0 if value == 0 return false end end #ここまでくれば true 判定 return true end end
こんな感じで使う。
# Ruby require 'custom_checker.rb' if Checker.content_exist?(value) print " : true" else print " : false" end
2016年12月16日金曜日
【JavaScript】非同期通信のためにPromiseをつかう
JavaScriptで非同期通信を利用する際に利用できるPromiseというのを勉強したのでメモ。
【参考URL】
http://qiita.com/koki_cheese/items/c559da338a3d307c9d88
https://www.htmlhifive.com/conts/web/view/study-room/async-programming-with-deferred
Promiseというのはデザインパターンのひとつらしい。
なのだけど、デザインパターンのWikipedia記事には記述がないのでデザインパターンということではないのかな。ただのライブラリ?
Promiseというのはオブジェクトである。
PHPやJSでは、関数を呼び出した際、リターンで値が戻ってこない限り次の処理に進めないので、まず素早くPromiseオブジェクトを返しておき、データの準備が整ったらそのオブジェクトを通してお目当ての値を取り出す、という仕組み。
■本来取得したい戻り値(WebAPIとの接続によって取得する値など)の代わりに「プロミスオブジェクト」という特別なオブジェクトをまず素早く返す。
■本来取得したい値を渡せる状態になったらそのプロミスオブジェクトを通して値を取り出す。
■Promiseオブジェクトのコンストラクタには、「成功時に実行する関数と失敗時に実行する関数とのふたつを引数に取る関数」を渡す。このコンストラクタにおいて、成功時のコールバックと失敗時のコールバックとの呼び出しを登録する。それぞれの実際の処理の内容をコンストラクタ内で定義するのではなく、あくまで呼び出されるタイミングを定義する。
■通信成功時の処理は呼び出し側のthen()に書く。
■通信失敗時の処理は呼び出し側のcatch()に書く。
■大抵の場合、非同期通信を使うだろうのでXMLHttpRequestインスタンスと組み合わせて使うことになると思う。Promiseコンストラクタでの「resolve(引数X)」と呼び出し側での「then(引数X)」とが対応するので、ここでXMLHttpRequestインスタンスをやり取りすると良さそう。
【サンプルコード】
index.php
my_promise_ajax.js
api01.php
【参考URL】
http://qiita.com/koki_cheese/items/c559da338a3d307c9d88
https://www.htmlhifive.com/conts/web/view/study-room/async-programming-with-deferred
Promiseというのはデザインパターンのひとつらしい。
なのだけど、デザインパターンのWikipedia記事には記述がないのでデザインパターンということではないのかな。ただのライブラリ?
Promiseというのはオブジェクトである。
PHPやJSでは、関数を呼び出した際、リターンで値が戻ってこない限り次の処理に進めないので、まず素早くPromiseオブジェクトを返しておき、データの準備が整ったらそのオブジェクトを通してお目当ての値を取り出す、という仕組み。
■本来取得したい戻り値(WebAPIとの接続によって取得する値など)の代わりに「プロミスオブジェクト」という特別なオブジェクトをまず素早く返す。
■本来取得したい値を渡せる状態になったらそのプロミスオブジェクトを通して値を取り出す。
■Promiseオブジェクトのコンストラクタには、「成功時に実行する関数と失敗時に実行する関数とのふたつを引数に取る関数」を渡す。このコンストラクタにおいて、成功時のコールバックと失敗時のコールバックとの呼び出しを登録する。それぞれの実際の処理の内容をコンストラクタ内で定義するのではなく、あくまで呼び出されるタイミングを定義する。
■通信成功時の処理は呼び出し側のthen()に書く。
■通信失敗時の処理は呼び出し側のcatch()に書く。
■大抵の場合、非同期通信を使うだろうのでXMLHttpRequestインスタンスと組み合わせて使うことになると思う。Promiseコンストラクタでの「resolve(引数X)」と呼び出し側での「then(引数X)」とが対応するので、ここでXMLHttpRequestインスタンスをやり取りすると良さそう。
【サンプルコード】
index.php
<html>
<head>
<title>Promiseによる非同期処理テスト</title>
<script type="text/javascript" src="my_promise_ajax.js"></script>
<script src="https://www.promisejs.org/polyfills/promise-6.1.0.min.js"></script>
</head>
<style>
textarea{
width:600px;
height:300px;
}
</style>
<body>
<script type="text/javascript">
var promise = getContentFromUrlAsync('api01.php', '{"reward_point_bonus":{"initial":"10","limit_break":"15"}}');
promise.then(function(xmlHttp){
//非同期処理成功時の処理定義
//Promiseクラスのコンストラクタへの引数における
//「resolve」に相当する処理の内容をここで定義している。
//通信で受け取ったコンテンツ
var content = xmlHttp.responseText;
//テキストエリアにコンテンツを代入
var textarea = document.getElementById('textarea01');
textarea.value = content;
})
.catch(function(xmlHttp){
//非同期処理失敗時の処理定義
//Promiseクラスのコンストラクタへの引数における
//「reject」に相当する処理の内容をここで定義している。
alert('失敗01');
});
</script>
<h1>Promiseによる非同期処理テスト</h1>
<div>
<h2>01</h2>
<textarea id="textarea01"></textarea>
</div>
</body>
</html>
<head>
<title>Promiseによる非同期処理テスト</title>
<script type="text/javascript" src="my_promise_ajax.js"></script>
<script src="https://www.promisejs.org/polyfills/promise-6.1.0.min.js"></script>
</head>
<style>
textarea{
width:600px;
height:300px;
}
</style>
<body>
<script type="text/javascript">
var promise = getContentFromUrlAsync('api01.php', '{"reward_point_bonus":{"initial":"10","limit_break":"15"}}');
promise.then(function(xmlHttp){
//非同期処理成功時の処理定義
//Promiseクラスのコンストラクタへの引数における
//「resolve」に相当する処理の内容をここで定義している。
//通信で受け取ったコンテンツ
var content = xmlHttp.responseText;
//テキストエリアにコンテンツを代入
var textarea = document.getElementById('textarea01');
textarea.value = content;
})
.catch(function(xmlHttp){
//非同期処理失敗時の処理定義
//Promiseクラスのコンストラクタへの引数における
//「reject」に相当する処理の内容をここで定義している。
alert('失敗01');
});
</script>
<h1>Promiseによる非同期処理テスト</h1>
<div>
<h2>01</h2>
<textarea id="textarea01"></textarea>
</div>
</body>
</html>
my_promise_ajax.js
////////////////////////////////////////////////////////////
//Promise関連
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
/**
* Promiseインスタンスを返す関数。
* 非同期処理を用いて同一ドメイン内APIから値を取得するために利用する。
* この関数は汎用的に作っておき、個々の具体的な処理は呼び出し側のthen()で定義する。
*
* @param string アクセスするURL(同一ドメイン内APIのURL)
* @param string 送信するJSON文字列
* @return Promiseインスタンス
*/
function getContentFromUrlAsync(
api_url,
str_params_json
){
var promise = new Promise(function(resolve, reject) {
//AjaxのためのXMLHttpRequestインスタンスの作成
xmlHttp = new XMLHttpRequest();
///////////////////////
//コールバック登録。
//通信ステータスが更新されるたびに実行されるコールバック処理。
//resolve()、reject()に引数を渡してthen()、catch()で変数を使えるようにするため、
//このコールバックメソッドは外出しせずにここに無名関数として書く。
//こうすることでresolve()、reject()を引数を指定して呼び出せる。
xmlHttp.onreadystatechange = function(){
// 非同期の処理
// 処理が終わったら、resolve または rejectを呼ぶ
if(xmlHttp.readyState == 4){
//readyState : 4 通信完了
if(xmlHttp.status == 200){
//成功時
//promise.then() にXMLHttpRequestインスタンスを渡す
resolve(xmlHttp);
}else{
//失敗時
//promise.catch() にXMLHttpRequestインスタンスを渡す
reject(xmlHttp);
}//if
}//if
};
///////////////////////
//POST送信値の作成
var data = {
json : str_params_json
};
//XMLHttpRequestインスタンスでURLの値を取得
xmlHttp.open(
"POST",
api_url, //接続API(同一ドメイン内)
true
);
//サーバに対して解析方法を指定する(POST送信の場合必要)
xmlHttp.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
//戻り値の型を指定(JSON) 受け取り側で処理が楽になる
//→IEでは使用できないようなので不採用
//xmlHttp.responseType = 'json';
//open()で作成したリクエストを送信 POST送信値を渡す
xmlHttp.send(EncodeHTMLForm(data));
});
return promise;
}//function
//////////////////////////////
/**
* 送信データをHTML形式にエンコードする。
*/
function EncodeHTMLForm(data){
var params = [];
for(var name in data){
var value = data[ name ];
var param = encodeURIComponent( name ) + '=' + encodeURIComponent( value );
params.push( param );
}//for
return params.join( '&' ).replace( /%20/g, '+' );
}//function
//Promise関連
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
/**
* Promiseインスタンスを返す関数。
* 非同期処理を用いて同一ドメイン内APIから値を取得するために利用する。
* この関数は汎用的に作っておき、個々の具体的な処理は呼び出し側のthen()で定義する。
*
* @param string アクセスするURL(同一ドメイン内APIのURL)
* @param string 送信するJSON文字列
* @return Promiseインスタンス
*/
function getContentFromUrlAsync(
api_url,
str_params_json
){
var promise = new Promise(function(resolve, reject) {
//AjaxのためのXMLHttpRequestインスタンスの作成
xmlHttp = new XMLHttpRequest();
///////////////////////
//コールバック登録。
//通信ステータスが更新されるたびに実行されるコールバック処理。
//resolve()、reject()に引数を渡してthen()、catch()で変数を使えるようにするため、
//このコールバックメソッドは外出しせずにここに無名関数として書く。
//こうすることでresolve()、reject()を引数を指定して呼び出せる。
xmlHttp.onreadystatechange = function(){
// 非同期の処理
// 処理が終わったら、resolve または rejectを呼ぶ
if(xmlHttp.readyState == 4){
//readyState : 4 通信完了
if(xmlHttp.status == 200){
//成功時
//promise.then() にXMLHttpRequestインスタンスを渡す
resolve(xmlHttp);
}else{
//失敗時
//promise.catch() にXMLHttpRequestインスタンスを渡す
reject(xmlHttp);
}//if
}//if
};
///////////////////////
//POST送信値の作成
var data = {
json : str_params_json
};
//XMLHttpRequestインスタンスでURLの値を取得
xmlHttp.open(
"POST",
api_url, //接続API(同一ドメイン内)
true
);
//サーバに対して解析方法を指定する(POST送信の場合必要)
xmlHttp.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
//戻り値の型を指定(JSON) 受け取り側で処理が楽になる
//→IEでは使用できないようなので不採用
//xmlHttp.responseType = 'json';
//open()で作成したリクエストを送信 POST送信値を渡す
xmlHttp.send(EncodeHTMLForm(data));
});
return promise;
}//function
//////////////////////////////
/**
* 送信データをHTML形式にエンコードする。
*/
function EncodeHTMLForm(data){
var params = [];
for(var name in data){
var value = data[ name ];
var param = encodeURIComponent( name ) + '=' + encodeURIComponent( value );
params.push( param );
}//for
return params.join( '&' ).replace( /%20/g, '+' );
}//function
api01.php
<?php
$random_value_array = [
'太郎',
'次郎',
'三郎',
'四郎',
'五郎',
];
$json_array = [];
if(isset($_REQUEST['json'])){
$json_array = json_decode($_REQUEST['json']);
}//if
$test_array = [
'return_value' => $random_value_array[mt_rand(0, 4)],
'json' => json_encode($json_array),
];
sleep (3);
echo(json_encode($test_array));
$random_value_array = [
'太郎',
'次郎',
'三郎',
'四郎',
'五郎',
];
$json_array = [];
if(isset($_REQUEST['json'])){
$json_array = json_decode($_REQUEST['json']);
}//if
$test_array = [
'return_value' => $random_value_array[mt_rand(0, 4)],
'json' => json_encode($json_array),
];
sleep (3);
echo(json_encode($test_array));
2016年5月14日土曜日
【Unity】WebAPIに接続してデータを取得する
PHPでもAndroidでも、過去の開発経験からいって、WebAPIに接続してデータを取得するという課題は必ずつきまとうし、また初回はつまづきが大きい。
でも一度やり方が分かってしまうと、あとは作っておいたライブラリファイルに処理を丸投げして終わりとか、コードのコピペ で終わりとか、さらりと出来てしまう。
なので、UnityでWebAPIに接続するサンプルを作っていた。
それなりに出来たのでGitHubにあげました。
https://github.com/ishiitakeru/Unity_WebApiConnectSample
主要なスクリプトはこれ
https://github.com/ishiitakeru/Unity_WebApiConnectSample/blob/master/WebApiConnectSample/Assets/script/ConnectWebApiSample.cs
説明。
WebAPIに接続して値をプロパティに格納する機能を持つのがWWWクラスのインスタンス。
POSTデータを送信するために使うのがWWWFormクラスのインスタンス。
WWWクラスをnewするときに WWWFormのインスタンスを引数に渡してやることでPOSTデータを送信する。
留意ポイントは、 WWWクラスのインスタンスは通信によって状態が変わるということ。
つまり、newした直後はWebAPIからのレスポンスがまだ受け取れていないので、取得した値を取り出せない。
しばらくして通信が完了すると、WWWインスタンスのプロパティから通信で手に入れたデータを取り出せる。
ということは、WWWインスタンスを生成したあと、その状態を時間経過に従って監視し続ける仕組みが必要。
そこで、コルーチンを使う。
コルーチンになっているメソッドでは、毎フレーム、yield return から次の yield returnまでが実行される。
つまり、コルーチンメソッドのすごいところは、 ひとつのメソッド内で時間経過で状態が変わることを取り込んで実現できるということだ。
そこで、WWWインスタンスによってWebAPIに接続するコルーチンメソッドは以下のような作りになるはず。
----------------------------------------------------------------------
private IEnumerator コルーチンメソッド名(){
//WebAPIにPOSTデータを送信するための WWWForm インスタンス
WWWForm my_www_form = new WWWForm();
//キーと値とのセットを登録
my_www_form.AddField("key", "value");
//WWWクラスのインスタンス作成
WWW my_www = new WWW(
"http://WebAPIのURL", //WebAPIのURL
my_www_form //POST送信値をセットしたWWWFormインスタンス
);
//↑ここまでは一度だけ実行されれば良い
//↓ここからは通信によるデータ取得が完了するまで繰り返し実行される必要がある
while(true){
//1フレームで、yield return から次の yield return まで実行される。
yield return null;
//WWW.isDone プロパティによって通信が完了したかどうかを調べられる。
if(my_www.isDone == true){
//通信完了
//WWWインスタンスからデータ取り出し
Debug.Log("Apiレスポンス : " + my_www.text);
break;
}else{
//まだ通信が完了してない
}//if
}//while
}//function
----------------------------------------------------------------------
コツ :
■WWWインスタンスは一度作ればいいので毎フレーム繰り返されるループの外で作っておく。
■WWW.isDone プロパティの確認を毎フレーム繰り返されるループ内で行う。
Androidの場合、別スレッドで通信処理を開始して、データ取得が終わったらそれを検知するコールバックメソッドからデータを取り出して、みたいにかなり複雑なことをやった記憶がある。
[Androidの場合]
■通信用にメインの処理とは別の非同期処理スレッドを作成し、通信はそこでしなくてはならない。
■通信完了を検知するコールバックメソッドを使い、通信で手に入れたデータをそこで取り出さないといけない。
UnityはAndroidに作りが似ているのかなと思ったけど、どうも、WebAPIとの通信においては非同期スレッドもコールバックメソッドも使わない(あるいは意識しないで良い)ということみたい。
その代わり、毎フレーム「yield return 」から「yield return 」まで繰り返し実行されるコルーチンという仕組みをきちんと理解する必要があるようですな。
でも一度やり方が分かってしまうと、あとは作っておいたライブラリファイルに処理を丸投げして終わりとか、コードのコピペ で終わりとか、さらりと出来てしまう。
なので、UnityでWebAPIに接続するサンプルを作っていた。
それなりに出来たのでGitHubにあげました。
https://github.com/ishiitakeru/Unity_WebApiConnectSample
主要なスクリプトはこれ
https://github.com/ishiitakeru/Unity_WebApiConnectSample/blob/master/WebApiConnectSample/Assets/script/ConnectWebApiSample.cs
説明。
WebAPIに接続して値をプロパティに格納する機能を持つのがWWWクラスのインスタンス。
POSTデータを送信するために使うのがWWWFormクラスのインスタンス。
WWWクラスをnewするときに WWWFormのインスタンスを引数に渡してやることでPOSTデータを送信する。
留意ポイントは、 WWWクラスのインスタンスは通信によって状態が変わるということ。
つまり、newした直後はWebAPIからのレスポンスがまだ受け取れていないので、取得した値を取り出せない。
しばらくして通信が完了すると、WWWインスタンスのプロパティから通信で手に入れたデータを取り出せる。
ということは、WWWインスタンスを生成したあと、その状態を時間経過に従って監視し続ける仕組みが必要。
そこで、コルーチンを使う。
コルーチンになっているメソッドでは、毎フレーム、yield return から次の yield returnまでが実行される。
つまり、コルーチンメソッドのすごいところは、 ひとつのメソッド内で時間経過で状態が変わることを取り込んで実現できるということだ。
そこで、WWWインスタンスによってWebAPIに接続するコルーチンメソッドは以下のような作りになるはず。
----------------------------------------------------------------------
private IEnumerator コルーチンメソッド名(){
//WebAPIにPOSTデータを送信するための WWWForm インスタンス
WWWForm my_www_form = new WWWForm();
//キーと値とのセットを登録
my_www_form.AddField("key", "value");
//WWWクラスのインスタンス作成
WWW my_www = new WWW(
"http://WebAPIのURL", //WebAPIのURL
my_www_form //POST送信値をセットしたWWWFormインスタンス
);
//↑ここまでは一度だけ実行されれば良い
//↓ここからは通信によるデータ取得が完了するまで繰り返し実行される必要がある
while(true){
//1フレームで、yield return から次の yield return まで実行される。
yield return null;
//WWW.isDone プロパティによって通信が完了したかどうかを調べられる。
if(my_www.isDone == true){
//通信完了
//WWWインスタンスからデータ取り出し
Debug.Log("Apiレスポンス : " + my_www.text);
break;
}else{
//まだ通信が完了してない
}//if
}//while
}//function
----------------------------------------------------------------------
コツ :
■WWWインスタンスは一度作ればいいので毎フレーム繰り返されるループの外で作っておく。
■WWW.isDone プロパティの確認を毎フレーム繰り返されるループ内で行う。
Androidの場合、別スレッドで通信処理を開始して、データ取得が終わったらそれを検知するコールバックメソッドからデータを取り出して、みたいにかなり複雑なことをやった記憶がある。
[Androidの場合]
■通信用にメインの処理とは別の非同期処理スレッドを作成し、通信はそこでしなくてはならない。
■通信完了を検知するコールバックメソッドを使い、通信で手に入れたデータをそこで取り出さないといけない。
UnityはAndroidに作りが似ているのかなと思ったけど、どうも、WebAPIとの通信においては非同期スレッドもコールバックメソッドも使わない(あるいは意識しないで良い)ということみたい。
その代わり、毎フレーム「yield return 」から「yield return 」まで繰り返し実行されるコルーチンという仕組みをきちんと理解する必要があるようですな。
【Unity】コルーチンに登場するIEnumeratorとはなんぞや
Unityには処理をフレームごとに分割するコルーチン(Coroutine)という仕組みがある。
理解するためのキーワードが IEnumerator というクラス。
これは、yield return 文とセットで使われる。
これは、Listなどのコレクションに関連するクラスだが、コレクションそのものではなく、JavaでいうIterator のように「元となるコレクションがまずあり、それを反復処理するときに使う」クラス。
コルーチンのためのメソッドというものがまず作られる。
これは、返り値がIEnumeratorになるメソッド。
------------------------------------------------------------------------
using UnityEngine;
using System.Collections;
public class CoroutineTest : MonoBehaviour {
//コルーチンで書き換える文字列
private string text_to_show;
//////////////////////////////
/**
* スタート
*/
void Start () {
//コルーチンをスタート
StartCoroutine(
SampleCoroutine()
);
}//Start
//////////////////////////////
/**
* コルーチンのテスト
* このメソッドはUnityで追加されたスクリプト(MonoBehaviour継承クラス)内に書かれる想定
*/
private IEnumerator SampleCoroutine() {
int i = 0;
while(true){
this.text_to_show = "test " + i;
i++;
//コンソールに出力
Debug.Log("text_to_show : " + this.text_to_show);
//2秒待つ
//n秒待たせるには、yield return 文でnew WaitForSeconds(n)を戻させなくてはならない。
yield return new WaitForSeconds(2);
}//while
}//function
}//class
------------------------------------------------------------------------
while(true) があるから無限ループのように見える、もしくは (yield) return のところで処理が終わってそうに見える。
実際には、yield return のところで処理が呼び出し元に帰る。
そして、このメソッドの返り値であるIEnumeratorのインスタンスがMoveNext()を実行すると、前回 yield return した続きから処理が再開され、次の yield return まで処理が走る。
Unityの場合、この MoveNext() は明示的には書かれず、Unityが見えないところで自動で実行する。
その仕組が、Unityの持つ「StartCoroutine(コルーチンメソッド)」という命令。
これは、Unity によって毎フレーム「MoveNext()」が実行されるというもの。
IEnumerator とは、Iterator のような、コレクションを反復処理する際に使う反復子というインターフェイスの一種。
MoveNext() というメソッドを持っているのが特徴。
IEnumerator というのはつまり、元になるコレクションを持ってて(IEnumerator がコレクションから取り出されるとも言える)、MoveNext() によって次の値次の値とアクセスできるということ。
で、IEnumerator を返り値としてyield return するメソッドというのは、MoveNext()によって次の値を返す(次のyield returnの戻り値を返す)コレクションであると見ればいい。
IEnumeratorの元(持ち主)に2つの系統があると考える。
■Listなどのコレクションインスタンス
■yield return を持ち、IEnumerator を戻り値とするメソッド(コルーチン)
以下の変数とメソッドとは、似たようなことをやろうとしている。
------------------------------------------------------------------------
変数 test_list(コレクションのインスタンス)
------------------------------------------------------------------------
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using System.Collections.Generic;
public class IEnumeratorTest01 : MonoBehaviour {
void Start () {
//IEnumerator のインスタンス(コレクション変数)
List<string> test_list = new List<string>();
test_list.Add("test01");
test_list.Add("test02");
test_list.Add("test03");
//コレクションからIEnumerator を取得
IEnumerator test_ienumerator = test_list.GetEnumerator();
while (test_ienumerator.MoveNext()) {
Debug.Log(test_ienumerator.Current);
}//while
}//Start
}//class
------------------------------------------------------------------------
メソッド TestIenumeratorMethod()
------------------------------------------------------------------------
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using System.Collections.Generic;
public class IEnumeratorTest02 : MonoBehaviour {
void Start () {
//IEnumerator のインスタンス(yield return を持つメソッドからの戻り値)
IEnumerator test_ienumerator_by_method = this.TestIenumeratorMethod("test");
while (test_ienumerator_by_method.MoveNext()) {
Debug.Log(test_ienumerator_by_method.Current);
}//while
}//Start
//////////////////////////////
/**
* IEnumeratorの元となるコレクションに相当するメソッド
*/
public IEnumerator TestIenumeratorMethod(string str){
yield return str + "01";
yield return str + "02";
yield return str + "03";
}//function
}//class
------------------------------------------------------------------------
IEnumerator.Current コレクションの中での現在地のオブジェクトを返す。
IEnumerator.MoveNext() boolean を返す。コレクションの現在地(Current)を次の要素に移動させ、次の要素がある場合はtrueを、ない場合はfalseを返す。
理解するためのキーワードが IEnumerator というクラス。
これは、yield return 文とセットで使われる。
これは、Listなどのコレクションに関連するクラスだが、コレクションそのものではなく、JavaでいうIterator のように「元となるコレクションがまずあり、それを反復処理するときに使う」クラス。
コルーチンのためのメソッドというものがまず作られる。
これは、返り値がIEnumeratorになるメソッド。
------------------------------------------------------------------------
using UnityEngine;
using System.Collections;
public class CoroutineTest : MonoBehaviour {
//コルーチンで書き換える文字列
private string text_to_show;
//////////////////////////////
/**
* スタート
*/
void Start () {
//コルーチンをスタート
StartCoroutine(
SampleCoroutine()
);
}//Start
//////////////////////////////
/**
* コルーチンのテスト
* このメソッドはUnityで追加されたスクリプト(MonoBehaviour継承クラス)内に書かれる想定
*/
private IEnumerator SampleCoroutine() {
int i = 0;
while(true){
this.text_to_show = "test " + i;
i++;
//コンソールに出力
Debug.Log("text_to_show : " + this.text_to_show);
//2秒待つ
//n秒待たせるには、yield return 文でnew WaitForSeconds(n)を戻させなくてはならない。
yield return new WaitForSeconds(2);
}//while
}//function
}//class
------------------------------------------------------------------------
while(true) があるから無限ループのように見える、もしくは (yield) return のところで処理が終わってそうに見える。
実際には、yield return のところで処理が呼び出し元に帰る。
そして、このメソッドの返り値であるIEnumeratorのインスタンスがMoveNext()を実行すると、前回 yield return した続きから処理が再開され、次の yield return まで処理が走る。
Unityの場合、この MoveNext() は明示的には書かれず、Unityが見えないところで自動で実行する。
その仕組が、Unityの持つ「StartCoroutine(コルーチンメソッド)」という命令。
これは、Unity によって毎フレーム「MoveNext()」が実行されるというもの。
IEnumerator とは、Iterator のような、コレクションを反復処理する際に使う反復子というインターフェイスの一種。
MoveNext() というメソッドを持っているのが特徴。
IEnumerator というのはつまり、元になるコレクションを持ってて(IEnumerator がコレクションから取り出されるとも言える)、MoveNext() によって次の値次の値とアクセスできるということ。
で、IEnumerator を返り値としてyield return するメソッドというのは、MoveNext()によって次の値を返す(次のyield returnの戻り値を返す)コレクションであると見ればいい。
IEnumeratorの元(持ち主)に2つの系統があると考える。
■Listなどのコレクションインスタンス
■yield return を持ち、IEnumerator を戻り値とするメソッド(コルーチン)
以下の変数とメソッドとは、似たようなことをやろうとしている。
------------------------------------------------------------------------
変数 test_list(コレクションのインスタンス)
------------------------------------------------------------------------
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using System.Collections.Generic;
public class IEnumeratorTest01 : MonoBehaviour {
void Start () {
//IEnumerator のインスタンス(コレクション変数)
List<string> test_list = new List<string>();
test_list.Add("test01");
test_list.Add("test02");
test_list.Add("test03");
//コレクションからIEnumerator を取得
IEnumerator test_ienumerator = test_list.GetEnumerator();
while (test_ienumerator.MoveNext()) {
Debug.Log(test_ienumerator.Current);
}//while
}//Start
}//class
------------------------------------------------------------------------
メソッド TestIenumeratorMethod()
------------------------------------------------------------------------
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using System.Collections.Generic;
public class IEnumeratorTest02 : MonoBehaviour {
void Start () {
//IEnumerator のインスタンス(yield return を持つメソッドからの戻り値)
IEnumerator test_ienumerator_by_method = this.TestIenumeratorMethod("test");
while (test_ienumerator_by_method.MoveNext()) {
Debug.Log(test_ienumerator_by_method.Current);
}//while
}//Start
//////////////////////////////
/**
* IEnumeratorの元となるコレクションに相当するメソッド
*/
public IEnumerator TestIenumeratorMethod(string str){
yield return str + "01";
yield return str + "02";
yield return str + "03";
}//function
}//class
------------------------------------------------------------------------
IEnumerator.Current コレクションの中での現在地のオブジェクトを返す。
IEnumerator.MoveNext() boolean を返す。コレクションの現在地(Current)を次の要素に移動させ、次の要素がある場合はtrueを、ない場合はfalseを返す。
2016年5月10日火曜日
【ECcube3】さくらのレンタルサーバーの深い階層に設置したECcubeに独自ドメインを割り振ったところ、URL解決がややこしくなった
さくらのレンタルサーバーにはECキューブ(3系)のインストール機能がある。
マルチドメインで設定したディレクトリにECキューブをインストールした。
○○.sakura.ne.jp/△△/html
みたいになるように。
独自ドメインの移管の関係で独自ドメインを割り当てるのが遅れそうだったため、○○.sakura.ne.jp 以下のURLでECキューブサイトの構築作業をし、独自ドメインの操作ができるようになり次第URLを入れ替えればいいやと思っていた。
こんな感じ。
前:
http://○○.sakura.ne.jp/△△/html
後:
http://○○.com
マルチドメインで設定したディレクトリにECキューブをインストールした。
○○.sakura.ne.jp/△△/html
みたいになるように。
独自ドメインの移管の関係で独自ドメインを割り当てるのが遅れそうだったため、○○.sakura.ne.jp 以下のURLでECキューブサイトの構築作業をし、独自ドメインの操作ができるようになり次第URLを入れ替えればいいやと思っていた。
こんな感じ。
前:
http://○○.sakura.ne.jp/△△/html
後:
http://○○.com
ところが、独自ドメインを割り当てても、独自ドメインでアクセスした場合に正常に動作しない。
色々やったところ、
\app\config\eccube\path.yml
に記述されている内容を変更したら一応動くようになった。
「root」だとか「○○_urlpath」だとかの設定は、URLの定数設定らしいので、それらを変更した。
ただし、「○○_realdir」という名前の項目はURLではなくサーバー内のパス指定らしいのでこれらは変更してはいけないらしい。
path.yml の変更で一応は動くようになったんですが、URLに「index.php」が必須になってしまった。
独自ドメイン割り当て前の管理画面(例)
http://○○.sakura.ne.jp/△△/html/admin
独自ドメイン割り当て後の管理画面(例)
http://○○.com/index.php/admin
のように。
TOPページも「index.php」を省略するとエラーになってしまう。
.htaccess に DirectoryIndex を設定したりしたのだが、エラーが取れない。
どうも、mod_rewrite の設定が関係しているらしいのだけど、一旦保留。
独自ドメインのネームサーバーの設定が反映されない
作成しているWebサイトに独自ドメインを割り当てるネームサーバーの設定でちょっとつまづいたのでメモ。
ネームサーバーの設定を変更し、数時間経って設定が反映されたはずなのに以前と同じ表示のまま変更されない。
たまたま同じURLにスマホでアクセスしたら、ネームサーバー設定変更後のファイルが表示された。
ところが作業用PCでアクセスすると表示が昔のまま。
そこで、ブラウザのキャッシュを削除したらきちんと新しいファイルが表示された。
DNSの設定の話だからサーバーサイドのファイルだけが問題で、ローカルのキャッシュが影響を及ぼすとは思っていたなかったので気づくのが遅れた。
ネームサーバーの設定を変更し、数時間経って設定が反映されたはずなのに以前と同じ表示のまま変更されない。
たまたま同じURLにスマホでアクセスしたら、ネームサーバー設定変更後のファイルが表示された。
ところが作業用PCでアクセスすると表示が昔のまま。
そこで、ブラウザのキャッシュを削除したらきちんと新しいファイルが表示された。
DNSの設定の話だからサーバーサイドのファイルだけが問題で、ローカルのキャッシュが影響を及ぼすとは思っていたなかったので気づくのが遅れた。
登録:
投稿 (Atom)