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

でも面倒なので適当なプリフィックスを決めて、使った方が良さげですね。

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
  }
}

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の実行内容が出力されますよ。

php artisan migrate --pretend

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 );
} );

こんな感じ。


“T-Rex scanned with Qlone” by Ronen Horovitz / CC BY

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ファイルを使ってみて。

iOS11のデザインって・・

iOS11にしたけど、なんかダサい。
妙に太いフォント、文字のサイズ、UIの細かい所、アイコンの角丸のアール、影の統一感がない。
細かいところがつめきれていないというか、バランスが悪いというか。。

自分の感性が間違ってるのかもしれんけど、Apple製品にあるまじきレベル。
これでOKだすクックがダメだわ。

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
}

SharedPreferencesでputStringSetを使う時の注意点

こんな感じでgetStringSetで得た結果に直接要素を追加して、
putStringSetしてもダメっぽい。

SharedPreferences pref 
  = context.getSharedPreferences("name",Context.MODE_PRIVATE);

String key = "keyA";

// Setを取得する
Set<String> set1 = pref.getStringSet(key, new HashSet<String>());

// Setに文字列を追加
set1.add("追加する文字列");

// そのまま保存
SharedPreferences.Editor editor = pref.edit();
editor.putStringSet(key, set1);
editor.apply();

 

この状態でgetStringSetするとちゃんと設定されてて正常に動いているように見えるんだけど、
プロセスが再起動されたあとに消えてます。(2件目以降)

対応としては下記のように別のSetを作ってコピーしてあげるとちゃんと動いてくれた。

// Setをクローンする
HashSet<String> set2 = new HashSet<>();
set2.addAll(set1);

SharedPreferences.Editor editor = pref.edit();
editor.putStringSet(key, set2);
editor.apply();

モヤモヤしますな。