tag:blogger.com,1999:blog-87521679668548549442024-02-19T20:23:00.585+09:00明日に向かって昇龍拳夕日に向かってひたすら昇龍拳を打ち込むような「気合」を大切にするブログmisahothttp://www.blogger.com/profile/08782505873150853833noreply@blogger.comBlogger253125tag:blogger.com,1999:blog-8752167966854854944.post-26558820684063591542020-09-18T16:56:00.011+09:002020-09-18T17:10:05.909+09:00Pixel3aをAndroid11へアップデートしたらモバイルネットワークが頻繁に切れる件Android11へアップデートしたことをきっかけに、Pixel3aのモバイル通信がとんでもなく不安定になってしまった(´・ω・`)<br><br>
5秒おきくらいの頻度で、繋がったり切れたりを繰り返し、ろくに調べ物もできない。<br><br><br>
探したら似たような症状の方が。この方はAndroid10で発症。<br><br>
pixel3aをAndroid10にアップデート - <a href="https://fan.uqwimax.jp/topics/detail/1155" target="_blank">https://fan.uqwimax.jp/topics/detail/1155</a><br><br>
<blockquote>pixel3a(simフリー版)をAndroid10にアップデートした途端に
UQのAPN設定が飛びました…(´・ω・`)
手打ちで再設定して復旧しましたが
pixel3aユーザーの方は御注意の程を</blockquote><br><br><br>
APNの設定を見直したら、なんと全く契約していないOCNなんかに接続設定が変わってる〜!<br>
APN設定が初期化された?<br><br>
<div class="separator" style="clear: both;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh4nfONunU2vFt66yydV1LXQ_Grg2kTkFJcqvkyBZvHLTL2eNGo0cTb_rXrA1U2I2n0T6fBVtKLlfHrUYNcYuw2LY0McL8IGRyLaY8hyphenhyphenKc3U6vRhmkSJxwCCUEDYV4jp571DTSbV_zhhax5/s2220/Screenshot_20200918-163035.png" style="display: block; padding: 1em 0; text-align: center; "><img alt="" border="0" height="320" data-original-height="2220" data-original-width="1080" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh4nfONunU2vFt66yydV1LXQ_Grg2kTkFJcqvkyBZvHLTL2eNGo0cTb_rXrA1U2I2n0T6fBVtKLlfHrUYNcYuw2LY0McL8IGRyLaY8hyphenhyphenKc3U6vRhmkSJxwCCUEDYV4jp571DTSbV_zhhax5/s320/Screenshot_20200918-163035.png"/></a></div><br><br>
契約先のAPNへ設定を選び直したら、復活!<br><br>
Andorid9から10へアプデした際は起こらなかったが、10から11へ上げた今回は発生。<br>何がトリガーとなるのかは、全く不明 ┐(´~`)┌misahothttp://www.blogger.com/profile/08782505873150853833noreply@blogger.com0tag:blogger.com,1999:blog-8752167966854854944.post-51087727113629728442016-03-18T14:45:00.000+09:002016-03-18T14:45:38.251+09:00Google App Script で「getActiveForm を呼び出す権限がありません(You do not have permission to call getActiveForm)」エラーが出たらGoogleフォームで、フォーム送信者+特定のメールアドレスへ自動返信メールを送る処理が、なぜか失敗していた。<br />
<br />
<blockquote>その操作を実行するには承認が必要です。<br />
</blockquote><br />
何かしら権限が足りずにエラーが発生しているようだが、これだけでは情報が足りない。<br />
<br />
<br />
<br />
まずは、エラーが発生した際に、すぐにエラー内容を送信するように、トリガーを追加。<br />
<br />
<br />
スクリプトエディタ > リソース > 現在のプロジェクトのトリガー<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEheGqwsoJee6Yj7mGV-ZWq6aRNPwsv-c2lCsQwLReYZN9XpmGfTCc7b_zfDD9rIr1jyYsp-20RLP4rQEonjZ1Dus8KIlKldC-8cLCzekk233wxFmrMCVHPOSxMAKsdQtHyPOgOW_wjta7eJ/s1600/20160318140652.png" imageanchor="1" ><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEheGqwsoJee6Yj7mGV-ZWq6aRNPwsv-c2lCsQwLReYZN9XpmGfTCc7b_zfDD9rIr1jyYsp-20RLP4rQEonjZ1Dus8KIlKldC-8cLCzekk233wxFmrMCVHPOSxMAKsdQtHyPOgOW_wjta7eJ/s400/20160318140652.png" /></a><br />
<br />
<br />
通知 > 起動失敗の通知<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjCDp8jwgCTWhow5KB87FOFgl-wXrhmUf0pnDe4ayPCawWEQG3w9MydSHWIsllBHc6A1YhP1AV5W8wOdokWuJuhP3r3NSxUAgl2G44ofnUUXzTE9OA-59rTxqvkQg87423VdZWTAwIRGGOn/s1600/20160318141036.png" imageanchor="1" ><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjCDp8jwgCTWhow5KB87FOFgl-wXrhmUf0pnDe4ayPCawWEQG3w9MydSHWIsllBHc6A1YhP1AV5W8wOdokWuJuhP3r3NSxUAgl2G44ofnUUXzTE9OA-59rTxqvkQg87423VdZWTAwIRGGOn/s400/20160318141036.png" /></a><br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjzXnUlIjwiC8Zo6-caYpOPzG3FavC_f11xcf7Gjk4rOC-th4Hd1FmhNpnZUZT52p8oMF4PvfQUbofQdD-0ouMTHtzdaV4gF_E0vwpJJfXBjDmblHqtLkCPBzM6Shyphenhyphenpot3q0LNmU7U_pEah/s1600/20160318141345.png" imageanchor="1" ><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjzXnUlIjwiC8Zo6-caYpOPzG3FavC_f11xcf7Gjk4rOC-th4Hd1FmhNpnZUZT52p8oMF4PvfQUbofQdD-0ouMTHtzdaV4gF_E0vwpJJfXBjDmblHqtLkCPBzM6Shyphenhyphenpot3q0LNmU7U_pEah/s400/20160318141345.png" /></a><br />
<br />
<br />
<br />
これでエラー時にメールが送られるようになったが、今度はエラー内容が、<br />
<br />
<blockquote>getActiveForm を呼び出す権限がありません<br />
</blockquote><br />
へ変わっている。<br />
<br />
<br />
あまり情報が無かったが、StackOverFlowで質問していた人がチラホラ見つかった。<br />
<br />
<a href="http://stackoverflow.com/questions/25712837/google-app-script-error-you-do-not-have-permission-to-call-getactiveform" target="_blank">Google App Script Error: You do not have permission to call getActiveForm? - Stack Overflow</a><br />
<br />
<br />
コードへ有無を言わさず、getActiveForm() を実行する一文を挿入して、権限を尋ねるダイアログを出させるしかないようだ。<br />
<br />
<blockquote>function submitForm(e){<br />
<br />
//getActiveForm を呼び出す権限がありません」を解決するためだけの一文<br />
//パーミッションを与えた後は不要<br />
FormApp.getActiveForm();<br />
<br />
....<br />
}<br />
</blockquote><br />
<br />
FormApp変数は宣言不要。上記コードを含めるだけでいい。<br />
<br />
デバッグを起動して、権限を尋ねるダイアログを表示させる。<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjqNKwr_PX7ziOfkxphmAr7QSQAmfD9Gau6p0u2z_fEs3iGOai1BKl7KMa7X3iGknSO6SCmIHpuFpv0FMsPfiC2WCvILGQx-46tqkn79rDgtSIDgtjGhrojo5UuIZXkyZDTVmypjXzoVgyL/s1600/20160318143643.png" imageanchor="1" ><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjqNKwr_PX7ziOfkxphmAr7QSQAmfD9Gau6p0u2z_fEs3iGOai1BKl7KMa7X3iGknSO6SCmIHpuFpv0FMsPfiC2WCvILGQx-46tqkn79rDgtSIDgtjGhrojo5UuIZXkyZDTVmypjXzoVgyL/s400/20160318143643.png" /></a><br />
<br />
<br />
<br />
<big>自動返信メールがキター</big>misahothttp://www.blogger.com/profile/08782505873150853833noreply@blogger.com0tag:blogger.com,1999:blog-8752167966854854944.post-76598828312452166672016-02-03T12:00:00.000+09:002016-02-03T12:00:11.151+09:00Git - 特定ファイルのコミットを取り消してまるっと前の状態へ戻したいとき前回コミットした中に含まれていた、ある特定のファイルについて、それだけまるっと前のバージョンへ戻したいなと思ったら。<br />
<br />
<br />
<big>1. そのファイルのコミット履歴を確認</big><br />
<pre class="brush: bash/shell gutter:false highlight:[1,1]">git log -p [ファイルパス]
</pre><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1C-10VzziAHfpePQzAz62Fhqasp1BDwd3l9UP7njX1ml6ketsQRYHQijv050lWzWVAqoMj8bJzWf4u9RoxB9yxACuDDJK1E8zsn8l0IhotMGJ2d4Lm17hD0zLJ4Un7-tBUoYGKAMhtuyD/s1600/%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AA%25E3%2583%25BC%25E3%2583%25B3%25E3%2582%25B7%25E3%2583%25A7%25E3%2583%2583%25E3%2583%2588+2016-02-03+11.45.33.png" imageanchor="1"><img border="0" height="197" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1C-10VzziAHfpePQzAz62Fhqasp1BDwd3l9UP7njX1ml6ketsQRYHQijv050lWzWVAqoMj8bJzWf4u9RoxB9yxACuDDJK1E8zsn8l0IhotMGJ2d4Lm17hD0zLJ4Un7-tBUoYGKAMhtuyD/s400/%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AA%25E3%2583%25BC%25E3%2583%25B3%25E3%2582%25B7%25E3%2583%25A7%25E3%2583%2583%25E3%2583%2588+2016-02-03+11.45.33.png" width="400" /></a><br />
<br />
差分などが表示される詳細なログのうち、戻したいバージョンのコミット番号だけ確認してコピーしておく。<br />
<br />
<br />
<big>2. コミット番号を指定して、そのファイルを指定したコミット時点まで戻す</big><br />
<pre class="brush: bash/shell gutter:false highlight:[1,1]">git checkout [コミット番号] [ファイルパス]
</pre>misahothttp://www.blogger.com/profile/08782505873150853833noreply@blogger.com0tag:blogger.com,1999:blog-8752167966854854944.post-58880486973314827642016-01-21T14:47:00.000+09:002016-01-21T14:53:20.439+09:00iMacのPCリサイクルマークシールはどこにある?iMac本体にはPCリサイクルマークシールが付いていない。”購入時に同梱”されているらしい。いざ回収にお願いしようと思って、「どこにあるんだ〜」となったので備忘録。<br />
<br />
<br />
結論。<br />
<br />
「キーボードケースの中の取説の束の中に有りました!」<br />
<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhiOHtUI3evsQEd131EurnEfvWF9uaYAHP2oMTXlP7E9eqwd4KrTFMmBn74oiBxNNy0Z6Grcw-LO6EI8Mqoe6EyHT4YtctxYKp47S5x9TmG3kig0GI-yO6qWwuAP7eaoQz9Q8s4xZyAOjIv/s1600/IMG_20160121_124548.jpg" imageanchor="1"><img border="0" height="213" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhiOHtUI3evsQEd131EurnEfvWF9uaYAHP2oMTXlP7E9eqwd4KrTFMmBn74oiBxNNy0Z6Grcw-LO6EI8Mqoe6EyHT4YtctxYKp47S5x9TmG3kig0GI-yO6qWwuAP7eaoQz9Q8s4xZyAOjIv/s400/IMG_20160121_124548.jpg" width="400" /></a><br />
右側「Hello」の言葉が載っている冊子の束の中に発見。<br />
<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEglTm-wurXtFWnbLhOLP4oIQdoDrEuaj8_OleXs-pQmfshvOvTaVkPLb4DPGuGl_ME-vfVOFTfYEFXI5YTW2k_Tccxm77KqfjGH5lCD390FtMu3n9ccwoLAMCleby2rjDG1bErCwQWC88sr/s1600/IMG_20160121_124643.jpg" imageanchor="1" ><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEglTm-wurXtFWnbLhOLP4oIQdoDrEuaj8_OleXs-pQmfshvOvTaVkPLb4DPGuGl_ME-vfVOFTfYEFXI5YTW2k_Tccxm77KqfjGH5lCD390FtMu3n9ccwoLAMCleby2rjDG1bErCwQWC88sr/s400/IMG_20160121_124643.jpg" /></a><br />
<br />
<br />
回収の申し込みにはリサイクルマークのシールに付いているIDが必要で、それが無いと有料回収になります。再発行もできないので、こういうケースは捨ててはなりませぬな。misahothttp://www.blogger.com/profile/08782505873150853833noreply@blogger.com0tag:blogger.com,1999:blog-8752167966854854944.post-1574909976668505172015-12-17T14:45:00.000+09:002015-12-17T14:45:09.300+09:00Android Wear で Notification をすぐに表示するために必要なことスマホで表示される通知が Android Wear でどのように表示されるのかを確認したいのに、なぜか Wear で表示されないという状況に嵌ったので、その解決メモ。<br />
<br />
<big>環境</big><br />
<br />
<ul><li>Handheld:Nexus 5(OS:6.0.1 Marshmallow)</li>
<li>Wearable:Sony SmartWatch 3(OS:5.1.1)</li>
<ul><li>通知表示:すべて</li>
<li>シアターモード:OFF</li>
</ul></ul><br />
※Nexus 5 と SmartWatch 3 は接続確立済み<br />
<br />
<br />
接続が確立されていたら、スマホ側で表示された通知は Wear 側でも表示されるはず。それがスマホでは表示されているのに Wear は無反応。<br />
<br />
<br />
<big>解決</big><br />
<br />
原因は、Wear へ届く通知には優先順位があり、<b>バイブする or 音を鳴らす 通知でない限りは、すぐに Wear へ通知が届かない</b>からだった。<br />
<br />
バイブや音を鳴らさない通知の場合、5分後など適当な間隔で Wear へ通知が届き、しれっとカードが表示されているという状態になる。それに気づかなかったので、通知が届かない〜!!とハマった。<br />
<br />
すぐに Wear へ通知を出したい場合は、バイブや音を鳴らすように注意するべし!<br />
<br />
<br />
以下、動作確認に使用したサンプルコード。<br />
<br />
<script src="https://gist.github.com/misahot/cebdb1453d4476baad65.js"></script>misahothttp://www.blogger.com/profile/08782505873150853833noreply@blogger.com0tag:blogger.com,1999:blog-8752167966854854944.post-46299013759770573712015-08-06T12:19:00.003+09:002015-08-06T12:19:59.657+09:00Amazon.comへ法人でTax Informationを登録する<a href="https://developer.amazon.com/public">Amazon Apps & Services Developer Portal</a> へデベロッパー登録するためには、amazon.comのアカウントが必要になる。<br />
<br />
amazon.co.jpでアカウントを作っていても、amazon.comとamazon.co.jpではアカウントは共通化されていないので、amazon.co.jpのアカウントはamazon.comでは使用できない。amazon.comでは新規にアカウントを作成する必要がある。<br />
<br />
更に、amazon.comでアカウントを作成する際、広告やIn-App課金などを想定している場合は、Tax Identityへの登録も要求される。やり方は、Tax Information Interviewに答えながらフォームを埋めていく形になるのだが、これがなかなか厄介だった。<br />
<br />
<br />
<h2>Tax Information Interview</h2><br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjMT3QYo3n0zIO-4VVITDMcvvuRKrnDuzQNGVgZOquf_TvLlOW-ruoR5XkwKwEzaDzx6HWNH7LvygOC6XI2lDhAuGPCmfVZ05iRtHaY0IE9UNQdN_VNEvMAN0W7aPm3Qdttb0vrsFKlePsT/s1600/1.%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AA%25E3%2583%25BC%25E3%2583%25B3%25E3%2582%25B7%25E3%2583%25A7%25E3%2583%2583%25E3%2583%2588_2015-08-05_17_39_04.png" imageanchor="1"><img border="0" height="313" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjMT3QYo3n0zIO-4VVITDMcvvuRKrnDuzQNGVgZOquf_TvLlOW-ruoR5XkwKwEzaDzx6HWNH7LvygOC6XI2lDhAuGPCmfVZ05iRtHaY0IE9UNQdN_VNEvMAN0W7aPm3Qdttb0vrsFKlePsT/s400/1.%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AA%25E3%2583%25BC%25E3%2583%25B3%25E3%2582%25B7%25E3%2583%25A7%25E3%2583%2583%25E3%2583%2588_2015-08-05_17_39_04.png" width="400" /></a><br />
<br />
米国市民か?に「No」を選択。「Save and continue」ボタンをクリック。<br />
<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj0RaqW0DCUCVQhrEJshQvLmXRsikwMP-FoX3MAqjJOzwW31fFI1wq3hNWnah1ksu13KbHyBqpivjsvY8Zqx6s5hWdIAb4aon8Hn0ri4TTUhPvnVqhyphenhyphena6twou64VP1e38Lu-LcHlzP9Rg6R/s1600/2.20150805175937.png" imageanchor="1"><img border="0" height="260" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj0RaqW0DCUCVQhrEJshQvLmXRsikwMP-FoX3MAqjJOzwW31fFI1wq3hNWnah1ksu13KbHyBqpivjsvY8Zqx6s5hWdIAb4aon8Hn0ri4TTUhPvnVqhyphenhyphena6twou64VP1e38Lu-LcHlzP9Rg6R/s400/2.20150805175937.png" width="400" /></a><br />
<br />
Type of beneficial owner(受益権所有者のタイプ):Corporation<br />
Place of organization:Japan<br />
を選択。<br />
<br />
Organization nameには、企業名を入力。<br />
主に個人事業主用のオプションなので入力は必要ないかなと思ったけれど、一応、代表者名をDisregarded entity nameへ入力。<br />
<br />
「Are you an agent acting as an intermediary?(仲介業者的なことをするエージェントなの?)」には「No」を選択。<br />
<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhS8XdHnRIV6B0b-sG4s5MlVafcFQGv7oZfvn450rYhare7mEd6FUsuQKwFbRtx_4ZlP8Vux8fDPcXxSu61YhE_oEDYKDrBwxPTc2uDMgUtCjEVsFq-1VSeGku0jBr8gU58zCcKI2c5N7PF/s1600/3.20150805180857.png" imageanchor="1"><img border="0" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhS8XdHnRIV6B0b-sG4s5MlVafcFQGv7oZfvn450rYhare7mEd6FUsuQKwFbRtx_4ZlP8Vux8fDPcXxSu61YhE_oEDYKDrBwxPTc2uDMgUtCjEVsFq-1VSeGku0jBr8gU58zCcKI2c5N7PF/s400/3.20150805180857.png" width="345" /></a><br />
<br />
Type of beneficial ownerにCorporationを選択すると、住所などを入力するフォームが表示されるので入力する。<br />
<br />
Mailing address(郵便を出す時の宛先住所)はPermanent addressと同じなので、「Same as permanent address」を選択。<br />
<br />
Tax identification number(TIN)、つまり納税者番号は日本には無いので、「I do not have a U.S. TIN or a foreign (non-U.S.) income tax identification number」を選択。<br />
<br />
「Save and continue」ボタンをクリックして次に進むと、ここまでの入力確認(レビュー)が表示される。<br />
<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj_ss02A4RywvKkUq9gu95NhYyLzqmxAxZJ3cQZB1GmyNhGs1yd_9pNx4Tb6FToDOPipjKhvuEq7FhjGEbFBCTPA86QVNgkHepVpyO-p8iNL6s_6ENWocl12g_zor24SBoENDYrFw3qct8_/s1600/4.20150806103523.png" imageanchor="1"><img border="0" height="360" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj_ss02A4RywvKkUq9gu95NhYyLzqmxAxZJ3cQZB1GmyNhGs1yd_9pNx4Tb6FToDOPipjKhvuEq7FhjGEbFBCTPA86QVNgkHepVpyO-p8iNL6s_6ENWocl12g_zor24SBoENDYrFw3qct8_/s400/4.20150806103523.png" width="400" /></a><br />
<br />
非米国法人の基本的なフォーマット「W-8BEN-E」の証明書類が選択されている。<br />
<br />
ここでは、確認のみで編集は出来ないので、修正をする場合はページ下の「Previous」ボタンをクリックして、戻って修正する。<br />
<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjYlETDHS98FOAGXjGk7BUo2wcGLoPBKJAX_yVXXdNPX3qjlnS56Ku-xSe0TydZvSe9_1wa37MNl1fmz30upi0O7ZV13mq_UQvRZ1ywAf9gcU6WkVHyAgp5KcLeW26MSeoS6A1hW-52Pmq8/s1600/5.20150806103924.png" imageanchor="1"><img border="0" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjYlETDHS98FOAGXjGk7BUo2wcGLoPBKJAX_yVXXdNPX3qjlnS56Ku-xSe0TydZvSe9_1wa37MNl1fmz30upi0O7ZV13mq_UQvRZ1ywAf9gcU6WkVHyAgp5KcLeW26MSeoS6A1hW-52Pmq8/s400/5.20150806103924.png" width="376" /></a><br />
<br />
Type of beneficial ownerにCorporationがチェックされていることを確認。<br />
<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg8YzJoIOx_qPBy7jxyJgge9LnW29VVLREvBbHLXaDTQCLT81ETUFtv4QS0f-5eVhIMRTSHQ4wThS7NOyIOXYI1mImca0LgtQpfNRpL5mECWrxvoNT5wi_kJBQsdGUtkf5r77mWjRxAxlTF/s1600/6.%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AA%25E3%2583%25BC%25E3%2583%25B3%25E3%2582%25B7%25E3%2583%25A7%25E3%2583%2583%25E3%2583%2588_2015-08-05_17_41_30.png" imageanchor="1"><img border="0" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg8YzJoIOx_qPBy7jxyJgge9LnW29VVLREvBbHLXaDTQCLT81ETUFtv4QS0f-5eVhIMRTSHQ4wThS7NOyIOXYI1mImca0LgtQpfNRpL5mECWrxvoNT5wi_kJBQsdGUtkf5r77mWjRxAxlTF/s400/6.%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AA%25E3%2583%25BC%25E3%2583%25B3%25E3%2582%25B7%25E3%2583%25A7%25E3%2583%2583%25E3%2583%2588_2015-08-05_17_41_30.png" width="315" /></a><br />
<br />
次に進むには「Save and continue」ボタンをクリック。<br />
<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjKmgtI6Gs8w5tmwrCskRhDPFmVdFnUIo7iILeoFJ_51h_3vAV_tjSY68JKjvAUiq0GEm7sc8VpEX0YvONVrAzOOlAZbFuv2-VIYs9gbk-ZfhSo4Pg93Q41m5tE6lU_n1fond3F9FXwpg9f/s1600/7.%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AA%25E3%2583%25BC%25E3%2583%25B3%25E3%2582%25B7%25E3%2583%25A7%25E3%2583%2583%25E3%2583%2588_2015-08-05_17_41_46.png" imageanchor="1"><img border="0" height="306" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjKmgtI6Gs8w5tmwrCskRhDPFmVdFnUIo7iILeoFJ_51h_3vAV_tjSY68JKjvAUiq0GEm7sc8VpEX0YvONVrAzOOlAZbFuv2-VIYs9gbk-ZfhSo4Pg93Q41m5tE6lU_n1fond3F9FXwpg9f/s400/7.%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AA%25E3%2583%25BC%25E3%2583%25B3%25E3%2582%25B7%25E3%2583%25A7%25E3%2583%2583%25E3%2583%2588_2015-08-05_17_41_46.png" width="400" /></a><br />
<br />
「I consent to electronic receipt of my information reporting documentation(税務情報を電子的に送ることに同意する)」を選択。<br />
<br />
「I consent to provide my electronic signature(電子署名を提供することに同意する)」を選択。更に、Electronic signatureについての詳細が表示されるので、全てにチェックを入れる。<br />
<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjwh710Ni_7OZ5lvVosLe4fqYrIUpQWETrS-EILlaW2vF85qE2igkVF73ruvrblg_yj4DjVniRF7W110Af5qgJfqsJyN_Yb9ThXmTJgQ0fZyX_WPP7qoBRes8JW4asTQqpsxQnBWAHDPHw4/s1600/8.%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AA%25E3%2583%25BC%25E3%2583%25B3%25E3%2582%25B7%25E3%2583%25A7%25E3%2583%2583%25E3%2583%2588_2015-08-05_17_42_03.png" imageanchor="1"><img border="0" height="356" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjwh710Ni_7OZ5lvVosLe4fqYrIUpQWETrS-EILlaW2vF85qE2igkVF73ruvrblg_yj4DjVniRF7W110Af5qgJfqsJyN_Yb9ThXmTJgQ0fZyX_WPP7qoBRes8JW4asTQqpsxQnBWAHDPHw4/s400/8.%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AA%25E3%2583%25BC%25E3%2583%25B3%25E3%2582%25B7%25E3%2583%25A7%25E3%2583%2583%25E3%2583%2588_2015-08-05_17_42_03.png" width="400" /></a><br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj07Aa-sY0TD6bPhIWh1jqrtpPP9Sjt8MDbBhCheVAVMO-d2m_NOmIYxC38-X7NAP2srCtp22M_27BpKEe9IBsXvd3PuumEL8IX1eEJx0bxyerl5YbIyK6O7JIc_sf7nigWif85U_pgdBd-/s1600/9.20150806104617.png" imageanchor="1"><img border="0" height="266" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj07Aa-sY0TD6bPhIWh1jqrtpPP9Sjt8MDbBhCheVAVMO-d2m_NOmIYxC38-X7NAP2srCtp22M_27BpKEe9IBsXvd3PuumEL8IX1eEJx0bxyerl5YbIyK6O7JIc_sf7nigWif85U_pgdBd-/s400/9.20150806104617.png" width="400" /></a><br />
<br />
「 Signature of individual authorized to sign for beneficial owner」の欄には代表者名を入力。<br />
<br />
「E-mail address」にはamazon.comへ登録したものと同じメールアドレスを入力。<br />
<br />
「I certify that I have the capacity to sign for the entity identified on line 1 of this form.」をチェック。このフォームの1行目とは企業名が入力されている部分。その企業を代表してサインする能力が私にあることを証明するという内容。そこまで断定的に言われると、ちょっとビビってしまいますが、チェックしましょう(^_^;)<br />
<br />
「Submit」ボタンをクリックすると、登録完了!<br />
<br />
<br />
<h2>登録完了後の確認</h2>Developer ConsoleからログインしてSETTING > Tax Identity を確認してみましょう。<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPwajroMXSZyEQXIfB54a_CoVZali-emAAt9aMO7x1uv4WhY81daG27h4e0Y7ZdHFqyjc5zS74oWNxFlH_KTJ8acGRyl7yNSeWQzek_qOe365xnwMd_O7UViSSGy3ABqq5k97qkiMqTljk/s1600/10.%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AA%25E3%2583%25BC%25E3%2583%25B3%25E3%2582%25B7%25E3%2583%25A7%25E3%2583%2583%25E3%2583%2588_2015-08-05_17_43_06.png" imageanchor="1"><img border="0" height="170" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPwajroMXSZyEQXIfB54a_CoVZali-emAAt9aMO7x1uv4WhY81daG27h4e0Y7ZdHFqyjc5zS74oWNxFlH_KTJ8acGRyl7yNSeWQzek_qOe365xnwMd_O7UViSSGy3ABqq5k97qkiMqTljk/s400/10.%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AA%25E3%2583%25BC%25E3%2583%25B3%25E3%2582%25B7%25E3%2583%25A7%25E3%2583%2583%25E3%2583%2588_2015-08-05_17_43_06.png" width="400" /></a><br />
<br />
Validationは、1時間もせずに完了しました。misahothttp://www.blogger.com/profile/08782505873150853833noreply@blogger.com0tag:blogger.com,1999:blog-8752167966854854944.post-78654936171939247642015-07-24T13:49:00.001+09:002015-08-10T14:02:59.224+09:00ソーシャルボタンのアイコンをSVGファイル化するソーシャル系のガジェットやスクリプトは、多数のリソースを読み込むなど、ユーザーの体感スピードを劣化される。デザインも統一感が、なかなか出せない。<br />
<br />
よって、自作しよう!となった時に、ソーシャルボタンのSVGファイルが欲しくなって調査した忘備録。<br />
<br />
<br />
<h2>1. Twitter、Facebook、Google+ のSVGファイル作成</h2><br />
これら3つは、font-awesomeの中にアイコンがあるので、「<a href="https://github.com/encharm/Font-Awesome-SVG-PNG">encharm/Font-Awesome-SVG-PNG</a>」を使う。<br />
<br />
<br />
<h3>環境</h3><br />
<ul><li>Mac OS X Yosemite 10.10.4</li>
<li>npm 2.5.1</li>
<li><a href="https://github.com/encharm/Font-Awesome-SVG-PNG">Font-Awesome-SVG-PNG</a> 1.1.2</li>
</ul><br />
<h3>手順</h3><br />
<h4>Font-Awesome-SVG-PNG のインストール</h4><pre class="brush: bash/shell; gutter: false;">$ npm install -g font-awesome-svg-png
</pre><br />
<h4>インストール確認</h4><pre class="brush: bash/shell; gutter: false;">$ npm ls --depth=0 -g
</pre><br />
インストールしたディレクトリ下の、bower_components/font-awesome-svg-png/には、black/ と white/ のディレクトリがあるので、黒や白のSGVファイルが欲しい場合は、そこからゲットすればよい。<br />
<br />
赤など、黒白以外のカラーのSVGファイルが欲しい場合は、コマンドで書き出せる。<br />
<br />
<h4>SVGファイルを書き出し</h4>オプションの例<br />
<ul><li>--dest: 出力パス</li>
<li>--color: 赤</li>
<li>--sizes: 80px</li>
<li>--icons: Twitter, Facebook, Google+</li>
<li>--no-png: SVGオンリー</li>
</ul><pre class="brush: bash/shell; gutter: false;">$ font-awesome-svg-png --dest font-awesome --color red --sizes 80 --icons twitter,facebook,google-plus --no-png
</pre><b><span style="color: red;">※--iconsの指定では、カンマの後ろにスペースを挟まない</span></b><br />
<br />
<br />
<h2>2. はてブ、LINE、Pocket のSVGファイル作成</h2><br />
これら3つは、font-awesomeの中にアイコンが無い。既にアイコンのパスをトレースしてSVG化してくれている方がいたので、ありがたく頂戴する。<br />
<br />
<br />
<h3>参考</h3><ul><li><a href="http://douraku.sw2x.com/web/3868/">ブログのデザインでも使用している「Webフォントアイコン」を本当にオススメしたい! | ドウラク</a></li>
</ul><br />
<br />
<h3>補足</h3><br />
これ、font-awesomeにあったっけ?という時は、<a href="http://fontawesome.io/icons/">font-awesomeのサイトで検索</a>するとよい。misahothttp://www.blogger.com/profile/08782505873150853833noreply@blogger.com0tag:blogger.com,1999:blog-8752167966854854944.post-76746905547684207352015-04-06T13:51:00.000+09:002015-04-06T13:53:21.702+09:00GAE/J上のGoogle Cloud EndpointsをバックエンドにしてSupersonicのdataアクセスを試してみた<br />
<h3>Supersonic data セットアップ</h3><br />
<br />
<h4>1. データプロバイダの追加</h4><br />
プロジェクトディレクトリ下でconnectを発動し、プロジェクトのコネクトスクリーンを開く。<br />
<pre class="brush: bash/shell">$ steroids connect
</pre><br />
コネクトスクリーンの"Data"タブで「Custom Provider」を選択。<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjWATDXnlqH6CuKX4KOib_0A5GnpYLTQsILYhfvXXE3lsQDuCUETDMCpJac8UU_A8McDeTmThxA8s7GTzOrY4-gZUlKPnPKQplsUeRW4zS1jkUi0eXrtemCD7VyUACjgCGyCbHMyk7SvS-z/s1600/selectProviders.png" imageanchor="1" ><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjWATDXnlqH6CuKX4KOib_0A5GnpYLTQsILYhfvXXE3lsQDuCUETDMCpJac8UU_A8McDeTmThxA8s7GTzOrY4-gZUlKPnPKQplsUeRW4zS1jkUi0eXrtemCD7VyUACjgCGyCbHMyk7SvS-z/s640/selectProviders.png" /></a><br />
<br />
<br />
NAMEに任意のデータプロバイダ名を入力。<br />
BASE URLは「https://YOUR_APP.appspot.com/_ah/api/」まで設定。v1などバージョンナンバーまでのパスにしない(後述のリソース追加設定のため)。<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjwwQbB47Jkhgznz8FwYroHPKnlwBlM6MjELipn8Jl09RImToccmFjFotI9mrec_Vq9TmskOLDIy5HQSE8iH2vag5l7Sof9RDfDpfpf8KJsenP9Xt9tFIW9j353Mhg67qTN1kiC-qkupmPp/s1600/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88+2015-04-06+12.50.49.png" imageanchor="1" ><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjwwQbB47Jkhgznz8FwYroHPKnlwBlM6MjELipn8Jl09RImToccmFjFotI9mrec_Vq9TmskOLDIy5HQSE8iH2vag5l7Sof9RDfDpfpf8KJsenP9Xt9tFIW9j353Mhg67qTN1kiC-qkupmPp/s640/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88+2015-04-06+12.50.49.png" /></a><br />
<br />
また、Google Cloud Endpointsはhttpsアクセスしか認めていないので、http://ではなく、https://にしておく。<br />
<br />
<br />
<h4>2. リソースの追加</h4><br />
「ADD NEW RESOURCE」ボタンをクリックして、リソースを設定する。<br />
<br />
NAMEに任意のリソース名を入力。<br />
URL PATHは「yourApi/v1/」を入力。<br />
例えば、API名が「playlistApi」、バージョンが「v1」の場合、URL PATHは「playlistApi/v1/」になる。<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhi72ma-24oNvot9r1dzaeznQIADLt7Y2y9Lf8D2qAZdRnf5wY6JTm599FzNcAlSrf9D0Ai-rizuMIzGDuJfeaAFHNWDwBFas8rr-cypaaGmNW34IK6qQj0xFDIHOyvQ1otJ45UkA-tBkqC/s1600/addResource.png" imageanchor="1" ><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhi72ma-24oNvot9r1dzaeznQIADLt7Y2y9Lf8D2qAZdRnf5wY6JTm599FzNcAlSrf9D0Ai-rizuMIzGDuJfeaAFHNWDwBFas8rr-cypaaGmNW34IK6qQj0xFDIHOyvQ1otJ45UkA-tBkqC/s640/addResource.png" /></a><br />
<br />
前述のプロバイダのBASE URLで「〜/v1/」まで含めてしまうと、必須入力であるリソースのURL PATHに何も設定できなくなるので、プロバイダのBASE URLを「api/」までと設定した。<br />
<br />
これにより、リソースへのパスは、BASE URL + RESORCE URL PATH = 「https://YOUR_APP.appspot.com/_ah/api/playlistApi/v1/」となる。<br />
<br />
<br />
<h4>3. アクションのカスタマイズ</h4><br />
「CUSTOMIZE ACTIONS」ボタンをクリックして、get/postなどのリクエストの、URL PATHやROOT KEYSを設定する。<br />
<br />
例えば、コレクションを取得するgetのAPIパスが「playlist」の場合、URL PATHは「playlist」になる。<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEinyYOW2tplp6YD8n5hH_ZxPR__wdelM91pzZehMLhLI2YwafG1Lr7ATUXRuRdhTWorHzDWBe5YCCsSu-2fvmwsJ6vPMCGRwPghnvEXShft-Bc2sk53OGAIl2NqYbplRq6vjbS8CDzr9sF-/s1600/manageActions.png" imageanchor="1" ><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEinyYOW2tplp6YD8n5hH_ZxPR__wdelM91pzZehMLhLI2YwafG1Lr7ATUXRuRdhTWorHzDWBe5YCCsSu-2fvmwsJ6vPMCGRwPghnvEXShft-Bc2sk53OGAIl2NqYbplRq6vjbS8CDzr9sF-/s640/manageActions.png" /></a><br />
<br />
これにより、完成したエンドポイントへのパスは、BASE URL + RESOURCE URL PATH + ACTION URL PATH = 「https://YOUR_APP.appspot.com/_ah/api/playlistApi/v1/playlist」となる。<br />
<br />
<br />
更に、Google Cloud Endpointsのコレクションのルートは「items」なので、ROOT KEYSに「items」を指定する。<br />
<br />
<br />
「RELOAD MODEL FROM API」ボタンをクリックすると、エンドポイントとの通信が行われる。無事にコレクションが取得できると、最新のエンティティモデルの内容が表示される。<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj8Wbg-aOqNfk1Q9KMcFeIR42gMhXUzifAAMI7p316LBGm-aHK5ckADC9IesXUkaujgYhX0HKAcmU779PWCAf7eg4IGsT1YLoTp0cldVXYG0q-08PIyd3DBWRj3UNk9Z7c05xs_ybrlOMWR/s1600/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88+2015-04-06+11.22.57.png" imageanchor="1" ><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj8Wbg-aOqNfk1Q9KMcFeIR42gMhXUzifAAMI7p316LBGm-aHK5ckADC9IesXUkaujgYhX0HKAcmU779PWCAf7eg4IGsT1YLoTp0cldVXYG0q-08PIyd3DBWRj3UNk9Z7c05xs_ybrlOMWR/s640/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88+2015-04-06+11.22.57.png" /></a><br />
<br />
<br />
<br />
<h3>デバイスから data access 確認</h3><br />
<br />
トップ画面には、「https://YOUR_APP.appspot.com/_ah/api/playlistApi/v1/playlist」へアクセスした結果が表示されている。<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi8xuDxh2KbqCbtgvz5JwsA-dy96WK4Z1l9gDXeSEvGaDr0mXQ4beCFJcuv_Co-x20coqOHFspqgWdMHVYwnuz7R8SETBgp8JbK4ukPEwuNx8J28Esa7roATLq8v4iEbFdLEUM8rz7rwGBg/s1600/Screenshot_2015-04-06-13-45-21.png" imageanchor="1" ><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi8xuDxh2KbqCbtgvz5JwsA-dy96WK4Z1l9gDXeSEvGaDr0mXQ4beCFJcuv_Co-x20coqOHFspqgWdMHVYwnuz7R8SETBgp8JbK4ukPEwuNx8J28Esa7roATLq8v4iEbFdLEUM8rz7rwGBg/s640/Screenshot_2015-04-06-13-45-21.png" /></a><br />
<br />
<br />
API Explorerで、dataStoreにエンティティを1つ追加してみる。<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAoDcVZ40VLQ_BRoCN4J7t-hzKhR7o4fVLs3Z0imwXP-5Dqlh6S8BGTQ_qtxKc-OdV0K-4joruWRH2J288R5Dz9Guq_JDCaGG1wzj84-etfOHlOFPtLMGOWE5axlOtDuDEyd68KuS7spLl/s1600/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88+2015-04-06+12.45.22.png" imageanchor="1" ><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAoDcVZ40VLQ_BRoCN4J7t-hzKhR7o4fVLs3Z0imwXP-5Dqlh6S8BGTQ_qtxKc-OdV0K-4joruWRH2J288R5Dz9Guq_JDCaGG1wzj84-etfOHlOFPtLMGOWE5axlOtDuDEyd68KuS7spLl/s640/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88+2015-04-06+12.45.22.png" /></a><br />
<br />
<br />
デバイスで起動しているアプリ側のリストも、数秒後に、自動的に更新される。<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhes1ao0jilYc2lInoVpwDtR3R4vhRiok7kq6uATsNBvAm1t6adBrCKDl5YbPrmt8zoT6fZDNIT9qwib8BTSOJEExJmhJdhGK7uA4TKIfVMZGGsz3GuokurDDUT0Nr5gOqZtH2VWoCq7izN/s1600/Screenshot_2015-04-06-13-45-50.png" imageanchor="1" ><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhes1ao0jilYc2lInoVpwDtR3R4vhRiok7kq6uATsNBvAm1t6adBrCKDl5YbPrmt8zoT6fZDNIT9qwib8BTSOJEExJmhJdhGK7uA4TKIfVMZGGsz3GuokurDDUT0Nr5gOqZtH2VWoCq7izN/s640/Screenshot_2015-04-06-13-45-50.png" /></a><br />
<br />
<br />
<br />
<h3>10秒間隔で同期が発動する</h3><br />
<br />
ログを見ると、ほぼ10秒間隔でエンドポイントが叩かれている。<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJ4SwZ0n40HOJOr8v2rarSY4tHoriOtM2AC25PHnlent_chiho3mY303bxNHH0E7riOLwID_TDXvmMOhpj104Wokogx2W3lxBMU8YY2uUmgUcVKYwpP_XlZ7WHtFCxY9aqKpFiVYkKbtAk/s1600/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88+2015-04-03+16.11.47.png" imageanchor="1" ><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJ4SwZ0n40HOJOr8v2rarSY4tHoriOtM2AC25PHnlent_chiho3mY303bxNHH0E7riOLwID_TDXvmMOhpj104Wokogx2W3lxBMU8YY2uUmgUcVKYwpP_XlZ7WHtFCxY9aqKpFiVYkKbtAk/s640/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88+2015-04-03+16.11.47.png" /></a><br />
<br />
<br />
データバインディングはいいのだが、10秒おきにプルリクエストが発動するのでは、GAEのdataStoreのRead Quotaの消費が激しくなってしまう。<br />
<br />
2015年4月現在の無料枠では、Read Operationsのリミットは50,000回/日なので、<br />
<br />
( 60秒 / 10秒間隔 ) x 60分 = 360回/時<br />
<br />
1時間アプリを起動しているだけで、360回のRead Operationsが発生する。<br />
<br />
50,000回 / 360回 = 138.889<br />
<br />
例えば、同時に138人が1時間アプリを起動しているだけで、50,000回のRead Operationsのクオータはすぐに消費されてしまう。<br />
<br />
<br />
データバインディングは、supersonic.data APIのwhenChanged()で実現されている。<br />
<pre class="brush: bash/javascript">Playlist.all().whenChanged( function (playlists) {
$scope.$apply( function () {
$scope.playlists = playlists;
$scope.showSpinner = false;
});
});
</pre><br />
クオータの消費を避けたいなら、whenChanged()を使わずに、適切なタイミングで自作でデータの同期を行う必要があるなと。<br />
<br />
<br />
参考:<a href="http://docs.appgyver.com/supersonic/guides/data/other-data-providers/custom-rest-backend/">Supersonic Guides - Supersonic Framework</a>misahothttp://www.blogger.com/profile/08782505873150853833noreply@blogger.com0tag:blogger.com,1999:blog-8752167966854854944.post-63853600545619948232015-04-03T12:51:00.001+09:002015-04-03T12:51:06.233+09:00AppGyverのSupersonicを使ってdataアクセスするアプリを作っていて"Module 'common' is not available!"のエラーが出たら<a href="http://www.appgyver.com/">AppGyver</a>が提供しているハイブリッドアプリ開発のフレームワークSupersonic。<br />
<br />
Supersonicを使ってアプリを作っていて、バックエンドのデータへアクセスする部分を組み込んだ途端、<br />
<br />
「<span style="color: red;">Error: [$injector:nomod] Module 'common' is not available! You either misspelled the module name or forgot to load it. If registering a module ensure that you specify the dependencies as the second argument.</span>」<br />
<br />
というエラーが出て、ハマってしまったので、その備忘録。<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjmEG2tBz6zn3AR6Jl1Os3sqjUJOtl7pfx5QFrFbwoAD2mqRI0IqGUgc7fyL_TYPAFjPKUY4nJaEr-0u4xR_2X0RL3cGdt5c6cun74QpRRb449vMmoE-UZDL43NNsmHanuFAYUhVYGOfY8F/s1600/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88+2015-04-03+11.55.46.png" imageanchor="1"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjmEG2tBz6zn3AR6Jl1Os3sqjUJOtl7pfx5QFrFbwoAD2mqRI0IqGUgc7fyL_TYPAFjPKUY4nJaEr-0u4xR_2X0RL3cGdt5c6cun74QpRRb449vMmoE-UZDL43NNsmHanuFAYUhVYGOfY8F/s1600/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88+2015-04-03+11.55.46.png" height="274" width="640" /></a><br />
<br />
アクセスしようとしたデータもnot found状態になる。<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgoHRiueOgYLcu6KHQLnHpb7Y-rLX1pTjO8x92lpk014AKOk0_AYxO6n8cTtjTwJ71SpihVHAEpwFbJjuSw59CJeEXPPH-cFqv3SJWw611Ig0b1uASKPVcK_RQxl20XM4SUSxcQO-QoyhfY/s1600/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88+2015-04-03+11.56.31.png" imageanchor="1"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgoHRiueOgYLcu6KHQLnHpb7Y-rLX1pTjO8x92lpk014AKOk0_AYxO6n8cTtjTwJ71SpihVHAEpwFbJjuSw59CJeEXPPH-cFqv3SJWw611Ig0b1uASKPVcK_RQxl20XM4SUSxcQO-QoyhfY/s1600/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88+2015-04-03+11.56.31.png" height="400" width="291" /></a><br />
<br />
<br />
<br />
ここで要求されている「common」モジュールは、プロジェクトのディレクトリ下のapp/common/。<br />
<br />
<a href="http://docs.appgyver.com/supersonic/guides/architecture/app-architecture/#utilising-the-common-module">Supersonic Guides - Supersonic Framework Utilising the common module</a><br />
<blockquote>The common module also already contains a layout file (located at app/common/views/layout.html), which has all the basic dependecies of your project declared, such as supersonic.js, steroids.js and cordova.js. Because common is declared as a dependency to all modules by default, the layout file in there can be used to throughout the app just by removing any layout files from the module itself.</blockquote><br />
Supersonicを使ったアプリのレイアウトなど基本的な部分で使われているモジュール。<br />
<br />
自分のプロジェクトには、確かにそのディレクトリが無い。<br />
<br />
<br />
レイアウトという時点で、もしやと思ったのは、シンプルページアプリケーションとして、プロジェクトを作ったことがボトルネックになっているのではないか、マルチページアプリケーションとして、そもそもプロジェクトを作っていないと、このcommonモジュールはapp下にデプロイされないんじゃないか?ということ。<br />
<br />
<br />
試しに、プロジェクトをマルチページで、一から作りなおしてみる。<br />
<br />
<pre class="brush: bash/shell">$ steroids create プロジェクト名
</pre><br />
続くアプリタイプの選択で、デフォルトのマルチページタイプを選択。<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgRUJIdAkxHXkC2lzbGI502FuB-IUkg5lJ_BsJp9quwP4VJhCgP8hMHOCYocXJ25SwDeNG85Prq6ALJlZaG59BRo9kBfH-AH6eY0Igug_IuJssXQADD9g2hXxbKzZv_LOJOVBbI985jvsjx/s1600/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88+2015-04-03+12.26.20.png" imageanchor="1"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgRUJIdAkxHXkC2lzbGI502FuB-IUkg5lJ_BsJp9quwP4VJhCgP8hMHOCYocXJ25SwDeNG85Prq6ALJlZaG59BRo9kBfH-AH6eY0Igug_IuJssXQADD9g2hXxbKzZv_LOJOVBbI985jvsjx/s1600/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88+2015-04-03+12.26.20.png" height="189" width="640" /></a><br />
<br />
改めて、新しく作ったプロジェクトで、データのリポジトリを作り、テストデータを入れてみたら、今度はエラーが出ない!<br />
<br />
テストデータも表示されている。<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjBwI8tEL_bEtT6GsS2cALMFJdDkCH07cqt29M5mMzag_v-ICwg7JCD1kZ-XmIcqG1I-68wntl14KPd-EN58AjGnGZv6XQhjDpIR37HJX1OtfWzTUAKsXWXitBZVGb48BCNXZle9OGnkCHf/s1600/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88+2015-04-03+12.28.29.png" imageanchor="1"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjBwI8tEL_bEtT6GsS2cALMFJdDkCH07cqt29M5mMzag_v-ICwg7JCD1kZ-XmIcqG1I-68wntl14KPd-EN58AjGnGZv6XQhjDpIR37HJX1OtfWzTUAKsXWXitBZVGb48BCNXZle9OGnkCHf/s1600/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88+2015-04-03+12.28.29.png" height="400" width="292" /></a><br />
<br />
<br />
commonモジュールが無いエラーで嵌ったら、プロジェクトをマルチページタイプで作りなおせばOKです。<br />
<br />
<br />
シングルページタイプのアプリでSueprsonicのdata APIが使えないということでは無いと思います。<br />
<br />
あくまでも、Supersonicのチュートリアルを試していて、データのハンドリングをやりたいと思っただけなのに、commonモジュールにはまってしまった場合の対処方法です。misahothttp://www.blogger.com/profile/08782505873150853833noreply@blogger.com0tag:blogger.com,1999:blog-8752167966854854944.post-45003971999659402812015-03-14T18:26:00.003+09:002015-03-14T18:44:13.302+09:00Android端末でとったスクリーンショットが「フォト」で同期されないと思ったらAndroid端末でとったスクリーンショットが、PCのブラウザから「<a href="https://plus.google.com/u/0/photos/yourphotos">フォト</a>」へアクセスしても見当たらないなと思ったら、自動バックアップがオンになっていても、スクリーンショットのディレクトリはデフォルトでは同期の対象外になっていました。<br />
<br />
スクリーンショットのディレクトリを同期対象に含めるには以下の方法で。<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhRAhIuoKe227cwzKHWkWav477wlcRWc12tsrihLdOPxl5j57QSESK_SilQhYz_tlrj0sadtAFw262UEqMQO_iMhhr-TTNytwea1JimPe6uAfjhpbFNwpoWQ134y-5KAWfqljIdzNDSnO0U/s1600/20150314175306.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhRAhIuoKe227cwzKHWkWav477wlcRWc12tsrihLdOPxl5j57QSESK_SilQhYz_tlrj0sadtAFw262UEqMQO_iMhhr-TTNytwea1JimPe6uAfjhpbFNwpoWQ134y-5KAWfqljIdzNDSnO0U/s320/20150314175306.png" /></a></div>Android端末で「フォト」を起動。「メニュー」から「端末内」を選択。<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiNcIwRslkau_-K6jQSzhsXH54A-g3YYZoKh5erZFnh1Pvh9f_U3kFdfNg3nOlRfCdQXxCkU1rXYxm8-479gsiarLTnMv12SMOjJ_5jblqWbdSv3d3FIiKjJcSldxaXdH3BAj2XnPJNwu_3/s1600/20150314182154.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiNcIwRslkau_-K6jQSzhsXH54A-g3YYZoKh5erZFnh1Pvh9f_U3kFdfNg3nOlRfCdQXxCkU1rXYxm8-479gsiarLTnMv12SMOjJ_5jblqWbdSv3d3FIiKjJcSldxaXdH3BAj2XnPJNwu_3/s320/20150314182154.png" /></a></div>Screenshotsのクラウドアイコンがオフになっているので、タップしてオンに変更。<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiufdqmF4H47vsxAyTanEsQu46SD3Jf1S07iyCjfVxStk9oHwS8CvZEK9SV2_caofOhBLuWS5-pganylvbGEIX-diJznsRhsBlGHadgMm6VfdxTLlk0ALgI9Mvhnr3eZgtMWpbvOcUTEoWV/s1600/20150314182232.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiufdqmF4H47vsxAyTanEsQu46SD3Jf1S07iyCjfVxStk9oHwS8CvZEK9SV2_caofOhBLuWS5-pganylvbGEIX-diJznsRhsBlGHadgMm6VfdxTLlk0ALgI9Mvhnr3eZgtMWpbvOcUTEoWV/s320/20150314182232.png" /></a></div>同期対象になりました。<br />
<br />
<br />
以降は、スクリーンショットを撮る度に、PCのブラウザからGoogle+ の「<a href="https://plus.google.com/u/0/photos/yourphotos">フォト</a>」へアクセスすると、すぐに画像が表示されます。misahothttp://www.blogger.com/profile/08782505873150853833noreply@blogger.com0tag:blogger.com,1999:blog-8752167966854854944.post-21844247057395949632015-02-27T17:33:00.000+09:002015-02-27T17:51:13.926+09:00npm installしたパッケージの名前だけを取得するローカルにインストールしたパッケージの名前だけを取得したいときのコマンド。<br />
<br />
<pre class="brush: bash/shell">$ npm ls --depth=0
</pre><br />
実行すると、以下のように、トップレベルの名前だけ取得できる。<br />
<pre class="brush: bash/shell">$ npm ls --depth=0
├── browser-sync@2.2.1
├── gulp@3.8.11
├── gulp-autoprefixer@2.1.0
├── gulp-concat@2.5.2
├── gulp-imagemin@2.2.1
├── gulp-less@3.0.1
├── gulp-minify-css@0.4.6
├── gulp-minify-html@1.0.0
├── gulp-newer@0.5.0
├── gulp-plumber@0.6.6
├── gulp-sass@1.3.3
├── gulp-uglify@1.1.0
├── rimraf@2.2.8
└── run-sequence@1.0.2
</pre><br />
トップレベルだけでなく、もう一階層深く知りたいときは<br />
<pre class="brush: bash/shell">$ npm ls --depth=1
</pre><br />
つまりdepthを増やしていけばいい。<br />
<br />
<br />
グローバルにインストールされたパッケージを取得したいときは-gを付けるだけ。<br />
<pre class="brush: bash/shell">$ npm ls --depth=0 -g
/usr/local/lib
└── npm@2.5.1
</pre>misahothttp://www.blogger.com/profile/08782505873150853833noreply@blogger.com0tag:blogger.com,1999:blog-8752167966854854944.post-59401024886424467132015-02-27T17:17:00.000+09:002015-02-27T17:18:22.258+09:00MacでNode.jsとnpmをアンインストールするには Node.jsをMacからアンインストールするには、以下のシェルスクリプトを実行する。<br />
<pre class="brush: bash/shell">lsbom -f -l -s -pf /var/db/receipts/org.nodejs.pkg.bom \
| while read i; do
sudo rm /usr/local/${i}
done
# uninstall node
sudo rm -rf /usr/local/lib/node \
/usr/local/lib/node_modules \
/var/db/receipts/org.nodejs.*;
# uninstall npm
sudo rm -rf ~/.npm;
# uninstall check
node -v;
npm -v;
</pre><br />
最後にnodeやnpmコマンドを実行して失敗することを確認している。<br />
<br />
<br />
上記のコードを、「uninstall-node.sh」などのファイル名で保存して、実行権限を付与。<br />
<pre class="brush: bash/shell">$ chmod +x ./uninstall-node.sh
</pre><br />
実行すると、Node.js、npmのアンインストールが完了する。<br />
<pre class="brush: bash/shell">$ ./uninstall-node.sh
</pre><br />
<br />
<big>再インストールするには</big><br />
<br />
<a href="http://nodejs.org/download/">公式のダウンロードサイト</a>からpkgをダウンロードしてNode.jsを再インストールする。homebrewやnodebrewなどからインストールする方法もあるが、公式のダウンロードサイトからpkgをダウンロードしてインストールする方法の方が確実だと感じた。<br />
<br />
再インストールが完了したら、念の為バージョン確認。<br />
<pre class="brush: bash/shell">$ node -v
v0.12.0
$ npm -v
2.5.1
</pre><br />
<br />
<big>アンインストールに至った経緯</big><br />
<br />
そもそも、Node.jsを再インストールして白紙からやり直そうと思ったのは、npm install 実行中に色々とエラーが発生したから。<br />
<br />
例えば、gulp-sass@1.3.3をインストールでは、以下のエラーが発生。<br />
<pre class="brush: bash/shell">Error: `libsass` bindings not found. Try reinstalling `node-sass`?
at getBinding (/path/to/my/project/node_modules/gulp-sass/node_modules/node-sass/lib/index.js:21:11)
at Object.<anonymous> (/path/to/my/project/node_modules/gulp-sass/node_modules/node-sass/lib/index.js:181:23)
at Module._compile (module.js:456:26)
at Object.Module._extensions..js (module.js:474:10)
at Module.load (module.js:356:32)
at Function.Module._load (module.js:312:12)
at Module.require (module.js:364:17)
at require (module.js:380:17)
at Object.<anonymous> (/path/to/my/project/node_modules/gulp-sass/index.js:3:17)
at Module._compile (module.js:456:26)
</pre><br />
更に、browser-sync@2.2.1のタスク実行では、以下のエラーが発生。<br />
<pre class="brush: bash/shell">dyld: Symbol not found: _node_module_register
Referenced from: /path/to/my/project/node_modules/browser-sync/node_modules/socket.io/node_modules/engine.io/node_modules/ws/build/Release/bufferutil.node
Expected in: dynamic lookup
</pre><br />
<br />
stackoverflowを見ると、Node.jsのバージョンが関係しているようだったので、クリーンな状態に一旦戻してからnpm installを試してみようと。<br />
<br />
クリーンな状態から、再度、npm installを実行すると、gulp-sassもbrowser-syncも、問題なくローカルにインストールできた。misahothttp://www.blogger.com/profile/08782505873150853833noreply@blogger.com0tag:blogger.com,1999:blog-8752167966854854944.post-66222684822131137312015-02-26T17:06:00.000+09:002016-02-03T11:40:40.298+09:00Git - gulpのパッケージをgitで共有したくなかったらgulp、そしてgulpで利用するパッケージだけで、かなりディレクトリ容量が膨らむ。<br />
これをgitで共有するのは嫌だなあと思って試行錯誤。<br />
<br />
とりあえず、グローバルとローカルの両方にgulpをインストールするのではなく、グローバルだけにできないかと思ったが、逆(<a href="http://protean.cc/getting-started-with-gulp-and-npm">ローカルのみインストールする</a>)はあっても、グローバルのみで動かす設定は発見できず。<br />
<br />
gulp.jsのフローを見る限り、ローカルへのインストールは必須のようだし。<br />
<pre class="brush: javascript">if (!env.modulePath) {
gutil.log(
chalk.red('Local gulp not found in'),
chalk.magenta(tildify(env.cwd))
);
gutil.log(chalk.red('Try running: npm install gulp'));
process.exit(1);
}
</pre><br />
このフローの通り、ローカルにgulpがインストールされていないと、gulpの実行はできない。<br />
<pre class="brush: bash/shell">[13:20:52] Local gulp not found in ~/git/プロジェクト名
[13:20:52] Try running: npm install gulp
</pre><br />
<br />
gulpが、グローバルとローカルとの、両方でインストールが必須なのには構造的な理由がある。<br />
<br />
グローバルにインストールされたgulpは、ローカルにインストールされたgulpを実行するためのもの。このグローバルとローカルの2つのモジュールの中身は完全に同じもので、まったく同じモジュールをグローバルとローカルにインストールすることになる。<br />
<br />
gulpコマンドが実行されると、gulp.jsのrequireで、ローカルのgulpがインポートされる。<br />
<br />
そして、実際の処理(gulpfile.jsに書かれたタスク)はrequireされたローカルのgulpモジュールが全て行う。つまり、gulpは、自分自身が自分自身と同じモジュールをrequireして使うという仕組みで動いている。<br />
<br />
<br />
では、ローカルへのインストールは仕方がないとしても、gitで共有したくない問題はどうにかできないかと思ったら、<br />
<br />
<blockquote>gitなどでプロジェクト共有している場合、この node_modules を共有していると結構重かったりしますし、 package.json が共有されていたらあとで $ npm install をしたら必要なものが入ってくるため、 node_modules のディレクトリの共有は不要です。<br />
</blockquote><a href="http://h2ham.net/gulp-001">Node.js 製の タスクランナー gulp.js を使ってみる - HAM MEDIA MEMO</a><br />
<br />
<br />
ハッ!その手があったか!思わずポンと手を打ってしまった。<br />
<br />
<br />
<big>.gitignoreでnode_modulesをignoreすればいいじゃないですか。</big><br />
<br />
<br />
gitからクローンした際は、npm installすればパッケージはインストールされるんだし。<br />
<br />
<br />
ですよねぇ。これって常識だったのかしら。<br />
<br />
<br />
<b>追記</b><br />
<br />
.gitignoreのサンプルは以下。一行だけだけれど。<br />
<br />
<pre class="brush: bash/shell">/node_modules/
</pre><br />
上記内容をプロジェクト直下にファイル名「.gitignore」で保存。<br />
「node_modules」の前の/で、プロジェクトと同階層であることを明示。<br />
「node_modules」の後の/で、ディレクトリであることを明示。<br />
<br />
つまり、「/node_modules/」で、プロジェクト直下のnode_modulesディレクトリを無視する、という設定になる。misahothttp://www.blogger.com/profile/08782505873150853833noreply@blogger.com0tag:blogger.com,1999:blog-8752167966854854944.post-54075015641759954812014-12-19T15:59:00.000+09:002014-12-19T15:59:19.335+09:00Google APIs Client Library for JavaScriptを使いながらfieldsを指定するにはGoogle APIs Client Library for JavaScriptを使っていて、必要ないレスポンスデータを削除する為にfieldsを指定するときの忘備録。<br />
<br />
検索やデータ取得などのGET系では、パラメータの一部として指定すればいい。<br />
<br />
<pre class="brush: javascript">gapi.client.drive.files.list({
'fields': 'items(id,title),selfLink',
'q': "title='" + folderTitle + "' and trashed=false"
}).execute(function(folder) {
console.debug(folder.selfLink);
});
</pre><br />
<br />
作成、更新などのPUT系では、Request bodyをresponseフィールドに定義するが、fieldsは、responseの中ではなく、外に指定する。この点が注意ポイント。<br />
<br />
<pre class="brush: javascript">gapi.client.drive.files.insert({
'fields': 'id',
'resource': {
mimeType: 'application/vnd.google-apps.folder',
title: folderTitle
}
}).execute(function(folder) {
console.debug(folder.id);
});
</pre>misahothttp://www.blogger.com/profile/08782505873150853833noreply@blogger.com0tag:blogger.com,1999:blog-8752167966854854944.post-83813383917447231682014-11-20T18:59:00.000+09:002014-11-20T18:59:03.943+09:00DartアプリケーションをGoogle App EngineのManaged VMsで動かすまでGoogle App Engine(GAE)のManaged VMs(MVMs)でDockerがサポートされた。これまでLimited Previewだったが、今月のGCP Liveでオープンになった。<br />
<br />
あわせて、GAEのMVMs上でDartアプリケーションを動かすことができるように、GoogleがDartアプリケーションのランタイムイメージを提供してくれたので、それをベースイメージにして、自分で作ったDartアプリケーションをGAEへデプロイできるようになった。<br />
<br />
以下、GAEのMVMsって何?という全体像の把握から、DockerやDartの環境構築、アプリのデプロイまでの一連の流れをまとめてみた。<br />
<br />
参考:<br />
<ul><li><a href="https://www.dartlang.org/cloud/setup.html">Set Up for App Engine Development | Dart: Structured web apps</a></li>
<li><a href="https://www.dartlang.org/cloud/run.html">Create and Run HelloWorld | Dart: Structured web apps</a></li>
</ul><br />
<br />
<br />
<div style="font-size: 1.25em;"><b>1. 全体のイメージを掴む</b></div><br />
<a href="https://www.dartlang.org/cloud/">Dart and Google Cloud Platform | Dart: Structured web apps</a><br />
まずは、GAEのMVMsでDartアプリケーションを動かすってどういうこと?を消化。上記のページに概要がまとまっている。動画でも、6分程度だが、内容が分かりやすくまとめられているのでチェックすることをオススメ。<br />
<br />
内容的に少しかぶるが、GAEのMVMsについての説明も目を通した。<br />
<a href="https://cloud.google.com/appengine/docs/managed-vms/">Managed VMs - Google App Engine — Google Cloud Platform</a><br />
<br />
<br />
<div style="background-color: ivory; padding: 1em;"><b>要点:</b><br />
<br />
これまでのGAEは、特定の言語、Python、Java、GoやPHPのランタイムをベースにしたWeb Serverと自分のコードが動くサンドボックス。環境は変えられず、全てはGoogleに管理されていた。<br />
<br />
MVMsだと、カスタマイズしたGoogle Compute Engine(GCE)のVirtual Machines(VMs)上でGAEのような手軽さで、自分のコードを動かすことができるよ、GCEのIaaS的な柔軟性とGAEのPaaS的な管理の手軽さ、どちらもおいしいとこどりができるよ、というもの。<br />
<br />
MVMsは、GAEのインスタンスを、GCE上で動かしてくれる。</div><br />
<br />
MVMsが登場した背景やメリットについては、トップゲートさんの以下のページが参考になる。<br />
<a href="view-source:http://www.topgate.co.jp/blog/20140908">GAE Managed VMs誕生までの歴史を振り返る | 株式会社トップゲート(Google Cloud Platform Partner / Google技術者集団)</a><br />
<br />
<br />
<br />
<div style="font-size: 1.25em;"><b>2. Dartの開発環境の構築</b></div><br />
Dart のツールやDart Editorをダウンロード。<br />
<a href="https://www.dartlang.org/codelabs/darrrt/#set-up">https://www.dartlang.org/codelabs/darrrt/#set-up</a><br />
<br />
解凍して、ローカルマシーンにdartディレクトリを配置したら、SDKのbin/までPATHをはる。<br />
<br />
例えばMacでは、.bash_profileに以下を追加。<br />
<pre class="brush: shell">$ export PATH=$PATH:<installation directory>/dart/dart-sdk/bin
</pre><br />
<br />
<br />
<div style="font-size: 1.25em;"><b>3. boot2dockerをインストール</b></div><br />
Mac:<a href="https://github.com/boot2docker/osx-installer/releases">Releases · boot2docker/osx-installer</a><br />
上記のリンクからパッケージをダウンロードして、boot2dockerをインストールする。<br />
<br />
boot2dockerは、VirtualBoxのためにパッケージされたLinux VMイメージ。このイメージの中には、boot2dockerのコマンドやDockerのデーモンが含まれている。<br />
<br />
<a href="https://www.dartlang.org/cloud/images/insidevirtualbox.png" imageanchor="1"><img border="0" src="https://www.dartlang.org/cloud/images/insidevirtualbox.png" /></a><br />
<div style="font-size: 0.85em;">from <a href="https://www.dartlang.org/cloud/setup.html">Set Up for App Engine Development | Dart: Structured web apps</a></div><br />
VirtualBoxは、DockerのVMデーモンをホストするもの。DockerコンテナはVirtualBox上で走る。なお、GAEのMVMsが対応しているのは、VirtualBox 4.3.10以上。<br />
<br />
boot2dockerの最新は、v1.3.1(Docker v1.3.1、Linux v3.16.4)。VirtualBox v4.3.18-r96516を含んでいるので、インストールが完了すると、VirtualBoxもインストールされる。Macの場合、boot2dockerコマンドも、dockerコマンドも、/usr/local/binにインストールされる。<br />
<br />
<br />
<br />
<div style="font-size: 1.25em;"><b>4. Docker設定</b></div><br />
boot2dockerにはDocker v1.3.1が含まれているが、boot2dockerのVM内部で、Docker v1.3.0を要求するそうで、以下のコマンドを走らせて、その辺の設定を行う。<br />
<br />
<pre class="brush: shell">$ mkdir ~/.boot2docker
$ echo 'ISOURL = "https://github.com/boot2docker/boot2docker/releases/download/v1.3.0/boot2docker.iso"' > ~/.boot2docker/profile
$ boot2docker init
</pre><br />
<br />
<br />
<div style="font-size: 1.25em;"><b>5. boot2docker起動</b></div><br />
boot2docker起動。<br />
<pre class="brush: shell">$ boot2docker up
</pre><br />
正常に起動したらセットアップ情報が表示される。GAEのMVMsでアプリケーション開発をするためには、必要ないので無視。<br />
<pre class="brush: shell">$ boot2docker up
Waiting for VM and Docker daemon to start...
.....o
Started.
Writing /Users/hogehoge/.boot2docker/certs/boot2docker-vm/ca.pem
Writing /Users/hogehoge/.boot2docker/certs/boot2docker-vm/cert.pem
Writing /Users/hogehoge/.boot2docker/certs/boot2docker-vm/key.pem
To connect the Docker client to the Docker daemon, please set:
export DOCKER_TLS_VERIFY=1
export DOCKER_HOST=tcp://192.168.59.103:2376
export DOCKER_CERT_PATH=/Users/hogehoge/.boot2docker/certs/boot2docker-vm
</pre><br />
<br />
<br />
<div style="font-size: 1.25em;"><b>6. Dockerイメージを取得</b></div><br />
以下のコマンドを実行して、環境変数をセットアップ。<br />
<pre class="brush: shell">$ $(boot2docker shellinit)
</pre><br />
次に、以下のコマンドを実行して、Googleが提供している幾つかのDockerイメージをダウンロードする。<br />
<pre class="brush: shell">$ docker pull google/docker-registry
</pre><br />
イメージが無事に取得できたかどうか確認。google/dartリポジトリからのイメージも取得できているはず。<br />
<pre class="brush: shell">$ docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
google/dart latest cd7baf4008f8 2 weeks ago 243.6 MB
google/docker-registry latest 5d4bb763edd7 3 weeks ago 428.8 MB
</pre><br />
確認のため、Dart VMのバージョンナンバーを出力。VMが走っていないといけないので、gogole/dartをrunする。<br />
<pre class="brush: shell">$ docker run google/dart /usr/bin/dart --version
Dart VM version: 1.7.2 (Tue Oct 14 12:12:42 2014) on "linux_x64"
</pre><br />
<br />
<div style="background-color: ivory; padding: 1em;"><b>Dockerについて、最低限知っておくとよい知識:</b><br />
<br />
Docker:コンテナ型の仮想化を実現するためのソフトウェア。VMwareなどのサーバの仮想化と違い、扱う単位がマシンではなくプロセスであることがポイント。サーバ管理で必要なあれこれは不要。<br />
<br />
リポジトリ:コンテナを管理する単位。<br />
<br />
コンテナ:プロセス空間やネットワーク等が外部から隔離された空間。コンテナの中でプロセスが動く。<br />
<br />
イメージ:コンテナのテンプレート。イメージを元にして新しいコンテナを作る。<br />
<br />
<br />
仮想化の単位がマシンイメージからコンテナイメージに移ってきた感じ。<br />
Dockerの基本については、TECHSCOREさんのブログが参考になる。<br />
<a href="http://www.techscore.com/blog/2014/08/05/introduction-to-docker/">» 隔離の技術Dockerの考え方と使い方の基本 TECHSCORE BLOG</a></div><br />
<br />
<br />
<div style="font-size: 1.25em;"><b>7. Google Developer Consoleでプロジェクトを作成</b></div><br />
<a href="https://console.developers.google.com/">Google Developers Console</a><br />
<br />
プロジェクト名とプロジェクトIDを決定する。IDは後で変えられないので、よく考えて決める。<br />
<br />
プロジェクト作成後、GAEやGCE周りの設定が必要なんじゃないかと試行錯誤したが、結局必要なことは単にプロジェクトを作るだけだった。<br />
<br />
<br />
<br />
<div style="font-size: 1.25em;"><b>8. Google Cloud SDKをインストール</b></div><br />
Mac:ターミナルで以下のコマンドを入力。インストールが終了したら、ターミナルを再起動。<br />
<pre class="brush: shell">$ curl https://sdk.cloud.google.com | bash
</pre><br />
Google Cloud SDKを使うにはgmailアカウントを設定する認証作業が必要。<br />
<pre class="brush: shell">$ gcloud auth login
</pre><br />
プロジェクトIDを設定。<br />
<pre class="brush: shell">$ gcloud config set project <my-project-id>
</pre><br />
コンポーネントを最新にする。<br />
<pre class="brush: shell">$ gcloud components update app
</pre><br />
設定を確認。<br />
<pre class="brush: shell">$ gcloud config list
[core]
account = xxx@gmail.com
disable_usage_reporting = False
project = <my-project-id>
user_output_enabled = True
</pre><br />
<br />
<br />
<div style="font-size: 1.25em;"><b>9. Dart App Engineプロジェクトを作成</b></div><br />
Dart App Engineプロジェクトとして、helloworldを作ってみる。ディレクトリ場所はお好みで。<br />
<pre class="brush: shell">$ mkdir helloworld
</pre><br />
<b>app.yamlファイル</b>を作成。これはDart App Engineアプリケーションの設定ファイル。ディレクトリのトップ、ここではhelloworldディレクトリ直下、に配置する。<br />
<pre class="brush: shell">$ cd helloworld
$ vi app.yaml
</pre>app.yaml中身<br />
<pre class="brush: shell">version: helloworld
runtime: custom
vm: true
api_version: 1
</pre><br />
続いて、<b>Dockerfileファイル</b>を作成。これも配置はディレクトリトップ。<br />
<pre class="brush: shell">$ vi Dockerfile</pre>Dockerfile中身<br />
<pre class="brush: shell">FROM google/dart-runtime</pre><br />
更に、<b>pubspec.yamlファイル</b>を作成。pubspec.yamlは、Dartプログラムが依存するパッケージなどを定義する。これもこれも配置はディレクトリトップ。<br />
<pre class="brush: shell">$ vi pubspec.yaml</pre>pubspec.yaml中身<br />
<pre class="brush: shell">name: helloworld
version: 0.1.0
author: Misaho Otsuka
dependencies:
appengine: '>=0.2.1 < 0.3.0'
</pre><br />
<br />
<br />
<div style="font-size: 1.25em;"><b>10. Dartプログラムを作成</b></div><br />
サーバーサイドのDartプログラムを作成。配置は<b>bin/</b>下。ファイル名は<b>server.dart</b>。Dart App Engineのプログラムは、bin/server.dartから実行を開始するので、このファイルは必須。 <br />
<pre class="brush: shell">$ mkdir bin
</pre>server.dartの中身 <br />
<pre class="brush: java">import 'dart:io';
import 'package:appengine/appengine.dart';
main() {
runAppEngine((HttpRequest request) {
request.response..write('こんにちは、世界!')
..close();
});
}
</pre><br />
runAppEngine()は、トップレベルメソッド。Dartのランタイムを走らせ、App Engineへ接続する。引数はコールバック関数。この例では、HttpRequestを使ってレスポンスに「こんにちは、世界!」と出力し、レスポンスをクローズしている。<br />
<br />
<br />
Dartプログラムは、Dart Editorで編集してもいい。<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiRhLH2tMef75s1GDrtw9Qlk8I-KIhJ1z3YaWD5VVsCBD8LvTBIEktP_TpDotkSt0S2QyeEURzPCUlkILYmuWMQjrohOYn6U6qEyGd8Z-yqytPXEx1JMAX9I_GtQdO0hPGCmxIU0wDhr3F7/s1600/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88+2014-11-19+17.19.03.png" imageanchor="1"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiRhLH2tMef75s1GDrtw9Qlk8I-KIhJ1z3YaWD5VVsCBD8LvTBIEktP_TpDotkSt0S2QyeEURzPCUlkILYmuWMQjrohOYn6U6qEyGd8Z-yqytPXEx1JMAX9I_GtQdO0hPGCmxIU0wDhr3F7/s1600/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88+2014-11-19+17.19.03.png" height="258" width="640" /></a><br />
<br />
最終的な階層構造。<br />
<br />
<a href="https://www.dartlang.org/cloud/images/filestructure.png" imageanchor="1"><img border="0" src="https://www.dartlang.org/cloud/images/filestructure.png" /></a><br />
<div style="font-size: 0.85em;">from <a href="https://www.dartlang.org/cloud/run.html">Create and Run HelloWorld | Dart: Structured web apps</a></div><br />
<br />
<br />
<div style="font-size: 1.25em;"><b>11. pub getを実行</b></div><br />
Dart App Engineのプロジェクトに必要なライブラリをインストールする。helloworldディレクトリ下で実行。 <br />
<pre class="brush: shell">$ pub get
Resolving dependencies...
Got dependencies!
</pre><br />
<br />
<br />
<div style="font-size: 1.25em;"><b>12. Dartのランタイムイメージをダウンロード</b></div><br />
これは1度だけ実行すれば良い。 <br />
<pre class="brush: shell">$ docker pull google/dart-runtime
</pre><br />
イメージがゲットできたことを一応、確認。<br />
<pre class="brush: shell">$ docker images
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
google/dart-runtime latest e2ab3ccbce58 2 weeks ago 243.6 MB
google/dart latest cd7baf4008f8 2 weeks ago 243.6 MB
google/docker-registry latest 5d4bb763edd7 3 weeks ago 428.8 MB
</pre><br />
<br />
<br />
<div style="font-size: 1.25em;"><b>13. ローカルで実行確認</b></div><br />
<pre class="brush: shell">$ gcloud preview app run app.yaml</pre>コンテナが起動した後は、ヘルスチェクが定期的に何度も呼び出される。<br />
<pre class="brush: shell">$ gcloud preview app run app.yaml
Module [default] found in file [/Users/hogehoge/git/mvm-sample/helloworld/app.yaml]
INFO: Looking for the Dockerfile in /Users/hogehoge/git/mvm-sample/helloworld
INFO: Using Dockerfile found in /Users/hogehoge/git/mvm-sample/helloworld
INFO: Skipping SDK update check.
INFO: Starting API server at: http://localhost:52671
INFO: Health checks starting for instance 0.
WARNING: Health check for instance 0 is not ready yet.
INFO: Building image test-mvmdart.default.helloworld...
INFO: Starting module "default" running at: http://localhost:8080
INFO: Starting admin server at: http://localhost:8000
INFO: Image test-mvmdart.default.helloworld built, id = 6a65a3d6cb36
INFO: Creating container...
INFO: Container 25f2f396d660bd8411e150e48ae05290922b3bebda07acaf8e90007a577aa045 created.
INFO: default: "GET /_ah/start HTTP/1.1" 200 2
INFO: default: "GET /_ah/health?IsLastSuccessful=no HTTP/1.1" 200 2
INFO: default: "GET /_ah/health?IsLastSuccessful=yes HTTP/1.1" 200 2
INFO: default: "GET /_ah/health?IsLastSuccessful=yes HTTP/1.1" 200 2
</pre><br />
localhost:8080へアクセスすると、実行結果が確認できる。<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjHyfE85q2AthukSZciZtbWxvywOUXvXA4mX4dHBYR85wnMOf5__Z1AmqJ1pIfL7wkB6KD4zI5HYSgEj5hqbqJLiz3bLqQX_U9B8ovwycQfdldfO_tKnXM5pUDDZNH2_dPVg64zN7PMcz_E/s1600/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88+2014-11-19+17.42.02.png" imageanchor="1"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjHyfE85q2AthukSZciZtbWxvywOUXvXA4mX4dHBYR85wnMOf5__Z1AmqJ1pIfL7wkB6KD4zI5HYSgEj5hqbqJLiz3bLqQX_U9B8ovwycQfdldfO_tKnXM5pUDDZNH2_dPVg64zN7PMcz_E/s1600/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88+2014-11-19+17.42.02.png" height="191" width="400" /></a><br />
<br />
<br />
「こんにちは、世界!」を「Hello, world!」へ変更して保存すると、<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEguvrJ3E4PuWxxmiFPY1RceuXVGHEDrS666ccQSQdq4y4zp0hzAcwSKnJvdJ_VoxJmDOfIiO30mivyZAbpSSJDccj-3UTC-w6sff_f020AVd2ShRHmQehGuoEmKhAXWBpxky6JeaJrn4cYe/s1600/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88+2014-11-19+17.56.30.png" imageanchor="1"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEguvrJ3E4PuWxxmiFPY1RceuXVGHEDrS666ccQSQdq4y4zp0hzAcwSKnJvdJ_VoxJmDOfIiO30mivyZAbpSSJDccj-3UTC-w6sff_f020AVd2ShRHmQehGuoEmKhAXWBpxky6JeaJrn4cYe/s1600/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88+2014-11-19+17.56.30.png" height="251" width="640" /></a><br />
<br />
<br />
ブラウザでリロードすれば、変更が反映されていることが確認できる。<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEii6LqjmdxB2vl9htGCcEakWUV_11BChc9e-y6a-08vdKIa5bmLXwShNntZ1svg_8PY0VHN1n-EHXcsS03cwFeqZcppKofQ1xuDLkMut3O3buFG36ARw2MqG5fJLplKqiP99PVKhk_uw9tO/s1600/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88+2014-11-19+18.28.06.png" imageanchor="1"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEii6LqjmdxB2vl9htGCcEakWUV_11BChc9e-y6a-08vdKIa5bmLXwShNntZ1svg_8PY0VHN1n-EHXcsS03cwFeqZcppKofQ1xuDLkMut3O3buFG36ARw2MqG5fJLplKqiP99PVKhk_uw9tO/s1600/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88+2014-11-19+18.28.06.png" height="181" width="400" /></a><br />
<br />
<br />
アプリケーションへの変更は監視されている。<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhkUdneo_IW-bz0IzkawkhxVlo42wO73zCST0ukifA6uVOT5il5uXSUS50hYjoDK7ldwVEiLmRBPB-S-jgJOdGBcZ1pPEXwrvxG9IT0Y3Voz5ThG-dVTXH6kmTySSkXHYmesdak72Gp6PK5/s1600/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88+2014-11-19+18.00.27.png" imageanchor="1"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhkUdneo_IW-bz0IzkawkhxVlo42wO73zCST0ukifA6uVOT5il5uXSUS50hYjoDK7ldwVEiLmRBPB-S-jgJOdGBcZ1pPEXwrvxG9IT0Y3Voz5ThG-dVTXH6kmTySSkXHYmesdak72Gp6PK5/s1600/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88+2014-11-19+18.00.27.png" height="194" width="640" /></a><br />
<br />
<a href="https://www.dartlang.org/cloud/images/managedVMwithdocker.png" imageanchor="1"><img border="0" src="https://www.dartlang.org/cloud/images/managedVMwithdocker.png" /></a><br />
<div style="font-size: 0.85em;">from <a href="https://www.dartlang.org/cloud/run.html">Create and Run HelloWorld | Dart: Structured web apps</a></div><br />
<br />
ローカルサーバーを停止したい場合は、Control-C。gcloudコマンドでrunしたローカルサーバーをstopするコマンドは、無い。<br />
<br />
ローカルで実行確認をすると、現在のプロジェクトの内容を元に、<my-project-id>.default.<app.yamlで定義したversion>という名前で、コンテナが作られる。<br />
<br />
<a href="http://4.bp.blogspot.com/-ntcm4EwCNFk/VG2v4YvNK3I/AAAAAAAAErc/mr2yQNO18_A/s1600/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88_2014-11-20_13_04_59.png" imageanchor="1"><img border="0" src="http://4.bp.blogspot.com/-ntcm4EwCNFk/VG2v4YvNK3I/AAAAAAAAErc/mr2yQNO18_A/s1600/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88_2014-11-20_13_04_59.png" height="43" width="640" /></a><br />
<br />
<br />
<br />
<div style="font-size: 1.25em;"><b>14. GAE上にデプロイ</b></div><br />
<pre class="brush: shell">$ gcloud preview app deploy app.yaml</pre><br />
<div style="background-color: ivory; padding: 1em;"><b>注意:</b><br />
<br />
デプロイし終わるまで結構時間がかかる!大体3分ほど。しかもヘルスチェクが5秒おきに30回程度実行される。でも待っていればヘルスチェクも終わって完全にデプロイ作業が完了する。デプロイが終わらないと、URLへアクセスしても、503になるだけなので辛抱。</div><br />
<pre class="brush: shell">$ gcloud preview app deploy app.yaml
Updating module [default] from file [/Users/hogehoge/git/mvm-sample/helloworld/app.yaml]
06:34 PM Host: appengine.google.com
{bucket: vm-containers.test-mvmdart.appspot.com, path: /containers}
06:36 PM Host: appengine.google.com
06:36 PM Application: test-mvmdart (was: None); version: helloworld
06:36 PM
Starting update of app: test-mvmdart, version: helloworld
06:36 PM Getting current resource limits.
06:36 PM Scanning files on local disk.
06:36 PM Scanned 500 files.
06:36 PM Cloning 530 application files.
06:36 PM Starting deployment.
06:36 PM Checking if deployment succeeded.
06:36 PM Deployment successful.
06:36 PM Checking if updated app version is serving.
06:36 PM Will check again in 5 seconds.
... 中略
06:39 PM Will check again in 5 seconds.
06:39 PM Checking if updated app version is serving.
06:39 PM Enough VMs ready (2/2 ready).
06:39 PM Completed update of app: test-mvmdart, version: helloworld
</pre><br />
<br />
デプロイが完了したら、<br />
<br />
<b>http://helloworld.<my_project_id>.appspot.com/</b><br />
<br />
へアクセス。ローカルと同じ実行結果が確認できる。<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgWmFrzRQ2z-abDzmIISXcY42PDE5zWGJQ3TsQqIYaAbb42TUgydVEzxds08pFIn5Dkf3mwG82ufTvUkmKTV1i3tJDZyWEL2NAEdWUW-LpPK9gRg1g7pCeaH1FZtOr4UI9JzpyMJayHSpEX/s1600/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88+2014-11-19+18.47.39.png" imageanchor="1"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgWmFrzRQ2z-abDzmIISXcY42PDE5zWGJQ3TsQqIYaAbb42TUgydVEzxds08pFIn5Dkf3mwG82ufTvUkmKTV1i3tJDZyWEL2NAEdWUW-LpPK9gRg1g7pCeaH1FZtOr4UI9JzpyMJayHSpEX/s1600/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88+2014-11-19+18.47.39.png" height="128" width="400" /></a><br />
<br />
<br />
<br />
Google Developer Consoleを確認すると、自動的に、GAEのインスタンス設定が行われ、GCEのVMインスタンスも設定されていることが確認できる。<br />
<br />
GAEのダッシュボード。「Google管理」となっているのがMVMsの印。<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiHJrYIlp7hNSqx6NMjpGwy2o_ZjdRnd-67RCgzACoVuyEQKqRLjRTeLv8gY_Qwqi0ClwxbZ5GePkUgRdqYMwbw9_VecNGMUDbfGzqC5a0etcPOoY_g-9u7A8GSeVEpU7eH_AG4WWBMosQi/s1600/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88_2014-11-20_18_38_18.png" imageanchor="1"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiHJrYIlp7hNSqx6NMjpGwy2o_ZjdRnd-67RCgzACoVuyEQKqRLjRTeLv8gY_Qwqi0ClwxbZ5GePkUgRdqYMwbw9_VecNGMUDbfGzqC5a0etcPOoY_g-9u7A8GSeVEpU7eH_AG4WWBMosQi/s1600/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88_2014-11-20_18_38_18.png" height="110" width="400" /></a><br />
<br />
<br />
GCEのVMインスタンスは自動的に2つ作成され、どちらもマシンタイプ、ゾーンは以下のようになっていた。<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhRYc780194aEsGgT3I2876CBrAxf9OVCvK90Ab9rt9gJr53G7LZa1-iVM3diY4K7Rl138GOCLORq3Ojr9BvGtc39lHZCtb-MRaPfFhWPFvTLAIk1R2w62YIm6oZPtQEgi4_ysDH7ru7HI8/s1600/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88+2014-11-20+18.40.45.png" imageanchor="1"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhRYc780194aEsGgT3I2876CBrAxf9OVCvK90Ab9rt9gJr53G7LZa1-iVM3diY4K7Rl138GOCLORq3Ojr9BvGtc39lHZCtb-MRaPfFhWPFvTLAIk1R2w62YIm6oZPtQEgi4_ysDH7ru7HI8/s1600/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88+2014-11-20+18.40.45.png" height="177" width="320" /></a><br />
<br />
<br />
<br />
<div style="background-color: ivory; padding: 1em;"><b>Docker お掃除コマンド Tips:</b><br />
<br />
デプロイを行うと、localhost...をリポジトリとするイメージや<none>なイメージ、busyboxのイメージが増える。<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjR9KzZJXLkYNeEZ0fb0w4OGHbvxGb7t4lYtUkEy09d6hYMziQxgoefnwpVzMgVTz7rzqdC3e9nrDH8FfdZvsvLttRXMwy43R5XwCyYlR70Cs5FGYNeZYKEfzKQ7nxJENIPcVTC5PShPoPR/s1600/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88_2014-11-20_18_04_23.png" imageanchor="1"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjR9KzZJXLkYNeEZ0fb0w4OGHbvxGb7t4lYtUkEy09d6hYMziQxgoefnwpVzMgVTz7rzqdC3e9nrDH8FfdZvsvLttRXMwy43R5XwCyYlR70Cs5FGYNeZYKEfzKQ7nxJENIPcVTC5PShPoPR/s1600/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88_2014-11-20_18_04_23.png" height="158" width="488" /></a><br />
<br />
<br />
停止したコンテナを消したり、不要なイメージを削除したりするお掃除コマンドは必須。<br />
<br />
・停止したコンテナのリストを取得して削除する<br />
<pre class="brush: shell">docker rm $(docker ps -a -q)</pre><br />
・タグが<none>イメージを削除する<br />
<pre class="brush: shell">docker rmi $(docker images | grep "^<none>" | awk "{print $3}")</pre><br />
・デプロイする度に増えるlocalhost...をリポジトリとするイメージを削除する<br />
<pre class="brush: shell">docker rmi $(docker images | grep "localhost*" | awk "{print $3}")</pre></div><br />
<br />
<br />
<br />
<div style="font-size: 1.25em;"><b>結論: どれくらい手間?</b></div><br />
Dart on App Engine、どれくらい管理やデプロイに手間がかかるか?というと、結局、<b>GAEとあまり変わらない</b>。<br />
<br />
一連の作業はプロセスが幾つもあって時間がかかる印象だが、一度環境を作ってしまえば、後は、サーバーサイドのDartプログラム書いて、runして、deployするだけ。仮想化も、サーバレベルでなく、コンテナレベルなので、プロジェクトに関係ないメンテ作業なども必要ない。<br />
<br />
<br />
<div style="font-size: 1.25em;"><b>結論: どれくらいお金かかる?</b></div><br />
手間はGAEと変わらないのだが、費用はGAEベースではなく、GCEベースなので、アクセスがなくても<b>インスタンスを削除しない限り費用が発生する</b>。この点は注意。<br />
<br />
今のところ、このhelloworld程度のプログラムをデプロイして、かかった費用は<br />
<br />
<b>3日間で$1.66</b><br />
<br />
(内訳:Storage Pd Capacityに$0.03、Generic Small instance with 1 VCPU, no scratch diskに$1.63)<br />
<br />
ということは、1日あたり$0.5程度。今のレートでは約50円。1ヶ月では約1,500円。悪くないんじゃない?という感じ。misahothttp://www.blogger.com/profile/08782505873150853833noreply@blogger.com0tag:blogger.com,1999:blog-8752167966854854944.post-44104898680239354752014-11-11T14:29:00.000+09:002014-11-11T16:05:17.107+09:00Dart事始めDartの「<a href="https://www.dartlang.org/codelabs/darrrt/">GET STARTED</a>」の一連のステップをやってみた。<br />
<br />
<br />
<div style="font-size: 1.25em;"><b>「GET STARTED」で最終的にできあがるWebアプリの仕様</b></div><br />
<ul><li>テキストフィールドへ名前が入力されると、右側のバッジ部分に「名前 the 称号」と海賊っぽく表示する</li>
</ul><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgvn6D3TAiKoMLEr5evwA7JFb1GwHblaW2AALPCeI_vZPyBIuIBj7BagPKd751j3ZpVgi8WA6teQejavB1zrNVAPwgYnI5pnEOsZWX-iLXkpwCLiv0tXcQJ4Q5xw2N8Orqk1QSi5TztAdT5/s1600/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88+2014-11-11+13.31.44.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgvn6D3TAiKoMLEr5evwA7JFb1GwHblaW2AALPCeI_vZPyBIuIBj7BagPKd751j3ZpVgi8WA6teQejavB1zrNVAPwgYnI5pnEOsZWX-iLXkpwCLiv0tXcQJ4Q5xw2N8Orqk1QSi5TztAdT5/s1600/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88+2014-11-11+13.31.44.png" height="225" width="640" /></a></div><br />
<ul><li>テキストフィールドが空の場合は、ボタンを押す度に「名前 the 称号」の組み合わせをランダムに表示する</li>
</ul><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjiN32R8kOVG9VCvb_qC486225UkD0ldTGf_6xmPdSfTEVPNMgdwJHU6z71hdHehzrxHoJo72TDtKa51RE1aH3GmhoC9oHOONoVT08g33RsuXuRTYCHDNhb-epBTsJIwBScZZaCBEPE9K1z/s1600/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88+2014-11-11+13.32.22.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjiN32R8kOVG9VCvb_qC486225UkD0ldTGf_6xmPdSfTEVPNMgdwJHU6z71hdHehzrxHoJo72TDtKa51RE1aH3GmhoC9oHOONoVT08g33RsuXuRTYCHDNhb-epBTsJIwBScZZaCBEPE9K1z/s1600/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88+2014-11-11+13.32.22.png" height="226" width="640" /></a></div><br />
<ul><li>テキストフィールドが空かどうかで、ボタンのラベルを変える</li>
<li>テキストフィールドへ入力すると、ボタンを無効化する</li>
<li>テキストフィールドへ入力する度に「名前 the 称号」の組み合わせをランダムに表示する</li>
<li>「名前 the 称号」が変わる度に、名前と称号をローカルストレージへ保存する</li>
</ul><br />
<br />
<br />
<div style="font-size: 1.25em;"><b>Dartで書くときのポイントについて色々と気づき</b></div><br />
<div style="background-color: #fffff0; padding: 1em;"><ul><li>importでライブラリ全体をインポート。特定のクラスをインポートしたい場合はshowで</li>
<li>final, staticなどはJavaと同じ意味で使用可能</li>
<li>privateキーワードは無い。_を付けた変数がprivate扱いになる</li>
<li>プログラムの実行主体はvoid main()</li>
<li>入出力ストリームの監視はlisten()で</li>
<li>JavaScriptのPromisesと同じ扱いでFutureオブジェクトが使える。then()やcatchError()で、onSuccessとonErrorの扱いを切り分ける</li>
<li>カスケードオペレーター(..)で、あるオブジェクトの複数のプロパティへアクセスできる</li>
<li>window.localStorage['キー']でローカルストレージへアクセス</li>
<li>List<String>などGenericsが使用可能</li>
<li>getterは、戻り値 get プロパティと定義する。例えば、getPirateName()はString get pirateName</li>
<li> => はreturn expr;の省略形として使える</li>
<li>$は文字列内で変数を明示する時に使える(print('Error occurred: $error');)</li>
<li>コンストラクタも定義できるPirateName(){}だけでなく、PirateName.fromJSON(){}といった定義も可能</li>
<li>オプションの引数はcurly brackets({})で指定可能</li>
<li>型変換はasで(e.target as InputElement)</li>
<li>querySelector()などでidを渡してDOM操作することになる。値のバインディングは Angularなどで別途自分でやる</li>
</ul></div><br />
<br />
<br />
<div style="font-size: 1.25em;"><b>「GET STARTED」で最終的にできあがるWebアプリのリソース</b></div><br />
<ul><li>piratebadge.css</li>
<li>piratebadge.html</li>
</ul><pre class="brush: html"><!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Pirate badge</title>
<meta name="viewport"
content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="piratebadge.css">
</head>
<body>
<h1>Pirate badge</h1>
<div class="widgets">
<div>
<input type="text" id="inputName" maxlength="15" disabled>
</div>
<div>
<button id="generateButton" disabled>
Aye! Gimme a name!
</button>
</div>
</div>
<div class="badge">
<div class="greeting">
Arrr! Me name is
</div>
<div class="name">
<span id="badgeName"> </span>
</div>
</div>
<script type="application/dart" src="piratebadge.dart"></script>
<script src="packages/browser/dart.js"></script>
</body>
</html>
</pre><br />
<ul><li>piratenames.json : 名前と称号のリストを含んだJSONファイル</li>
</ul><pre class="brush: javascript">{ "names": [ "Anne", "Bette", "Cate", "Dawn",
"Elise", "Faye", "Ginger", "Harriot",
"Izzy", "Jane", "Kaye", "Liz",
"Maria", "Nell", "Olive", "Pat",
"Queenie", "Rae", "Sal", "Tam",
"Uma", "Violet", "Wilma", "Xana",
"Yvonne", "Zelda",
"Abe", "Billy", "Caleb", "Davie",
"Eb", "Frank", "Gabe", "House",
"Icarus", "Jack", "Kurt", "Larry",
"Mike", "Nolan", "Oliver", "Pat",
"Quib", "Roy", "Sal", "Tom",
"Ube", "Val", "Walt", "Xavier",
"Yvan", "Zeb"],
"appellations": [ "Awesome", "Captain",
"Even", "Fighter", "Great", "Hearty",
"Jackal", "King", "Lord",
"Mighty", "Noble", "Old", "Powerful",
"Quick", "Red", "Stalwart", "Tank",
"Ultimate", "Vicious", "Wily", "aXe", "Young",
"Brave", "Eager",
"Kind", "Sandy",
"Xeric", "Yellow", "Zesty"]}
</pre><br />
<ul><li>piratebadge.dart : Dartプログラム</li>
</ul><pre class="brush: java">// Import Dart Library.
import 'dart:html';
// 'show' keyword, which imports only the specified classes.
import 'dart:math' show Random;
import 'dart:convert' show JSON;
import 'dart:async' show Future;
// Declare button element as a global variable.
ButtonElement genButton;
// Declare span element as a global variable.
SpanElement badgeNameElement;
// key-value pairs string save to local storage.
final String TREASURE_KEY = 'pirateName';
// App starts here.
void main() {
// Stash the input element in a local variable.
InputElement inputField = querySelector('#inputName');
// Listen input streams.
inputField.onInput.listen(updateBadge);
genButton = querySelector('#generateButton');
genButton.onClick.listen(generateBadge);
badgeNameElement = querySelector('#badgeName');
// Call the function, which returns a Future(silimar to JavaScript Promises).
// Using underscore (_) as a parameter name indicates that the parameter is ignored.
PirateName.readyThePirates().then((_) {
// on success
inputField.disabled = false; // enable
genButton.disabled = false; // enable
// Retrieve the name from local storage.
setBadgeName(getBadgeNameFromStorage());
}).catchError((error) {
// '$' indicates that this is a variable, not a string.
print('Error initializing pirate names: $error');
badgeNameElement.text = 'Arrrr! No names.';
});
}
void updateBadge(Event e) {
// use 'as' to type casting
String inputName = (e.target as InputElement).value;
setBadgeName(new PirateName(firstName: inputName));
if (inputName.trim().isEmpty) {
// The cascade operator (..) allows you to perform multiple
// operations on the members of a single object.
genButton
..disabled = false
..text = 'Aye! Gimme a name!';
} else {
genButton
..disabled = true
..text = 'Arrr! Write yer name!';
}
}
void setBadgeName(PirateName newName) {
if (newName == null) {
return;
}
querySelector('#badgeName').text = newName.pirateName;
// Save the name to local storage.
window.localStorage[TREASURE_KEY] = newName.jsonString;
}
void generateBadge(Event e) {
setBadgeName(new PirateName());
}
// The function retrieves the name from local storage and
// creates a PirateName object from it.
PirateName getBadgeNameFromStorage() {
String storedName = window.localStorage[TREASURE_KEY];
if (storedName != null) {
return new PirateName.fromJSON(storedName);
} else {
return null;
}
}
// declare class
class PirateName {
// final variables cannot change.
static final Random indexGen = new Random();
// Declare generic type—List.
static List<String> names = [];
static List<String> appellations = [];
// Private variables start with underscore (_).
// Dart has no private keyword.
String _firstName;
String _appellation;
// Provide a constructor for the class.
// curly brackets{} indicates optional parameters.
PirateName({String firstName, String appellation}) {
if (firstName == null) {
_firstName = names[indexGen.nextInt(names.length)];
} else {
_firstName = firstName;
}
if (appellation == null) {
_appellation = appellations[indexGen.nextInt(appellations.length)];
} else {
_appellation = appellation;
}
}
// The constructor creates a new PirateName instance
// from a JSON-encoded string.
PirateName.fromJSON(String jsonString) {
Map storedName = JSON.decode(jsonString);
_firstName = storedName['f'];
_appellation = storedName['a'];
}
// Declare a class level method.
static Future readyThePirates() {
// If you spell miss the json file name,
// executes PirateName.readyThePirates().catchError() in main().
var path = 'piratenames.json';
// getString() returns Future is used as arguments
// to _parsePirateNamesFromJSON().
// then() is a callback function is called when the Future completes successfully.
return HttpRequest.getString(path).then(_parsePirateNamesFromJSON);
}
// Declare a instance and private method.
static _parsePirateNamesFromJSON(String jsonString) {
Map pirateNames = JSON.decode(jsonString);
names = pirateNames['names'];
appellations = pirateNames['appellations'];
}
// Provide a getter for the private variables
// The fat arrow ( => expr; ) syntax is a shorthand for { return expr; }.
String get pirateName => _firstName.isEmpty ? '' : '$_firstName the $_appellation';
// Add a getter to the PirateName class that encodes a pirate name in a JSON string.
String get jsonString => JSON.encode({
"f": _firstName,
"a": _appellation
});
}
</pre><br />
<br />
実行は以下から確認できる。<br />
<br />
<a href="https://www.dartlang.org/codelabs/darrrt/#i-classfa-fa-anchor-i-run-the-app-5">Step6 Run the app.</a>misahothttp://www.blogger.com/profile/08782505873150853833noreply@blogger.com0tag:blogger.com,1999:blog-8752167966854854944.post-72310012459525310612014-10-30T15:30:00.000+09:002014-10-30T15:34:24.584+09:00GitHubでプルリクエストがマージされた後にすることGitHubで初めてプルリクエストを送ってみました(・∀・)<br />
<br />
少しでも、fork元のリポジトリに貢献できるって嬉しいですね。初めての快感です。<br />
<br />
送ったプルリクエストは早速マージしてもらったのですが、その後、自分の作業用ブランチやforkした後の自分のリポジトリって、どのように処置したらいいの?と戸惑いました。次回またそうならないために忘備録としてメモ。<br />
<br />
<br />
<div style="font-size: 1.25em;"><b>1. ローカルのmasterへcheckout</b></div><br />
変更作業を開始する前の、forkしてきた初期のステータスを参照している段階へ戻ります。<br />
<br />
<pre>$ git checkout master
</pre><br />
<br />
<div style="font-size: 1.25em;"><b>2. ローカルの作業用ブランチを削除</b></div><br />
必要なくなったローカルの作業用ブランチを削除します。なんやかんや聞かれても面倒なので、-Dオプションを付けて、強制的に削除してしまいます。<br />
<br />
<pre>$ git branch -D fixTypo
</pre><br />
必要ないブランチが複数ある場合は、それぞれ削除していきます。<br />
<br />
ローカルにある、プルリクエストを送るために使ったブランチも、削除します。<br />
<br />
<br />
<div style="font-size: 1.25em;"><b>3. プルリクエスト済みのGitHub上にあるリモートブランチを削除</b></div><br />
<pre>$ git push origin :fixTypo
</pre><br />
<br />
<div style="font-size: 1.25em;"><b>4. ローカルでfork元のリポジトリからプル</b></div><br />
ここから先は<a href="https://www.atlassian.com/ja/software/sourcetree/overview">SourceTree</a>で作業しました。<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhi143mcPCqgs_1tCLYHj3nrEVRp2Zjd0FNluHzqERjTzsp8yQAMqqKnVX948v-kyzK85e4KnBF-sJkikyufRo3U8uCT40jimV0fk3yBtgDbSLWeUqmfDwFNA4qqop6YhMqwakUU-ZCTQ24/s1600/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88_2014-10-30_14_26_09.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhi143mcPCqgs_1tCLYHj3nrEVRp2Zjd0FNluHzqERjTzsp8yQAMqqKnVX948v-kyzK85e4KnBF-sJkikyufRo3U8uCT40jimV0fk3yBtgDbSLWeUqmfDwFNA4qqop6YhMqwakUU-ZCTQ24/s1600/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88_2014-10-30_14_26_09.png" width="411" height="196"/></a></div><br />
fork元のリポジトリからプルを実行します。<br />
<br />
プルする時に「プルする元のリポジトリ」をGitHub上の自分のリポジトリではなく、fork元のリポジトリを指します。そのために、プルダウンをクリックして、「カスタム」を選択して、パスを変更します。<br />
<br />
単に変更内容を吸いたいだけなので、「マージしてコミットする」などのチェックボックスは全てオフにしておく方が無難です。<br />
<br />
fork元は、自分が送ったプルリクエストをマージして更新済みなので、プルが済むと、自分が編集を始める前の状態との差分が出てきます。<br />
<br />
<br />
<div style="font-size: 1.25em;"><b>5. GitHub上の自分のリモートリポジトリへプッシュ</b></div><br />
ローカルのmasterへプルした、最新のfork元のリポジトリの内容を、リモートのGitHub上の自分のリポジトリへプッシュします。<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhuuiRaG0G-3FRc3gn3NQVFKxAg3QahhH6jO2xgLcm3sgdOs9vvjoe9z5-VO0pl60E8xJEPyDWhGGUg_8V7lk4002ZfcEHS0TMuvFT9HxUzU9HH8EB5Wa44D-ktmzYH2bhX_khbjnyEn2K6/s1600/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88_2014-10-30_14_26_38.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhuuiRaG0G-3FRc3gn3NQVFKxAg3QahhH6jO2xgLcm3sgdOs9vvjoe9z5-VO0pl60E8xJEPyDWhGGUg_8V7lk4002ZfcEHS0TMuvFT9HxUzU9HH8EB5Wa44D-ktmzYH2bhX_khbjnyEn2K6/s1600/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88_2014-10-30_14_26_38.png" width="434" height="177"/></a></div><br />
これで、fork元のリポジトリの状態と、GitHub上の自分のリポジトリの状態とが一致します。<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiS2mFiV538nhlPvclJvARyVAMyCPGPfj2nka-4jpwDKcd9sOdauhboEzprQOi2s9fgbkgVqm56_J4c_eFPg24nqwvfqUPuvKKNAwi5JWd5wy-o_OtFA7xbTDipG5C-6q9oN-Uwcc3VGuvf/s1600/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88+2014-10-30+15.15.18.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiS2mFiV538nhlPvclJvARyVAMyCPGPfj2nka-4jpwDKcd9sOdauhboEzprQOi2s9fgbkgVqm56_J4c_eFPg24nqwvfqUPuvKKNAwi5JWd5wy-o_OtFA7xbTDipG5C-6q9oN-Uwcc3VGuvf/s1600/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88+2014-10-30+15.15.18.png" height="47" width="397" /></a></div><br />
<br />
<br />
もう、このリポジトリはwatchしないなという場合は、リポジトリを削除してもいいと思いますが、なにかしらまだチェックしておきたいかもという場合は、自分のリポジトリが古いバージョンをさしたままにならないようにプルしておくといいと思います。misahothttp://www.blogger.com/profile/08782505873150853833noreply@blogger.com0tag:blogger.com,1999:blog-8752167966854854944.post-41836380883700956662014-09-09T13:12:00.001+09:002014-09-09T13:12:53.356+09:00昭和天皇が靖国神社へのご親拝をおやめになったのはなぜか、調べて考えた昭和天皇の靖国神社へのご親拝は過去8回あった(1945年・1952年・1954年・1957年・1959年・1965年・1969年・1975年)。<br />
<br />
しかし、1975年(昭和50年)11月21日を最後に、一度もご親拝はなされていない。今上陛下も天皇陛下になられてからのご親拝はない。皇太子殿下も皇太子殿下になられてからは行かれていない。<br />
<br />
その代わり、昭和天皇、今上陛下は、ご親拝の代わりに皇室の勅使を春と秋の例大祭に差し遣われている。また、奉幣=皇室の幣帛(へいはく=神に供える物)を、伊勢神宮と靖国神社に奉(たてまつ)る伝統は、変わらず行われている。<br />
<br />
でも、ご親拝は途絶えている。なぜ、1975年で昭和天皇のご親拝が止まっているのか、最大の原因は当時の三木首相の「私的参拝」発言の影響だと考えられる。<br />
<br />
<br />
<a href="http://blogs.yahoo.co.jp/deliciousicecoffee/9761519.html">天皇陛下が御親拝を中止された本当の理由</a> から引用<br />
<blockquote>1975年(昭和50年)、当時の首相三木武夫は8月15日に靖国神社に参拝したが公用車を使わず、肩書きを記帳せず、玉串料を公費から支払わず、閣僚を同行しないことの4条件を以て、「私的参拝」だと述べた。<br />
<br />
このことが国会でも取り上げられ政治問題化した。<br />
<br />
そして、昭和天皇の最後の靖国神社御親拝となった1975年11月21日の前日にも、国会でこの問題が大きく取り上げられている。<br />
<br />
11月21日、天皇皇后両陛下は靖国神社と千鳥ヶ淵戦没者記念墓苑にご親拝されたが、前日の11月20日の参議院内閣委員会で日本社会党の野田哲、秦豊、矢田部理の3議員が質問に立って厳しく追及した。<br />
<br />
政府委員として答弁した富田朝彦・宮内庁次長(当時)が、天皇陛下の靖国神社御親拝は「私的行為」であると説明したところ、社会党議員からは、公的行為だとか私的行為だとかいった区別はできないのではないか、明日の天皇陛下の御親拝は、「(当時国会で議論されていた)天皇陛下や内閣総理大臣らの靖国神社「公式参拝」に道を開くものであって、「表敬法案」の先取りであり、【憲法20条】に違反する疑いがある、更に「あなた方によれば、私的行為の名のもとに天皇が靖国神社に参拝されるということは、どんな答弁、どんな強弁に接しようともわれわれは断じて認めるわけにはいかない」などといった厳しい批判が加えられている。<br />
<br />
追求を受けた吉国一郎内閣法制局長官は遂に、天皇の参拝は、「憲法第20条第3項の重大な問題になるという考え方である」と答えてしまった。<br />
</blockquote><br />
<br />
先帝陛下であられる昭和天皇も今上陛下も、どのような設立経緯や内容であったとしても、日本の民主主義の根幹を成す憲法を順守する遵法の精神を強く持っておられる。<br />
<br />
憲法上、重大な問題になると言われれば、行動をお控えになるだろう。三木発言、それにまつわる国会質問、内閣法制局長官の答弁、これらの影響はとてつもなく大きい。<br />
<br />
<br />
以下、ポイントになる点について、事実と、自分の考えをまとめてみた。<br />
<br />
<br />
<div style="font-size:1.25em"><b>A級戦犯合祀されたのはいつ?</b></div><br />
1978年(昭和53年)10月17日に「昭和殉難者」(国家の犠牲者)として靖国神社に合祀された。その事実が、1979年(昭和54年)4月19日朝日新聞によって報道され国民の広く知るところとなった。<br />
<br />
<br />
<br />
<div style="font-size:1.25em"><b>合祀とご親拝取りやめの関連は?</b></div><br />
昭和天皇がご親拝をなさらなくなったのは、1978年(昭和53年)にA級戦犯が合祀されたからではない。ご親拝はその前に、1975年を最後に、途絶えていた。直接の原因となったのは、国会での政教分離に関わるという批判だったと思われる。<br />
<br />
「木戸幸一日記」や「昭和天皇独白録」などから察すると、昭和天皇ご自身は「戦犯」という言葉に否定的だったご様子。その上で更に、A級やC級(ちなみにA級〜C級は罪の種類の違いであって重みの違いではない。Aクラス、Bクラスのようなもの)の違いで対応を変えるようなお考えはお持ちでなかったのではないか。つまり、B・C級戦犯が合祀されている間はご親拝されて、A級だったらご親拝されないという対応は、なされないのではないか。<br />
<br />
<blockquote>戦争責任者を連合国に引渡すは真に苦痛にして忍び難きところなるが、自分が一人引受けて退位でもして納める訳には行かないだろうか<br />
昭和20年8月29日「木戸幸一日記」<br />
</blockquote><br />
<blockquote>首相宮御参内、戦争犯罪人の処罰を我国に於て実行することを聯合国に申入る〃ことに閣議に於て決定したる由にて、其旨奏上せられたるに、御上は敵側の所謂責任者は何れも嘗ては只管忠誠を尽したる人々なるに、之を天皇の名に於て処断するは不忍ところなる故、再考の余地はなきやとの御尋ねあり<br />
昭和20年9月12日「木戸幸一日記」<br />
</blockquote><br />
<blockquote>「元来東条と云ふ人物は、話せばよく判る、それが圧制家の様に評判が立つたのは、本人が余りに多くの職をかけ持ち、忙しすぎる為に、本人の気持が下に伝らなかつたことゝ又憲兵を余りに使ひ過ぎた。<br />
「昭和天皇独白録」<br />
</blockquote><br />
<blockquote>「東条は民論を重んずべきことを屡々口にせり。しかるに迫水のような考えがありとすると、その原因は、東条が余り各省大臣を兼任して、自分の意思通り事が運び兼ね、軍務局や憲兵が、東条に名において勝手なことをしたのではないか。東条はそんな人間とは思わぬ。彼ほど朕の意見を直ちに実行に移したものはない」<br />
「側近日誌」木下道雄<br />
</blockquote><br />
<br />
<br />
<div style="font-size:1.25em"><b>合祀時に各国の反応は?</b></div><br />
1978年(昭和53年)のA級戦犯合祀時点での諸外国からの抗議は皆無だった。<br />
<br />
1979年に合祀されていたことが報道された後も、鈴木善幸氏が首相在任中も含め8月15日等計8回参拝しているが、抗議は受けていない。<br />
<br />
しかし、<b>1985年(昭和60年)</b>に中曽根康弘氏が首相として公式参拝を表明し実施して以降、中国と韓国から抗議を受け始めた。<br />
<br />
つまり、天皇陛下がご親拝なさらない原因に、A級戦犯合祀、そして各国の反応は関連していない。中国や韓国が反対を言い出したのは1985年になってから。ますます陛下がご親拝されにくい状況にはなっているが、とりやめの原因ではない。<br />
<br />
<br />
<br />
<div style="font-size:1.25em"><b>どうしたら天皇陛下にご親拝頂けるようになるか</b></div><br />
直接の原因となった、陛下が靖国神社へご親拝なさることが政教分離に抵触するのか、という問題を解決しなければいけない。<br />
<br />
まずは、天皇陛下が祭祀王であることを日本国民がしっかりと認識する必要がある。現在、天皇陛下の宮中祭祀のお務めは公務ではなく、私的なものとされている。よって、マスコミも報道しない。この根本がおかしい。<br />
<br />
天皇陛下は神道の長、常に日本国民の安寧を祈って祭祀を行われていることを知るべき。そして、日本のために命を落とした様々な方が祀られている靖国神社へ参拝されることは当然のことだという認識を持つべき。その考えを持った総理大臣が、まずは必要。<br />
<br />
そして、陛下がそのように神社へご親拝されることは政教分離には全く抵触しないことを、国民、政治家、共に認識すべき。政教分離とは、特定の宗教を政治が贔屓しないという平等性であって、全く無関係にするというものではない。政教分離のアメリカだって聖書の上に手をおいて大統領は宣誓する。<br />
<br />
天皇陛下が祭祀王であること、ご親拝は政教分離に抵触しないこと、これらを国民が認識し、国外へも積極的に伝えていく努力が必要。<br />
<br />
<br />
そうして初めて、靖国神社への陛下のご親拝は可能になると思う。misahothttp://www.blogger.com/profile/08782505873150853833noreply@blogger.com0tag:blogger.com,1999:blog-8752167966854854944.post-91337064480776782512013-05-08T13:23:00.000+09:002014-03-28T10:51:37.310+09:00プレビューのPDF書き出しでバイト数を削減しようとQuartzフィルタを設定しても一覧に表示されなくて困ったらスキャンしたデータを、Macのアプリ「プレビュー」でPDFへ書きだすと、めちゃめちゃバイト数が高くなる。<br />
<br />
Quartzフィルタの「Reduce File Size」を指定すると、もの凄くバイト数が削減されるのだが、粗くなりすぎて、人様に送れるレベルではない(^_^;)<br />
<br />
そこで、Reduce File Sizeの「アラアラ」な設定をカスタマイズして、「アラ」程度に調整みようと、ColorSyncユーティリティでフィルタの複製を試みた。<br />
<br />
<br />
<br />
でも、・・・出ない。<br />
PDF書き出しのQuartzフィルタのリストに複製したフィルタ名が表示されない。<br />
<br />
<br />
<br />
調べたら、ColorSyncユーティリティで書き出されるパス<b>「$HOME/Library/Filters/」</b>下のQuartzフィルタは、プレビューでは<b>表示されない</b>とのこと。<br />
<br />
<a href="http://d.hatena.ne.jp/zariganitosh/20130312/my_pdf_quartz_filter">独自のPDFのQuartzフィルタを追加する - ザリガニが見ていた...。</a><br />
<br />
<br />
「/Library/Filters/」下だったら、プレビューでも表示されるようになるようだ。<br />
<br />
つまり、「/System/Library/Filters/」下にある「Reduce File Size.qfilter」をコピーして「/Library/Filters/」下へ配置し、そのファイルをカスタマイズすればよい。<br />
<br />
そのためには、rootになって/Library/へアクセスできるようにしないと。<br />
<br />
<br />
<br />
ということで、rootになれるように、ターミナルを開いてrootのパスワードを設定。<br />
<pre>$sudo passwd root
Password:
Changing password for root.
New password:
Retype new password:
</pre><br />
その後、rootへsu。<br />
<pre>$ su -
Password:
</pre><br />
/Library/にFiltersディレクトリを作り、Reduce File Size のフィルタファイルをコピーしてくる。ファイル名は末尾に_customizeをつけるなり、適当に。このファイル名がプレビューでフィルタ名として表示されるわけではないので。<br />
<br />
<pre>root# mkdir /Library/Filters
root# cp /System/Library/Filters/Reduce\ File\ Size.qfilter /Library/Filters/Reduce\ File\ Size_customize.qfilter
</pre><br />
コピーできたら、自分好みの設定にカスタマイズ編集。viなんかで。<br />
<br />
自分が変更したのは3点。<br />
<br />
<ol><li><key>ImageScaleFactor</key>の値を「0.5」から「1.0」へ</li>
<li><key>ImageSizeMax</key>の値を「512」から「1024」へ</li>
<li><key>Name</key>の値を「Reduce File Size」から「Reduce File Size(customize)」へ</li>
</ol><br />
2点目のImageSizeMaxの値が特にバイト数へ影響を与える。大きければ大きいほどバイト数は高くなる。1点目のImageScaleFactorを1.0へ設定したので、このフィルタを使うと倍率1.0の1024のサイズへ画像をスケールすることになる。<br />
<br />
3点目で設定した名前は、Quartzフィルタ名としてプレビューで表示される。<br />
<br />
<br />
<br />
今度はちゃんと出た〜(・∀・)<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhuODFeSPp18qIAzibyMhi2q1EKmBmXqlkGTWZPE7nbDDrfYHTCKYdgUZwijfcyhLW1jS_JeSBznAyelsNq5k2WLyC5pMvmp_KKydDRG-BN_El8UeUOr5u1YkBfeggmks3JUMXcbr_NRJNQ/s1600/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88+2013-05-08+13.19.03.png" imageanchor="1" ><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhuODFeSPp18qIAzibyMhi2q1EKmBmXqlkGTWZPE7nbDDrfYHTCKYdgUZwijfcyhLW1jS_JeSBznAyelsNq5k2WLyC5pMvmp_KKydDRG-BN_El8UeUOr5u1YkBfeggmks3JUMXcbr_NRJNQ/s320/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88+2013-05-08+13.19.03.png" /></a><br />
<br />
<br />
<div style="font-size:1.25em"><b>OS 10.9.2(Mavericks)のプレビューでの注意点</b></div><b>追記 2014.03.28</b><br />
<br />
・「PDFで書き出し」<br />
・「印刷」→「PDF」→「PDFとして保存」<br />
これらではQuartzフィルタが選べないようです。<br />
<br />
<b>「書き出す」→フォーマット「PDF」選択</b>と進むと、Quartzフィルタの欄が表示されます。<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh1Jlpjyd26jiwW7VPoJ5UKL9S9HMxRKwNueUyeMq1Qh9URKAp1HXDw9soPJ39gWbKtZ_Cht66kcCLkestPoG8fvPh3fY59B3Ubx8Pa0HKiq1AG4bfoMvSbZbgtxIJaJDKfIIUkav8xjF51/s1600/resize_20140328104342.png" imageanchor="1" ><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh1Jlpjyd26jiwW7VPoJ5UKL9S9HMxRKwNueUyeMq1Qh9URKAp1HXDw9soPJ39gWbKtZ_Cht66kcCLkestPoG8fvPh3fY59B3Ubx8Pa0HKiq1AG4bfoMvSbZbgtxIJaJDKfIIUkav8xjF51/s320/resize_20140328104342.png" /></a>misahothttp://www.blogger.com/profile/08782505873150853833noreply@blogger.com0tag:blogger.com,1999:blog-8752167966854854944.post-17846701739258744712013-03-14T16:51:00.001+09:002014-10-30T13:24:19.733+09:00MediaProviderのgetType()でIllegalStateExceptionが発生して困ったらmimeTypeの指定を確認すべし画像をダウンロードした際に、そのメタ情報をMediaProviderへ登録するために、MediaScannerConnectionを使っている。<br />
<pre class="java" name="code">MediaScannerConnection.scanFile(context,
new String[] { uri.getPath() },
new String[] { "image/*" },
new MediaScannerConnection.OnScanCompletedListener() {
public void onScanCompleted(String path, Uri uri) {
// Notificationを表示する
notifyDownloadComplete(context, path, uri, sms);
}
});
</pre><br />
ところが、MediaProviderのgetType()でIllegalStateExceptionが発生して、Activity Managerがクラッシュしてしまうことが多くて困った。<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhs9NJQgSDO34H7CIhM_6fiqMT2d0JUh8dSojeaN1LLpshtOmRJTdwwhTmCmrQ9coLlg9AY7iNlAVFXriFK1noLY-7fjFFko95AnxoaxYBCG8Y_CpR5E0m3m16NMtJvvSHUI336FCAefgbe/s1600/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88+2013-03-06+13.41.38.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="245" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhs9NJQgSDO34H7CIhM_6fiqMT2d0JUh8dSojeaN1LLpshtOmRJTdwwhTmCmrQ9coLlg9AY7iNlAVFXriFK1noLY-7fjFFko95AnxoaxYBCG8Y_CpR5E0m3m16NMtJvvSHUI336FCAefgbe/s640/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88+2013-03-06+13.41.38.png" width="603" /></a><br />
<br />
<br />
原因は、MediaScannerConnectionのscanFile()へmimeTypeを指定しているところ。<br />
<pre class="java:nogutter:nocontrols" name="code">new String[] { "image/*" },
</pre><br />
mimeType判断として、"image/*"と指定して、「何らかの画像」と加えたのが、逆にmimeType判断に時間をかけさせてしまい、IllegalStateException発生の温床にしてしまっていたらしい。<br />
<br />
mimeTypeが特定できない場合にスキャンさせる時は、mimeTypeを指定せず、nullを与えて、拡張子から自動的に判断させるようにする方が好ましいようだ。<br />
<br />
<br />
よって、mimeTypeの指定をnullへ変更。<br />
<pre class="java" name="code">MediaScannerConnection.scanFile(context,
new String[] { uri.getPath() },
null,
new MediaScannerConnection.OnScanCompletedListener() {
public void onScanCompleted(String path, Uri uri) {
// Notificationを表示する
notifyDownloadComplete(context, path, uri, sms);
}
});
</pre><br />
<br />
面積や、JPEG、PNGなど種類を変えて、様々な画像のダウンロードを試してみたが、変更後は、IllegalStateExceptionは全く発生しなくなった(^。^)misahothttp://www.blogger.com/profile/08782505873150853833noreply@blogger.com0tag:blogger.com,1999:blog-8752167966854854944.post-16892545018040619712013-03-04T17:37:00.000+09:002014-03-28T10:48:49.932+09:00MacでAndroidデバッグ用証明書を生成するときにターミナルの文字化けを防ぐにはMacでkeytoolコマンドを使ってAndroidデバッグ用の証明書を生成しようとしたら、メッセージが文字化けして何が何だか…(´・ω・`)<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgIar3Bz3JjHm1tVkEgMNGWuCf0ZgoggkJwMCbQ9s5XJH95gWZME_11HYR0GyhXNUK4gNBZ9OfHU176NYybyPNbqv8tQ6oyqVUJEoWBamyeVN50jFs5elxvXDKIq3XXV_Mx_My_D8Dg0881/s1600/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88+2013-03-04+16.52.57.png" imageanchor="1"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgIar3Bz3JjHm1tVkEgMNGWuCf0ZgoggkJwMCbQ9s5XJH95gWZME_11HYR0GyhXNUK4gNBZ9OfHU176NYybyPNbqv8tQ6oyqVUJEoWBamyeVN50jFs5elxvXDKIq3XXV_Mx_My_D8Dg0881/s1600/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88+2013-03-04+16.52.57.png" /></a><br />
<br />
コマンド出力時のエンコードがUTF-8ではなく、SJISになっていることが原因。Appleが配布しているMac用のJavaの問題と思われ。<br />
<br />
<br />
文字化けを解消するには、環境変数_JAVA_OPTIONSを指定してUTF-8のエンコードを指定する。<br />
<br />
$<span style="color: red;">_JAVA_OPTIONS='-Dfile.encoding=UTF-8'</span> keytool -genkey -v -keystore .android/<b>CUSTOM_debug.keystore</b> -alias androiddebugkey -keyalg RSA -validity 10000 -dname "CN=Android Debug, O=Android,C=JP"<br />
※<b>CUSTOM_debug.keystore</b>は任意の名前に置き換え<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_zC_EEg7Cel__tmQN6v4ma6VG4Ty3b_d6JsmBMqr7zoQRf2-HpS4hESArOlszeR4Wc8UE-xI796yuW9OpJl1gLxJdnOW1yan-zCIl6f3JEy_U7Nrah5F5XNj2V7MORfmU97GnzY9vvr6a/s1600/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88+2013-03-04+17.06.09.png" imageanchor="1"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_zC_EEg7Cel__tmQN6v4ma6VG4Ty3b_d6JsmBMqr7zoQRf2-HpS4hESArOlszeR4Wc8UE-xI796yuW9OpJl1gLxJdnOW1yan-zCIl6f3JEy_U7Nrah5F5XNj2V7MORfmU97GnzY9vvr6a/s1600/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88+2013-03-04+17.06.09.png" /></a><br />
<br />
<br />
<br />
生成済みの証明書を確認する際も、文字化けを防ぐためにオプションを指定する。<br />
<br />
$<span style="color: red;">_JAVA_OPTIONS='-Dfile.encoding=UTF-8'</span> keytool -exportcert -alias androiddebugkey -keystore .android/<b>CUSTOM_debug.keystore</b> -list -v<br />
<br />
パスワードを入力すると、以下のように生成済みの証明書が表示される。<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiYUYmRPVPNl8AOduUN-nD-iuVAbFSNwVz2Du0BqY0EkrTLmEZh8wxk_EexEjJYavaMxtp-53rAUPmTw9UG2O3syjB04z45ajZBno2tCPVWup21JZe065sFlU1sxHA0j6wUK-xkWtnk3KKc/s1600/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88+2013-03-04+17.14.22.png" imageanchor="1"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiYUYmRPVPNl8AOduUN-nD-iuVAbFSNwVz2Du0BqY0EkrTLmEZh8wxk_EexEjJYavaMxtp-53rAUPmTw9UG2O3syjB04z45ajZBno2tCPVWup21JZe065sFlU1sxHA0j6wUK-xkWtnk3KKc/s1600/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88+2013-03-04+17.14.22.png" /></a><br />
<br />
<br />
<br />
<div style="font-size:1.25em"><b>複数の開発マシーンでデバッグキーを共有するとちょっと便利</b></div><br />
ちなみに、作成した.keystoreファイルを複数の開発マシーンで共有すると、同じデバイス宛へアプリを上書きインストールする際にも便利。<br />
<br />
通常、同じデバイスに複数の開発マシーンからデバッグビルドでアプリを上書きインストールすると、開発マシーン毎に証明書が異なるので、アプリのアップデートができず、一度アプリをアンインストールしなければならない。<br />
<br />
デバッグ用証明書を共有しておくと、アンインストールせずにアプリのアップデートができる。<br />
<br />
<br />
Mac OS Xの場合、Eclipseのメニューから、<b>「環境設定」→「Android」→「Build」→「Custom Debug keystore」</b>に共通のデバッグ用証明書を設定すればOK。misahothttp://www.blogger.com/profile/08782505873150853833noreply@blogger.com0tag:blogger.com,1999:blog-8752167966854854944.post-78477019533218839942013-02-06T17:52:00.001+09:002013-02-06T17:52:58.486+09:00Eclipseで設定したXMLのインデント幅がAndroidManifest.xmlに反映されないなと思ったらEclipseでxmlのインデントを半角スペース2つ分と設定している。<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjWfA_0UMulnqE8RtiicBqWa5o0iso2JqthIsWNvyBGKEfaY5Rn4Yy_FO7JdiC9ZqI-yEv4TGlgekaijX1DngCk8A-HSlGcGRI4MPsT_Ph-vwK7Jn7AB3ViXXgfW1Nomqyj-5u2bh1OaiRf/s1600/%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AA%25E3%2583%25BC%25E3%2583%25B3%25E3%2582%25B7%25E3%2583%25A7%25E3%2583%2583%25E3%2583%2588_2013-02-06_17.41.15.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="122" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjWfA_0UMulnqE8RtiicBqWa5o0iso2JqthIsWNvyBGKEfaY5Rn4Yy_FO7JdiC9ZqI-yEv4TGlgekaijX1DngCk8A-HSlGcGRI4MPsT_Ph-vwK7Jn7AB3ViXXgfW1Nomqyj-5u2bh1OaiRf/s400/%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AA%25E3%2583%25BC%25E3%2583%25B3%25E3%2582%25B7%25E3%2583%25A7%25E3%2583%2583%25E3%2583%2588_2013-02-06_17.41.15.png" width="400" /></a></div><br />
<br />
その設定をAndroidで編集するAndroidManifest.xmlなどのxmlにも反映するためには、<br />
「Android > Editors > Use Eclipse setting for indentation width and space or tab character indentation(Android default is 4 space characters)」のチェックが必要。<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiXNjJOQNzowPTQgc7jCnomExQfKabwvYgt5quHVd-kn3ybS6E1lNuOAZUSwuvThd42cehnM7oMun0Rz3mc5Y2xGTZMhC_vf8-Kwa1iaMR4y-aTzyWFRUomEvp-oGbjG5ABjBrCHetmsV8i/s1600/%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AA%25E3%2583%25BC%25E3%2583%25B3%25E3%2582%25B7%25E3%2583%25A7%25E3%2583%2583%25E3%2583%2588_2013-02-06_17.34.51-2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="141" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiXNjJOQNzowPTQgc7jCnomExQfKabwvYgt5quHVd-kn3ybS6E1lNuOAZUSwuvThd42cehnM7oMun0Rz3mc5Y2xGTZMhC_vf8-Kwa1iaMR4y-aTzyWFRUomEvp-oGbjG5ABjBrCHetmsV8i/s400/%25E3%2582%25B9%25E3%2582%25AF%25E3%2583%25AA%25E3%2583%25BC%25E3%2583%25B3%25E3%2582%25B7%25E3%2583%25A7%25E3%2583%2583%25E3%2583%2588_2013-02-06_17.34.51-2.png" width="400" /></a></div><br />
<br />
ちょっと困ったのでメモ。misahothttp://www.blogger.com/profile/08782505873150853833noreply@blogger.com0tag:blogger.com,1999:blog-8752167966854854944.post-63314198616982560632013-02-04T15:15:00.001+09:002013-02-04T16:44:45.974+09:00minSdkVersion16以上ではGCMBroadcastReceiverのintent-filterにcategory指定が要らない4.1以上の端末と、それより下のAPIレベルの端末で、GCMサーバーからメッセージを受信した際のインテントの内容が異なることに気がついた。<br />
<br />
<br />
4.1(APIレベル16)以上<br />
<blockquote>02-04 13:25:37.552: V/GCMIntentService.onMessage:70(11555): Intent { act=com.google.android.c2dm.intent.RECEIVE flg=0x10 <b>pkg=my.app.package</b> cmp=my.app.package/.GCMIntentService (has extras) }<br />
</blockquote><br />
<br />
4.0.3(APIレベル15)<br />
<br />
<blockquote>02-04 13:46:33.894: V/GCMIntentService.onMessage:70(380): Intent { act=com.google.android.c2dm.intent.RECEIVE <b>cat=[my.app.package] </b>cmp=my.app.package/.GCMIntentService (has extras) }<br />
</blockquote><br />
<br />
16以上だと、インテント内にカテゴリー指定がなく、パッケージに変わっている。<br />
<br />
<a href="http://developer.android.com/google/gcm/gs.html#android-app">GCMのドキュメント</a>にも、minSdkVersionを16以上に指定した場合は、マニフェストに記述するcom.google.android.gcm.GCMBroadcastReceiverのintent-filterで、categoryのname値にパッケージ名を指定する必要がないとある。<br />
<br />
<blockquote>Notice that android:name in the category tag must be replaced by your application's package name (and the category tag is not required for applications targeted to minSdkVersion 16 and higher).<br />
</blockquote><br />
<br />
メッセージが送られる端末に応じて、インテントにカテゴリーを含めるのか、パッケージ名を含めるのか、振り分けているようだ。<br />
<br />
<br />
minSDKVersionを16以上にすると、さらに、<a href="http://developer.android.com/google/gcm/gs.html#android-app">permission.C2D_MESSAGEの要求も要らなくなる</a>し、マニフェストがスッキリするね。misahothttp://www.blogger.com/profile/08782505873150853833noreply@blogger.com0tag:blogger.com,1999:blog-8752167966854854944.post-88322871670131388372013-02-04T14:28:00.000+09:002013-02-04T16:51:18.734+09:00DownloadManagerのDOWNLOAD_COMPLETEアクションに対応するBroadcastReceiverを書いていてふと疑問に思ったことDownloadManagerのDOWNLOAD_COMPLETEアクションに対応するBroadcastReceiverを書いていて、ふと疑問。<br />
<br />
DownloadManagerはシステムレベルで提供されているので、DOWNLOAD_COMPLETEアクションが発生した場合、自分のアプリだけにかかわらず、対応するアプリ全てにアクションがブロードキャストされるのではないかと。<br />
<br />
そうだとしたら、フィルタリングする何らかの処理を書いておかないと、他のアプリが叩いたDownloadManagerのDOWNLOAD_COMPLETEアクションに対しても、自分のアプリのBroadcastReceiverが起動してしまうのではないかと。<br />
<br />
<br />
試しに、DOWNLOAD_COMPLETEアクションに対応するBroadcastReceiverの記述を持つアプリをコピーして、それぞれのアプリをインストール。DOWNLOAD_COMPLETEアクションに対応して両方のBroadcastReceiverが起動するのか確認してみた。<br />
<br />
DownlaodManagerにキュー入れする部分のコード。<br />
<pre class="xml" name="code">Request req = new Request(uri);
req.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_MOBILE | DownloadManager.Request.NETWORK_WIFI);
req.setDestinationInExternalPublicDir(Environment.DIRECTORY_PICTURES, subPath);
req.setMimeType(contentType);
req.setTitle(getResources().getString(R.string.app_name));
req.setDescription(getResources().getString(R.string.dl_start_str));
Log.v(getTag(), "ダウンロード開始:" + uri);
//ダウンロードマネージャにダウンロードリクエストをキュー
DownloadManager dlman = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
dlman.enqueue(req);
</pre><br />
DOWNLOAD_COMPLETEアクションをレシーブするDownloadManagerBroadcastReceiverクラス。<br />
<pre class="java" name="code">public class DownloadManagerBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// 関係のあるメッセージがどうか調べる
String pkg = intent.getPackage();
if (pkg.equals("my.app.package")) {
Log.v(getTag(), "関係あるブロードキャスト受信:" + intent);
} else {
Log.v(getTag(), "無関係なブロードキャスト受信:" + intent);
return;
}
} // onReceive
} // class
</pre><br />
<b>my.app.package</b>のAndroidManifest.xml抜粋<br />
<pre class="xml" name="code"><!-- .DownloadManagerBroadcastReceiverではなく、分かりやすさのために敢えてフルネーム指定で記述 -->
<receiver android:name="my.app.package.DownloadManagerBroadcastReceiver" >
<intent-filter>
<action android:name="android.intent.action.DOWNLOAD_COMPLETE" />
</intent-filter>
</receiver>
</pre><br />
<b>my.app.package_dummy</b>のAndroidManifest.xml抜粋<br />
<pre class="xml" name="code"><receiver android:name="my.app.package_dummy.DownloadManagerBroadcastReceiver" >
<intent-filter>
<action android:name="android.intent.action.DOWNLOAD_COMPLETE" />
</intent-filter>
</receiver>
</pre><br />
<b>my.app.package_dummy</b>のDownloadManagerBroadcastReceiverクラス。<br />
<pre class="java" name="code">public class DownloadManagerBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// 関係のあるメッセージがどうか調べる
String pkg = intent.getPackage();
if (pkg.equals("my.app.package_dummy")) {
Log.v(getTag(), "関係あるブロードキャスト受信:" + intent);
} else {
Log.v(getTag(), "無関係なブロードキャスト受信:" + intent);
return;
}
} // onReceive
} // class
</pre><br />
<br />
結果、my.app.packageでDOWNLOAD_COMPLETEアクションが発生した場合は、my.app.package.DownloadManagerBroadcastReceiverが起動、<br />
<br />
<blockquote>関係あるブロードキャスト受信:Intent { act=android.intent.action.DOWNLOAD_COMPLETE flg=0x10 pkg=my.app.package cmp=<b>my.app.package</b>/.DownloadManagerBroadcastReceiver (has extras) }<br />
</blockquote><br />
<br />
my.app.package_dummyでDOWNLOAD_COMPLETEアクションが発生した場合は、my.app.package_dummy.DownloadManagerBroadcastReceiverが起動した。<br />
<br />
<blockquote>関係あるブロードキャスト受信:Intent { act=android.intent.action.DOWNLOAD_COMPLETE flg=0x10 pkg=my.app.package_dummy cmp=<b>my.app.package_dummy</b>/.DownloadManagerBroadcastReceiver (has extras) }<br />
</blockquote><br />
<br />
両方のDownloadManagerBroadcastReceiverが同時に起動することはなかった。<br />
<br />
つまり、それぞれのアプリ内でDownloadManagerを使ってダウンロードした時に発生するDOWNLOAD_COMPLETEアクションには、それぞれのマニフェストに明記しているBroadcastReceiverクラスが対応することになる。<br />
<br />
<br />
onReceive()内でパッケージ名をチェックする必要などはない。<br />
<br />
ということがわかった。<br />
<br />
<br />
では、さらに、同一パッケージ内で、DOWNLOAD_COMPLETEアクションに対応するBroadcastReceiverが複数登録されていたらどうなる?というテストをしてみた。<br />
<br />
AndroidManifest.xml抜粋<br />
<pre class="xml" name="code"><receiver android:name="my.app.package.DownloadManagerBroadcastReceiver" >
<intent-filter>
<action android:name="android.intent.action.DOWNLOAD_COMPLETE" />
</intent-filter>
</receiver>
<receiver android:name="my.app.package.DownloadManagerBroadcastReceiver2" >
<intent-filter>
<action android:name="android.intent.action.DOWNLOAD_COMPLETE" />
</intent-filter>
</receiver>
</pre><br />
<blockquote>02-04 16:23:35.943: V/<b>DownloadManagerBroadcastReceiver</b>.onReceive:42(1695): 関係あるブロードキャスト受信:Intent { act=android.intent.action.DOWNLOAD_COMPLETE flg=0x10 pkg=my.app.package cmp=my.app.package/.DownloadManagerBroadcastReceiver (has extras) }<br />
02-04 16:23:36.033: V/<b>DownloadManagerBroadcastReceiver2</b>.onReceive:42(1695): 関係あるブロードキャスト受信:Intent { act=android.intent.action.DOWNLOAD_COMPLETE flg=0x10 pkg=my.app.package cmp=my.app.package/.DownloadManagerBroadcastReceiver2 (has extras) }<br />
</blockquote><br />
両方のBroadcastReceiverが呼ばれた。起動順序はマニフェストに記述した順序かな?misahothttp://www.blogger.com/profile/08782505873150853833noreply@blogger.com0tag:blogger.com,1999:blog-8752167966854854944.post-33261599280109083952013-01-26T15:45:00.000+09:002013-01-26T15:49:50.690+09:00NotificationのBigPictureStyleで画像全体を表示させてみるBigPictureStyleでは、画像の中央を中心にして、最大幅450dpに合う大きさへ画像がクロップされる。<br />
<br />
でもそれだと、画像が縦長の場合は上下が大きくクロップされてしまい、何が写っているのかよく分からない。<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiuC8hpzWCfyXbbNaX_mRNFr4nouiD2AbYz7MCrih06seC-j3oS1MXY6O3O38g6IXdu_wpYqxTc_PI5xhXg3Pz_6tAYtpkI3R9ITFqt2DCL27Zuob1Ues4A1eMSJaelUQguQAdpPefNlCiG/s1600/dog.jpg" imageanchor="1" style="margin-left:1em; margin-right:1em"><img border="0" height="320" width="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiuC8hpzWCfyXbbNaX_mRNFr4nouiD2AbYz7MCrih06seC-j3oS1MXY6O3O38g6IXdu_wpYqxTc_PI5xhXg3Pz_6tAYtpkI3R9ITFqt2DCL27Zuob1Ues4A1eMSJaelUQguQAdpPefNlCiG/s320/dog.jpg" /></a></div><br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg5h30j990t2-p6RDP52qBokXzNfYkY_f2muPUxJ-L4Y4uXSQW5t-31MW0s9Jqxl4wpZpF1PFdaS80RD5I4Jc4vTCFFqgXL-MH-zwGn4Je1m6D-LglB8w_rM9K_sA8kfRvSeit2j15MqM-I/s1600/device-2013-01-26-151324.png" imageanchor="1" style="margin-left:1em; margin-right:1em"><img border="0" height="184" width="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg5h30j990t2-p6RDP52qBokXzNfYkY_f2muPUxJ-L4Y4uXSQW5t-31MW0s9Jqxl4wpZpF1PFdaS80RD5I4Jc4vTCFFqgXL-MH-zwGn4Je1m6D-LglB8w_rM9K_sA8kfRvSeit2j15MqM-I/s320/device-2013-01-26-151324.png" /></a></div><br />
画像の比率によらず、bigPicture部分で、常に画像全体が見られるようにしてみた。<br />
<pre class="java" name="code">private Bitmap getBigPicture(Resources res, String path) {
Bitmap canvasBmp = null;
final float WIDTH_DP = 450.0f;
final float HEIGHT_DP = 192.0f;
// ピクセル密度を取得する
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
float scaleDensity = metrics.scaledDensity;
// ピクセル密度を加味してターゲットの幅と高さのピクセル数を計算する
int targetWidthPx = (int) (WIDTH_DP * scaleDensity);
int targetHeightPx = (int) (HEIGHT_DP * scaleDensity);
// 画像読み込みオプションを作成
BitmapFactory.Options options = new BitmapFactory.Options();
// メモリが少なくなったらパージできるようにオプションを設定
options.inPurgeable = true;
// 画像サイズだけを読み込むようにオプションを設定
options.inJustDecodeBounds = true;
// 画像読み込み
Bitmap srcBmp = BitmapFactory.decodeFile(path, options);
// 画像の幅と高さのピクセル数を取得する
int srcWidth = options.outWidth;
int srcHeight = options.outHeight;
// Matrixを取得する パディングは0で小さな画像は拡大しない
Matrix mat = getMatrix(srcWidth, srcHeight, targetWidthPx, targetHeightPx, 0, false);
// Paintを作成する
Paint bmpPaint = new Paint();
bmpPaint.setFilterBitmap(true);
// ターゲット幅と高さで背景が透明のキャンバスを作成
canvasBmp = Bitmap.createBitmap(targetWidthPx, targetHeightPx, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(canvasBmp);
// 画像サイズだけでなく画像そのものを読み込むようにオプションを設定しなおす
options.inJustDecodeBounds = false;
// 実際に画像を読み込み
srcBmp = BitmapFactory.decodeFile(path, options);
// 背景が透明のキャンバスに画像を合成する
canvas.drawBitmap(srcBmp, mat, bmpPaint);
// メモリ解放
srcBmp.recycle();
srcBmp = null;
Log.i("#getBigPicture()", "scaleDensity=" + scaleDensity + " srcWidth="
+ srcWidth + " srcHeight=" + srcHeight + " targetWidthPx="
+ targetWidthPx + " targetHeightPx=" + targetHeightPx);
return canvasBmp;
}
/**
* @param sw ソース幅
* @param sh ソース高さ
* @param pw この幅にフィットさせる
* @param ph この高さにフィットさせる
* @param padding フィット時に考慮するパディング
* @param enableMagnify 画像が小さい場合に拡大するか否か
* @return フィットさせるためのスケール値
*/
private static final Matrix getMatrix(int sw, int sh, int pw, int ph,
int padding, boolean enableMagnify) {
float scale = getMaxScaleToParent(sw, sh, pw, ph, padding);
if (!enableMagnify && scale > 1.0f) {
scale = 1;
}
Log.i("#getMatrix()", "scale=" + scale);
Matrix mat = new Matrix();
mat.postScale(scale, scale);
mat.postTranslate((pw - (int) (sw * scale)) / 2,
(ph - (int) (sh * scale)) / 2);
return mat;
}
private static final float getMaxScaleToParent(int sw, int sh, int pw,
int ph, int padding) {
float hScale = (float) (pw - padding) / (float) sw;
float vScale = (float) (ph - padding) / (float) sh;
return Math.min(hScale, vScale);
}
</pre><br />
上記では、bigPictureの最大幅・高さのキャンバスを作り、そのキャンバスサイズに合わせて画像を縮小し、中央に配置する。<br />
<br />
BigPictureStyleでは、450dpに合わせて、上下左右に隙間なく画像を表示するよう設計されている。しかしこの設計では、多くの場合に上下や左右に隙間ができても、常に画像全体が写るようにして、どんな画像か把握出来るようにしている。<br />
<br />
<br />
上記メソッドを呼び出してBigPictureStyleを作るコードのスニペット。<br />
<pre class="java" name="code">NotificationCompat.BigPictureStyle bigPictureStyle = new NotificationCompat.BigPictureStyle(
notificationBuilder);
bigPictureStyle.bigPicture(getBigPicture(res, path));
bigPictureStyle.setBigContentTitle(contentTitle);
bigPictureStyle.setSummaryText(contentText);
notificationBuilder.setStyle(bigPictureStyle);
</pre><br />
<br />
結果、bigPicture部分はこんなふうに表示される。<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhtYmqDLWipIM5Wb2-PP_rfP4Qy1_XiO-Dd73hYNdKrISVrtIAHbLcfbMrZ2kKFi3h6CcMvHh4X-kA9IgjjTxyr9k0ZBp6aoBYNhK5Cae3aOY6tF3PLbagtVv9hP37qNiILCj4w_ufx-Gt0/s1600/device-2013-01-26-153948.png" imageanchor="1" style="margin-left:1em; margin-right:1em"><img border="0" height="184" width="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhtYmqDLWipIM5Wb2-PP_rfP4Qy1_XiO-Dd73hYNdKrISVrtIAHbLcfbMrZ2kKFi3h6CcMvHh4X-kA9IgjjTxyr9k0ZBp6aoBYNhK5Cae3aOY6tF3PLbagtVv9hP37qNiILCj4w_ufx-Gt0/s320/device-2013-01-26-153948.png" /></a></div>misahothttp://www.blogger.com/profile/08782505873150853833noreply@blogger.com0