Subscribed unsubscribe Subscribe Subscribe

3DCG手習い

学習/馴れるコストが異様に高いというイメージを持っていたけど意外とそんなことはなくて、1日やってみたらちょっと満足感を得られる程度には形になった。思ったようなものを作れるまでかなり遠いもののこの調子だと適度に続けられそうな気がする。


sculptris で粘土をこねる。

f:id:ToQoz:20160817222146p:plain

blender に import してボーンを設定する。色んなポーズが取れるようになる。

f:id:ToQoz:20160817222154p:plain

f:id:ToQoz:20160817222201p:plain

動かしてみた。

https://i.gyazo.com/50ed20cf043c5fc436692f8e8768a28a.gif

見たもの

操作の解説が分かりやすくて、自分の環境で一部のショートカットが効かなくて困ったこと以外は結構スムーズにいった。

気軽な playground

gopwt 用の play.golang.org みたいなのを作りたいなと思っていた。だからといって、入力されたコードをサーバーで実行しても大丈夫なような環境を作るのも面倒ということで、gopwt で変換して、それを、gopherjs で javascript にして、ブラウザで実行してもらえば良いというとても気軽な案を思いついたら結構うまくいっている。そのうち play.gopwt.org 的なところで動かす予定。

テストコードが対象になるので、go test がやっているような main.main を作ってやらないといけないんだけど、それは適当に手を抜きつつこんなテンプレートで作ってる。

testmain.go:

package main

import (
    "testing"
)

var benchmarks = []testing.InternalBenchmark{}
var examples = []testing.InternalExample{}
var tests = []testing.InternalTest{
{{range .}}
    {
        "{{.}}",
        {{.}},
    },
{{end}}
}

func main() {
    match := func(pat string, str string) (bool, error) {
        return true, nil
    }
    testing.Main(match, tests, benchmarks, examples)
}`

gopwt につけようと思っている expected と got の diff

github.com/sergi/go-diff/diffmatchpatch を使う。

なんか良さげな基準で文字単位か行単位かを切り替えたい。power assert は何も考えずに assert 書いとけばいい感じの出力が得られるというのが嬉しいところなので、オプションはつけたくないということで考えた。

このような用途に限って言うと、すごく似てるけどちょっと違ってどこが違うのが見つけるのが辛い時だけに文字単位の diff があったらよくて、どう見ても違うみたいなケースでは必要ないだろうという予想があって、まず文字単位で diff をとって、複数行にまたがる diff があったら行ごとの diff をとり直すということにしようかなと思った。

あと副作用的に、複数行にまたがらなければ [- -] {+ +} みたいなマークなしで色だけで ins/del 表現してもそんな分かりにくくなくて良さそうだった。

文字単位で嬉しい

word-diff:

        foo(x("a[-'-]{+"+})
bar

line-diff:

-       foo(x("a')
+       foo(x("a")
bar

行単位でいい

word-diff:

        fo{+x
        bo+}o
bar
ba[-r2-]{+z+}

line-diff

-       foo
+       fox
+       boo
 bar
-bar2
+baz

昔からここからハゲそうだなと予感がしつつも生えていた産毛(Mのところ)がなくなって気付いたんだけど、かなり薄い産毛でもあると無いでかなり外観が変化する。流れが無い感じ

シェルスクリプトでテストが書きたいことがよくある

シェルスクリプトのテストを書きたい時というのは自分はあまり無いんだけど、シェルスクリプトでテストを書きたいことはわりと頻繁にある。なんかした後に diff が{なかったら,あったら}OKみたいなやつとか

0 で終了しなければ、コケました、詳細は適当に stdout/stderr に出しといたんで。でもいいんだけど、出力する形式が決まってたほうがなんか嬉しい気がしたから作った

$ echo "[ 0 != 1 ]" > test_1.sh
$ echo "[ 0 != 0 ]" > test_2.sh
$ echo "diff -u test_1.sh test_2.sh" > test_3.sh
$ tapf ./test_*.sh
TAP version 13
1..3
ok 1 - ./test_1.sh
not ok 2 - ./test_2.sh
not ok 3 - ./test_3.sh
  ---
  stdout: |
    --- test_1.sh       2016-01-24 23:34:58.000000000 +0900
    +++ test_2.sh       2016-01-24 23:34:58.000000000 +0900
    @@ -1 +1 @@
    -[ 0 != 1 ]
    +[ 0 != 0 ]

github.com

docker machine+insecure docker registry

外に公開せず楽したい時

./insecure_docker_registry:

#!/bin/sh

ip=$(ifconfig | grep 'addr:192\.168' | awk -F' ' '{print $2}' | sed -e 's/addr://')

if_not_exist() {
  test $(cat /var/lib/boot2docker/profile | grep "insecure-registry $ip:5000" | wc -l) = 0
}

insert() {
  echo 'EXTRA_ARGS="$EXTRA_ARGS --insecure-registry '$ip':5000"' | sudo tee -a /var/lib/boot2docker/profile
}

if_not_exist && insert && sudo /etc/init.d/docker restart
$ machine=docker-dev
$ docker-machine scp ./insecure_docker_registry $machine:/tmp/
$ docker-machine ssh $machine /tmp/insecure_docker_registry

gopwt v0.0.1

gopwt(PowerAssert for golang)がだいたいできた - コンドルが飛んでいる。

まずはv0.0.1を打てるようにやっていく予定

から 100年ぐらいがたったけど v0.0.1 ようやく打った。最近 Lambda + API Gateway でとにかく節約みたいな感じで Go で Web アプリを書いたりということが当分なかったんだけど、ちょっとは書いていて2015年はその際に気付いたバグの修正を時々した。 あとは assert.Require という名前があまり気にいってない関数を追加した。これは基本 assert.OK と一緒なんだけど、アサーションが失敗した時に t.Skipを呼ぶやつで後続のアサーションの前提条件っぽいようなアサーションに用いる。具体的に使いたいケースはこういう感じ。

u, err := createUser("ToQoz", "toqoz403@gmail.com")
assert.Require(t, err != nil)
assert.OK(t, u.Name = "ToQoz")
assert.OK(t, u.Email = "toqoz403@gmail.com")

で、もう一個欲しかったのが assert.OK(t, a == nil, "a should not be nil") のように失敗した時の出力するメッセージを渡せるというもので、まあ無くてもそこまで困ってなくて渋っていたけどもう2016年だということでさっき追加した。とりあえずこれで当初から欲しいと思っていた機能は揃ったのでタグを打つことにした。

contenteditable で div じゃなくて段落 + 改行で文章が書きたい

abc<ENTER>abc と入力した際に abc<div>abc</div> じゃなくて <p>abc</p><p>abc</p> となって欲しいというような話。改行はShift+Enterでできるようにした。

なんとなく動いてる。

AWS API Gateway の Mapping Template を楽に試せるようにした

API Gateway + Lambda で API なり Web サイトなりを作っていると、

  • Mapping Template が上手く動いてるか確認するのがデプロイしてみないといけなくて面倒
  • ローカルで開発してる時に lambda function に event を渡せない (もしくは固定のやつをとりあえず渡すようにするとか)

ということがあって、なんとかしようとしていたら3つできた。

これまで作っていたものがあんまり複雑なことをやってないのもあり、手元では割と動いていて快適になってきた。

git grep+pecoで絞りこんだ結果をvimで開く時、git grep word | x としたい

はじめは grep-vim word みたいな関数でいいかなと思っていたんだけど、「git grep した後に開きたいファイルがあることが分かる」というようなケースが何度かあって、そうなると、人/設定等によっても違うだろうけども git grep word から grep-vim word とするのに一個履歴を遡ってから C-a -> M-d -> M-d -> x となってちょっと面倒。| -> x でやりたいという気持ちが強まってこういう感じにした。

find-and-open-file() {
  input=$(cat)
  selected=$(echo $input | grep '.\+:[0-9]\+:.*' | peco)
  echo "$selected" | awk -F : '{print "-c " $2 " " $1}' | xargs -o vim 
}
alias fof=find-and-open-file

そもそもこの用途だと「git grep した後に開きたいファイルがあることが分かる」って時点でだいたい開きたいファイルが決まってるから x filename:line_number で開ければいい気がしないでもない

参考

jaws-framework で作ったアプリケーションを MFA によって保護された API アクセスでデプロイする

JAWS/best_practices.md at 6bfd2ca7a00054b750add46a909af3f4a9d4a749 · jaws-framework/JAWS · GitHub によると cloudformation の実行は jaws が生成したテンプレートを多段階の認証を経てAWS Consoleにアクセスし、UI上でペーストして実行すると良いみたいなことが書いてあるが、とてもやりたくないと思う。

かといってアクセスキーで認証できるアカウントに AdministratorAccess をやらないというは確かにそうしたい。

CLIAPI アクセスも MFA で認証する方法があることを知って、上の記事を読むと意外に手間がかからなかった。

  • IamUser: jaws (MFA 有効化 + アクセスキー発行 + いくらか使いたいポリシーをアタッチ)
  • Role: cfn-executer (MFA による認証が済んだ jaws ユーザーにロールを使うのを許可している)

を用意して ~/.aws/credentialsをこのようにした。

[toqoz-jaws]
aws_access_key_id = aaa # jawsユーザーのアクセスキー
aws_secret_access_key = AAA
[toqoz-jaws-cloudformation]
source_profile = toqoz-jaws
role_arn = arn:aws:iam::$ACCOUNT_ID:role/cfn-executer
mfa_serial = $MFA_SERIAL

aws cli とかだと、これでプロファイル toqoz-jaws-cloudformation を使えば勝手に mfa code を聞いてくれるんだけど、jaws dash はそうもいかなかったので、cloudformation の update は aws cli からそれ以外は jawsのコマンドでやることにした

bin/deploy

~/.aws/credentials の default を削除した

もともと default しか無いところに B を追加してこういう形になっていて、B でやりたい時は指定するようにしていた。

[default]
aws_access_key_id = AAA
aws_secret_access_key = aaa
[B]
aws_access_key_id = BBB
aws_secret_access_key = bbb

--profile オプション(もしくは環境変数 AWS_PROFILE)付け忘れてる時に、意図せず A のアカウントで実行されたりしてヒヤッとしたりしたので default を消した。

[A]
aws_access_key_id = AAA
aws_secret_access_key = aaa
[B]
aws_access_key_id = BBB
aws_secret_access_key = bbb

常にどれかを指定しないといけなくなったけど、今のところちょっと面倒以外に問題はないのでまあいいかと思ってる。

AWS Lambdaを利用しスクレイピングしたい

  • なんとか EC2 にインスタンス立てたりすることなく楽に安く済ませたい
  • 適度な速度でHTMLをダウンロードしてきたい

HTML 保存した後はまあ適当にやれば良いと思っていて、ゆっくりダウンロードしてくるところが面倒。

今動いてるやつは 200-300個ぐらいしか URL がないのもあって Timeout を5分に設定した lambda:crawl で setTimeout しながら lambda:download_html を invoke してるんだけど増えてきて5分で終わらなくなったら困るなというのがあって考えたやつ

1つめの方法

  1. 適当な URL が DynamoDB とかに入ってるとして lambda:crawl で Table を Scan して SQS へ {s3_key: "", url: ""} のようなメッセージを送っていく
  2. lambda:consume_download_html_queue を Scheduled Event で5分おきに実行し、適度な速度で5分間メッセージを消費/lambda:download_html の invoke を行う
  3. lambda:download_html で HTML をダウンロードし、S3 に Put する

2つめの方法

SQS について調べるの面倒だなと思っていたら浮かんだやつ。

{tableName: "ダイナモ"} みたいなのを渡されてそっから URL とってきて動きはじめて、いくらか処理してから残ってたら自分に {urls: [処理してないURL]} を渡してinvoke する。

var AWS = require("aws-sdk");
var lambda = new AWS.Lambda();
var dynamoDB = new AWS.DynamoDB();
var s3 = new AWS.S3();

var Promise = require('bluebird');
Promise.promisifyAll(Object.getPrototypeOf(dynamoDB));
Promise.promisifyAll(Object.getPrototypeOf(lambda));

var MAX_FETCH_COUNT = 300;
var FETCH_INTERVAL = 1000;
var DOWNLOAD_HTML_LAMBDA_FUNCTION_NAME = "download-html";

exports.handler = function crawl(event, context) {
  console.log("start to crawl");

  var fetchUrls = event.urls ? Promise.resolve(event.urls) : fetchUrlsFromTable(event.tableName);
  fetchUrls
    .then(function(urls) {
      var fetchCount = Math.min(MAX_FETCH_COUNT, urls.length);
      var urlsToFetch = [];
      for (var i = 0; i < fetchCount; i++) {
        urlsToFetch.push(urls.pop());
      }

      return Promise
        .all(urlsToFetch.map(function(item, index) {
          // downloaded_html/site_a/ 以下にズラっとファイルを並べたいので / を適当に置換しておく
          var s3_key = "downloaded_html/site_a/" + url.replace(/\//g, "$");
          var params = buildLambdaParams(DOWNLOAD_HTML_LAMBDA_FUNCTION_NAME, {
            url: url,
            s3_key: s3_key
          });
          return delayedLambdaInvoke(params, FETCH_INTERVAL * index);
        }))
        .then(function(_) {
          console.log("finish to crawl");

          if (urls.length > 0) {
            var params = buildLambdaParams(process.env.AWS_LAMBDA_FUNCTION_NAME, {
              urls: urls
            });
            return lambda.invokeAsync(params);
          }
        });
    })
    .then(function(_) {
      context.done(null, "OK");
    })
    .catch(function(err) {
      context.done(err);
    });
};

function fetchUrlsDynamoDB(tableName) {
  return dynamoDB
    .scanAsync({
      TableName: tableName
    })
    .then(function(data) {
      var urls = [];
      data.Items.forEach(function(item) {
        urls.push(item.url.S);
      });

      return Promise.resolve(urls);
    });
}

function delayedLambdaInvoke(params, delay) {
  return new Promise(function(resolve, reject) {
    setTimeout(function() {
      console.log("Invoke lambda with " + JSON.stringify(params, null, 2));

      lambda.invokeAsync(params, function(err, data) {
        if (err) {
          reject(err);
        } else {
          resolve(data);
        }
      });
    }, delay);
  });
}

function lambdaInvoke(params) {
  return new Promise(function(resolve, reject) {
    console.log("Invoke lambda with " + JSON.stringify(params, null, 2));

    lambda.invokeAsync(params, function(err, data) {
      if (err) {
        reject(err);
      } else {
        resolve(data);
      }
    });
  });
}

function buildLambdaParams(funcName, payloadObject) {
  return {
    FunctionName: funcName,
    InvocationType: 'Event',
    LogType: 'None',
    Payload: JSON.stringify(payloadObject, null, 2),
  };
}

API Gateway+LambdaでWebサイトを作っている時に404ページを出す最悪な方法

*で/含めてなんでも受けれるみたいな機能があったならもう少し嬉しいんだけど、特にそれっぽいのも見付からなかったので。 /a/b/c/d まではこれでなんとなく動いてるので、このまま放置して忘れたい。

f:id:ToQoz:20151128033415p:plain