HUGOに移行しました

2013年からWORDPRESSでこのサイトを運用してきましたが、HUGOに乗り換えました。
理由は

  • サーバの管理が面倒臭い
  • アップデートが面倒臭い
  • セキュリティもなんか不安
  • サーバ費用(S3)がもったいない (←これが一番)

ちなみに今の構成はありがちですが 「S3 + CloudFront」 です。
サーバ費用がほとんどかからない上にSSLにも対応するのがありがたいですね。

なお、昔のコンテンツは既に役に立たなくなってしまっていると思うので、ほとんど削除しました。
またボチボチ投稿していきます。

ホイール替えた!

ギア1枚軽く回せる」とか、「巡航速度が3km速くなった」とか。
ホイールをアップグレードすると、すごい効果があるように聞きますよね。
程度の差こそありますが、自分の自転車仲間からもそのように聞きます。

うん。これはホイールを変えるしかない!

これまで使っていたホイールは完成車に標準で付いてきたジャイアント「PR2」、前後で1850gほどの鉄下駄と呼ばれるホイールです。
これをどれに変更するか・・うーん、迷います。

WEBで色々検索していると「最低限ZONDA」と書かれているサイトをよく見ますよね。
履き替えて違いを感じられるのは、この辺のグレードからなのだとか。

そのZONDA基準でホイールを探すとこの辺りか?

  • Campagnolo ZONDA
  • FULCRUM RACING3
  • MAVIC KSYRIUM ELETE

うーん、コスパの良いZONDAにするか・・
リムが軽いと評判のKSYRIUMにするか・・

・・って迷ってけど、「最低限」ってことはギリギリ違いがわかるって事なのか?
もしかして違いが感じられない事もあるのか??
なにせ、感覚の鈍さには自信がある!

ならば、確実に違いがわかるであろう上位モデルを選んだ方が幸せになれるのでは?
結局上のグレードが欲しくなるのではないのか??

そんな事考えて、迷いに迷った挙句、「FULCRUM RACING ZERO」を発注しました。
理由はズバリ「かっこよさ」!
(本当はシャマルが良かったんですけど、なんとなくロゴのデザインが・・)

同時にタイヤは評判の「Continental GP4000S2」を選択。
うん、この組み合わせなら確実に速くなっているだろう!

んで、装着!
かれこれ300kmほど走ってみました!
一漕ぎ目から驚くような軽さがあったりするのか?!
坂でギア1枚分軽くなったのか?!

結果: 違いがわからない!!

まぁ・・、なんとなくそんな気はしてましたけど。

いいんです。いいんですよ!
ホイールを手で回し、いつまでも回るホイールを眺めながらビールを頂く。これだけで大満足ですっ!!(強がり)

あ、タイヤのGP4000S2は、噂通り良いものでした。
グリップ性能が以前の物と比べると段違い。コーナーでの安心感が違います。

vue.jsでなぜかdataの要素がundefinedになってしまう(涙

mixinする共通コンポーネントがあるんですけどね、
そのコンポーネント内部で使用するプライベート変数をプライベートだと分かりやすいように、先頭にアンダースコアを付けて定義してたんですよ。

例えばこんな感じ↓

export default {
    mounted() {
        console.log("_testcode is " + this._testcode);
    },
    data(){
        return {
            _testValue:"abc",
            _testArray:[],
        }
    }
}

このようなコンポーネントをマウントすると当然
「_testcode is abc」
ってコンソールに表示されて欲しいんだけど、
なぜか
「_testcode is undefined」
って表示されちゃう。なんでだよっ!!配列も”[]“で初期化されていると思って
_testArray.push("おにぎり")
とかすると
「 Cannot read property ‘push’ of undefined 」
とか言われて怒られちゃう!
https://vuejs.org/v2/api/#data
によると

Properties that start with _ or $ will not be proxied on the Vue instance because they may conflict with Vue’s internal properties and API methods. You will have to access them as vm.$data._property.
ってことなので、 this.$data._testArray.push("おにぎり") とすればOK でも面倒なので適当なプリフィックスを決めて、使った方が良さげですね。

PEMのBASE64文字列からPrivateKeyを生成する方法

PEMをテキストファイルとして読み込んでjava.security.PrivateKeyを作る必要があったんだけど、読み取れるものと読み取れないものがあってちょっと困っていた。
読み取れないものは「not a sequence at ・・」みたいなエラーになってる。

で、良く見るとヘッダが異なる事に気がついた。
要するに PKCS#8 は読み込めるけど PKCS#1 はすんなり読んでくれないんですね。

ちなみに 見分け方は
-----BEGIN PUBLIC KEY-----
で始まってたら PKCS#8

-----BEGIN RSA PUBLIC KEY-----
で始まってたら PKCS#1

で、具体的なコードは下記のようになる。

PKCS8の時は簡単、PKCS8EncodedKeySpecにそのままバイト配列を食わせればOK。

String code "BASE64の文字列";
byte[] bytes = Base64.getDecoder.decode(code);

KeySpec spec = new PKCS8EncodedKeySpec(bytes);
KeyFactory kf = KeyFactory.getInstance("RSA");
PrivateKey privateKey = kf.generatePrivate(spec);

PKCS1の時はDerInputStreamを使ってパラメータを分解して、その情報からPrivateKeyを作成する。

String code "BASE64の文字列";
byte[] bytes = Base64.getDecoder.decode(code);

DerInputStream derReader = new DerInputStream(bytes);
DerValue[] seq = derReader.getSequence(0);

BigInteger modulus = seq(1).getBigInteger;
BigInteger publicExp = seq(2).getBigInteger;
BigInteger privateExp = seq(3).getBigInteger;
BigInteger prime1 = seq(4).getBigInteger;
BigInteger prime2 = seq(5).getBigInteger;
BigInteger exp1 = seq(6).getBigInteger;
BigInteger exp2 = seq(7).getBigInteger;
BigInteger crtCoef = seq(8).getBigInteger;

KeySpec spec = new RSAPrivateCrtKeySpec(
     modulus, publicExp, privateExp, prime1, prime2, exp1, exp2, crtCoef);
KeyFactory kf = KeyFactory.getInstance("RSA");
PrivateKey privateKey = kf.generatePrivate(spec);

あ、ちなみにですが、“BASE64の文字列"はヘッダとフッタの間に記述されている文字列です。

-----BEGIN RSA PUBLIC KEY-----
      (ここの文字列)
-----END RSA PUBLIC KEY-----

S3署名付きURLをCloudFront経由で使う (java.security.PrivateKeyを指定して)

タイトルの通りなんですけど、S3の署名付きURLをCloudFront経由で使う例です。
ちょっと困った事があったのでここにメモしておきます。

単純にpemファイルなどがパス指定で取得できるのであれば

String privateKey = "プライベートキーへのパス";
String keyPairId  = "hogehoge";
Date expiration = /* 有効期限 */;
String distributionDomain = "xxxxxxx.com"; // CloudFrontに指定してるドメイン
String objectKey = "sample.jpg";

String url
 = CloudFrontUrlSigner.getSignedURLWithCannedPolicy(
    https, distributionDomain, privateKey, objectKey, keyPairId, expiration);

こんな感じで取得できる。

でもkeyをアプリ独自に暗号化してた場合などではファイルを直接指定することができない場合がありますね。
で、java.security.PrivateKeyを使って署名付きURLを欲しかったのですが、丁度良いオーバーロードがありませんでした。

どうすべ?と思ってCloudFrontUrlSignerのソースを見るとSignerUtilsを使えば良さそうな事がわかった。

java.security.PrivateKey privateKey = /* キーを作成 */;

String resourcePath 
  = SignerUtils.generateResourcePath(https,distributionDomain,objectKey);

String url
  = CloudFrontUrlSigner.getSignedURLWithCannedPolicy(resourcePath,keyPairId,privateKey,expiration)

こんな感じ。

Laravel の migrationからSeederを直接実行する

Migrationを作って新規テーブルを追加した時に、初期データを投入したい事があると思う。 通常はSeederを個別に実行するのだと思うけど面倒ですよね。

そこで migrationから直接Seederを呼び出す方法です。 Artisanコマンドってこうやって使えるんですな。

Migrationファイル

use Illuminate\Support\Facades\Artisan;

(省略)

public function up()
{
    (省略)

    Artisan::call('db:seed', [
        '--class' => UserTypeTableSeeder::class,
        '--force' => true,
    ]);
}

この例ではUserTypeTableSeederを実行してます。

ちなみにドライランで実行されるSQLを確認できるオプション「–pretend」でもちゃんとSeederの実行内容が出力されますよ。

Playframework2.6でファイルアップロードのユニットテスト

Multipartでファイルアップロードするapiのテストを行いたかったんだけど、めちゃくちゃハマったというか、わからなかったのでここにメモする!

ポイント1 「アップロードするファイルをリソースから取得する」

まず、テストケースで使用するリソース(今回はアップロードするファイル)はどこに保存すればいいのか分からんね!

そんな時は次のコマンドを叩けばわかるぞ!

sbt "show test:resourceDirectory" 
> [info] Test / resourceDirectory
> [info] /Users/honyarara/project/test/resources

そしてこのファイルを取得するにはこうだ!

/Users/honyarara/project/test/resources/testfile.jpg を取得する時は。

val fileUrl = getClass.getResource("/testfile.jpg")

先頭のスラッシュが無いとnullになっちゃうので注意!

 

ポイント2 「multipart/form-dataのFakeRequestを作成する」

WEBに転がってる情報が古くて正解になかなかたどり着けなかったよ。

最初に リソースから取得したファイルをTemporaryFileに変換する。 既に存在するファイルのTemporaryFileって何言ってるんだ?と思うかもしれないが、そういうもんだ。

val tempFile = SingletonTemporaryFileCreator.create(Paths.get(fileUrl.toURI))

で、このtempFileを使ってFilePartを作成する

val filepart = FilePart[TemporaryFile]("imageFile","testfile.jpg",Some("image/jpeg"),tempFile)

次にファイルと同時に送信するフォーム情報を作成する

val data:Map[String,Seq[String]] = Map(
  "title" -> Seq("パイスラッシュ女子")
)

後は、ここまでの情報を組み立てればOK

val formData = MultipartFormData[TemporaryFile](
	dataParts = data,files = Seq(filepart),badParts = Seq())

val request = FakeRequest(POST,"/upload")
	.withMultipartFormDataBody(formData);	

これで “multipart/form-data” なFakeRequestができる。

 

ポイント3 「実行する!」

実行時にはroute経由でやらないと上手く動かなかった。

val result = route(app, request).get
status(result) mustBe OK

 

まとめると

import java.nio.file.Paths
import play.api.libs.Files.SingletonTemporaryFileCreator
import play.api.libs.Files.TemporaryFile
import play.api.mvc.MultipartFormData
import play.api.mvc.MultipartFormData.FilePart

class UploadControllerSpec extends PlaySpec with GuiceOneAppPerTest with Injecting
{
  "画像をuploadするとOkを返すよ" in {

  	// 送信するフォームデータ
	val data:Map[String,Seq[String]] = Map(
	  "title" -> Seq("パイスラッシュ女子大生")
	)

	// リソースからファイルを取得してTemporaryFileを作成する
  	val fileUrl = getClass.getResource("/testfile.jpg")
  	val tempFile = SingletonTemporaryFileCreator.create(Paths.get(fileUrl.toURI))

  	// ファイルパートを作成する
  	val filepart = FilePart[TemporaryFile](
  			"imageFile","testfile.jpg",Some("image/jpeg"),tempFile)

  	// 組み合わせてMultipartFormDataを作成
  	val formData = MultipartFormData[TemporaryFile](
  		dataParts = data,files = Seq(filepart),badParts = Seq())

  	// FakeRequestを作成
  	val request = FakeRequest(POST,uploadEndpointUrl)
  		.withMultipartFormDataBody(formData)

  	// 実行
  	val result = route(app, request).get
  	status(result) mustBe OK
  }
}

Polyから落としてきたOBJファイルをglTFに変換してThree.jsで表示する

googleが3Dモデルの共有ライブラリPolyを公開しましたね。 このライブラリでは数多くのモデルがOBJ形式でダウンロードできるようになっています。

ただ、googleにしては意外だったのはglTF形式に対応してないこと。 まだ時期尚早だと判断したのかな??

glTFってのは3Dモデルの標準化を狙った次世代の3Dモデルフォーマットで、このところ注目のモデル形式なのです。 今後はglTFが標準になってくることを祈って、Polyから拾ってきたモデルをglTFに変換してThree.jsで表示してみます。

前提 ・webpackでビルドできる環境ができてる ・前の投稿のようにsceneに別の形式でモデルを描画できている

 

サンプルとして使わせてもらうのは↓の恐竜
T-Rex scanned with Qlone

ここからダウンロードしたファイルを展開(解凍)して
[projectroot]/src/3dmodels/t_rex
の中に保存しておきます。
※“model_T Rex_20171104_194215374.obj"ってファイル名なんですけど、あとあと面倒なので"t_rex.obj"にリネームしておいてください。
   

んで、このOBJ形式のモデルをglTFに変換するのですが、obj2gltf を使わせてもらいます。
https://github.com/AnalyticalGraphicsInc/obj2gltf

公式サイトにある通り、プロジェクトのルートディレクトリで
$ npm install --save obj2gltf を行います。
package.jsonにobj2gltfが追加されましたね??

んで、npmのスクリプトでPolyから落としてきたモデルをglTFに変換しましょう。
下記のようにスクリプトを追記して

package.json

{
  "scripts": {    
    "gltf:rex": "obj2gltf -b -i 'src/3dmodels/t_rex/t_rex.obj' -o 'public/t_rex/t_rex.glb'"
  },  
  ・・省略・・
}

※ -oで指定するのが出力先ファイル名なのですが、ファイル名は無視されてしまうっぽいです。入力されたobjと同じ名前で出力されてしまいます。

obj2gltfを実行

$ npm run gltf:rex

そしたら public/t_rex/t_rex.glb ってのが作成されたかと思います。
     

次にこのglTF形式のモデルを表示してみましょう。
全部のコードを載せるのは冗長なので端折って説明しますね。
(それ以外のコードは前の投稿を見てください)

glTFのローダーはOBJと同じようにexamplesに保存されていますのでそれをインポートします。

import 'imports-loader?THREE=three!../../node_modules/three/examples/js/loaders/GLTFLoader'	

で先ほど作ったglTFモデルを読みこんでsceneに追加します。

var loader = new THREE.GLTFLoader();
var url = "t_rex/t_rex.glb";
loader.load( url, function(data) {
    var object = data.scene;
    scene.add( object );
}, undefined, function ( error ) {
    console.error( error );
} );

こんな感じ。

Three.jsでOBJファイルを読み込む(webpackを使って)

Three.jsでOBJファイルを読み込む時にMTLLoaderとOBJLoaderを使いたいけど、webpackを使った時に導入方法がよくわからなかったので色々調べたメモ。
ついでにStatsでFPSの計測とOrbitControlsを使ったマウス操作にも対応。

前提
・webpackでビルドできる環境ができてる
・objモデルはどこからか拾ってきてください

まずは"three.js"と、three.jsのexampleに保存されているファイルをインポートするために
“imports-loader"と"expose-loader"をpackage.jsonに追加しておく。

package.json

"devDependencies": {
  "imports-loader": "^0.7.0",
  "expose-loader": "^0.7.0",
  "three": "^0.88.0",
  ・・省略・・
}

 

表示するHTMLはwebpackで処理されたapp.jsを読み込む。それだけ。

index.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>three.js sample</title>
    <style>
        body { margin: 0; }
        canvas { width: 100%; height: 100% }
    </style>
</head>
<body>
<script src="js/app.js"></script>
</body>
</html>

   

んで、これが実際の処理

app.js

var THREE = require('three');

// Threeのサンプルソースからインポート
import 'imports-loader?THREE=three!../../node_modules/three/examples/js/loaders/MTLLoader'
import 'imports-loader?THREE=three!../../node_modules/three/examples/js/loaders/OBJLoader'
import 'imports-loader?THREE=three!../../node_modules/three/examples/js/controls/OrbitControls.js'
// ついでにStatsも使えるように
require("expose-loader?Stats!../../node_modules/three/examples/js/libs/stats.min.js");

(function(){
  var container;
  var stats;
  var scene,camera,ambient;
  var renderer;
  var initialized = false;

 
  init();
  animate();

  function init() {

    container = document.createElement( 'div' );
	  document.body.appendChild( container );

    var width = window.innerWidth,
        height = window.innerHeight;
        
    scene = new THREE.Scene();

    // 読み込むモデルの指定
    var modelPath = "cat/";   // mtlとobjの保存されてるパス
    var mtlName = "cat.mtl";  // mtlファイル名
    var objName = "cat.obj";  // objファイル名


    var objLoader = new THREE.OBJLoader();
    var mtlLoader = new THREE.MTLLoader();
    
    // onProgressとonErrorの引数にはProgressEventってオブジェクトが渡されます
    var onProgress = function ( evt ) {
      if ( evt.lengthComputable ) {
          var completed = evt.loaded / evt.total * 100;
          console.log( Math.round(completed, 2) + '% downloaded' );
      }
    };
    var onError = function ( evt ) {
        console.error("モデルの読み込みに失敗したみたい");
        var xhr = evt.target;
        if (xhr){
          console.error(xhr.responseURL + " status:" + xhr.status + "(" + xhr.statusText + ")");
        }
    };

    mtlLoader.setPath( modelPath ); //パスを指定。これを指定しないとテクスチャ画像の読み込み先が変なことになる。
    mtlLoader.load(mtlName, function (materials){
        materials.preload();

        objLoader.setMaterials(materials);
        objLoader.setPath( modelPath);
        objLoader.load(objName, function (object){
            
            scene.add( object );

            initialized = true;
        }, onProgress, onError);

    }, onProgress, onError);


    ambient = new THREE.AmbientLight(0xffffff);
    scene.add(ambient);

    camera = new THREE.PerspectiveCamera(45, width / height, 1, 5000);
    camera.position.set(0, 100, 200);
    camera.lookAt(new THREE.Vector3(0, 0, 0));


    renderer = new THREE.WebGLRenderer();
    renderer.setSize(width, height);
    renderer.shadowMap.enabled = true;
    container.appendChild( renderer.domElement );


    // マウスで操作可能とする
    var controls = new THREE.OrbitControls( camera, renderer.domElement );
    controls.maxPolarAngle = Math.PI * 0.5;
    controls.minDistance = 100;
    controls.maxDistance = 3000;

    // FPSなどのモニタリング
    stats = new Stats();
    container.appendChild( stats.dom );
  }

  function animate() {
    requestAnimationFrame( animate );
    if (!initialized) return;

    renderer.render( scene, camera );
    stats.update();
  }

})();

   

よくあるThree.jsのサンプルと変わらないけど、先頭のインポートたちが重要。
この記述

import 'imports-loader?THREE=three!../../node_modules/three/・・省略・・'

でnode_modulesの中に展開されているjsファイルをインポート。

“mtlLoader.load"あたりでインポートされたMTLLoaderとOBJLoaderを使ってモデルを読み込んでます。

 

注意点

たまに正しく読み込めないOBJファイルがある。(Free3Dにあるブガッディとかはダメだった)
なんか変だな?と思ったら別のOBJファイル、MTLファイルを使ってみて。

SwiftでQRコードを生成する

Core Imageを使うと簡単に作れるんですね〜 Swiftで実装するとこんな感じです。

func makeQRCodeImage(code:String) -> UIImage? {    
    guard let ciFilter = CIFilter(name: "CIQRCodeGenerator") else {
        return nil
    }
    ciFilter.setDefaults()
    
    // QRコードを設定
    ciFilter.setValue(code.data(using: String.Encoding.utf8), forKey: "inputMessage")
    
    // 誤り訂正レベルを設定
    // "L" "M" "H" が設定できるみたい
    ciFilter.setValue("M", forKey: "inputCorrectionLevel")
    
    if let outputImage = ciFilter.outputImage {        
    	// 作成されたQRコードのイメージが小さいので拡大する
        let sizeTransform = CGAffineTransform(scaleX: 10, y: 10)
        let zoomedCiImage = outputImage.applying(sizeTransform)
        return UIImage(ciImage: zoomedCiImage, scale: 1.0, orientation: .up)
    }
    
    return nil
}