技術をかじる猫

適当に気になった技術や言語、思ったこと考えた事など。

WindowsでYeaomanしてみる

表題の通り。
環境は Winows10 …つってもあんま意味ないな。

まずは Node.js から。

といっても Windowsインストーラあるから楽勝だね。
v4.2.6 をインストール。面倒なので PATH もひいてもらう。

C:\test>node -v
v4.2.6

次、Git for Windows
バージョンに特にこだわりはない。

ダウンロードであったのが 2.7.1 なので、それをそのまま使う。

GUI?いらんでしょ。Consoleで動くようにだけしておく。
改行コードは lf だよね。

C:\test>git --version
git version 2.7.1.windows.1

さて

Yeoman, Bower, Grunt 突っ込む

コマンドで

npm install yo bower grunt-cli -g

すると

―――中略
Yeoman Doctor
Running sanity checks on your system

√ Global configuration file is valid
√ NODE_PATH matches the npm root
√ Node.js version
√ No .bowerrc file in home directory
√ No .yo-rc.json file in home directory
× npm version

Your npm version is outdated.

Upgrade to the latest version by running:
npm install -g npm

ゴフゥ…。黙って npm install -g npm して再度インストールをかけて成功。

Let's Yo

シンプルに Bootstrap-Coffee-Less のパッケージでも落とすか。

このへん「Generators | Yeoman」から検索して、こんなものを発見

GitHub - websiddu/generator-bs-less-coffee-webfont: A yeoman generator with boostrap, less, coffee, web fonts(autogenerated font icons) and gh-pages deployment

手始めにこれでいいかなと。

D:\sources>mkdir web_templates

D:\sources>cd web_templates

D:\sources\web_templates>mkdir example_yeaoman

D:\sources\web_templates>cd example_yeaoman

D:\sources\web_templates\example_yeaoman> npm install -g generator-bs-less-coffee-webfont

中略

| | +-- run-async@0.1.0
| | +-- shelljs@0.3.0
| | +-- text-table@0.2.0
| | `-- underscore.string@2.4.0
| `-- yosay@1.1.0
|   +-- ansi-regex@2.0.0
|   +-- pad-component@0.0.1
|   +-- repeating@2.0.0
|   | `-- is-finite@1.0.1
|   +-- string-width@1.0.1
|   | +-- code-point-at@1.0.0
|   | | `-- number-is-nan@1.0.0
|   | `-- is-fullwidth-code-point@1.0.0
|   +-- taketalk@1.0.0
|   | `-- get-stdin@4.0.1
|   `-- word-wrap@1.1.0
`-- UNMET PEER DEPENDENCY generator-karma@>=0.9.0

npm WARN generator-bs-less-coffee-webfont@0.0.6 requires a peer of generator-karma@>=0.9.0 but none was installed.

D:\sources\web_templates\example_yeaoman>

さっくりできたっぽい。

後はなにも考えず

D:\sources\web_templates\example_yeaoman>yo bs-less-coffee-webfont

     _-----_
    |       |    .--------------------------.
    |--(o)--|    |  Superpower is with you  |
   `---------´   | when Yeoman is with you! |
    ( _´U`_ )    '--------------------------'
    /___A___\
     |  ~  |
   __'.___.'__
 ´   `  |° ´ Y `

? What more would you like?
>(*) Bootstrap Javascript files
 ( ) respond.js (mediaquery polyfill for ie6-8)

素敵なおっさんの問いかけに答える。

? What more would you like? Bootstrap Javascript files
? Do you want to deploy your project to Github Pages? This requires an empty Gi
thub repository. No
   create Gruntfile.js
   create package.json
   create .gitignore
   create .gitattributes
   create .bowerrc
   create bower.json
   create .jshintrc
   create .editorconfig
   create app\favicon.ico
   create app\404.html
   create app\robots.txt
   create app\.htaccess
   create app\styles\main.less
   create app\styles\pages\index.less
   create app\styles\utils\icons.less
   create app\index.html
   create app\scripts\hello.coffee
   create app\scripts\main.js
Error bs-less-coffee-webfont

You don't seem to have a generator with the name mocha installed.
You can see available generators with npm search yeoman-generator and then install them with npm install [name].
To see the 1 registered generators run yo with the `--help` option.

D:\sources\web_templates\example_yeaoman>

とテンプレが出来上がる。

npm と bower の読み込み

node_modules はデフォルト空。
これは、package.json に書いてある、依存モジュールをインストールすれば埋まる。

D:\sources\web_templates\example_yeaoman>npm install
npm WARN deprecated lodash@0.9.2: lodash@<3.0.0 is no longer maintained. Upgrade to lodash@^4.0.0
npm WARN deprecated lodash@2.4.2: lodash@<3.0.0 is no longer maintained. Upgrade to lodash@^4.0.0
loadDep:pause -> fetch    \ |#################--------
----中略
    +-- parse-ms@1.0.0
    `-- plur@1.0.0

npm WARN example-yeaoman@0.0.0 No description
npm WARN example-yeaoman@0.0.0 No repository field.
npm WARN example-yeaoman@0.0.0 No license field.

D:\sources\web_templates\example_yeaoman>

途中 Python ねえよとか見た気がしないではないが、先に進む。
名前の通り構成なら Python 何に使うんだろうか…。

まぁ Bower も入れる。

D:\sources\web_templates\example_yeaoman>bower install
bower not-cached    git://github.com/twbs/bootstrap.git#>=3.0.0
bower resolve       git://github.com/twbs/bootstrap.git#>=3.0.0
bower not-cached    git://github.com/Modernizr/Modernizr.git#>=2.6.2
bower resolve       git://github.com/Modernizr/Modernizr.git#>=2.6.2
bower not-cached    git:
--- 中略
jquery#2.2.0 app\bower_components\jquery

modernizr#3.3.1 app\bower_components\modernizr

bootstrap#3.3.6 app\bower_components\bootstrap
└── jquery#2.2.0

D:\sources\web_templates\example_yeaoman>

ほう、IE9 以下お断りですかw

まぁ grunt serve

D:\sources\web_templates\example_yeaoman>  grunt serve
Running "serve" task
Warning: Task "coffee" not found. Use --force to continue.

Aborted due to warnings.


Execution Time (2016-02-08 13:01:47 UTC)
loading tasks  24ms  ████████████████████████████████████ 75%
serve           7ms  ███████████ 22%
Total 32ms

What's !?

coffee なんてタスクねーよと怒られてる。
gruntfile 覗くと、

// 中略
    coffee: {
      dist: {
        files: [{
          expand: true,
          cwd: '<%= yeoman.app %>/scripts',
          src: '{,*/}*.coffee',
          dest: '<%= yeoman.app %>/scripts',
          ext: '.js'
        }]
      }
    },

書かれてるね。で、どこでモジュールロードしてるのかなと思ったら、先頭近くに

  // load all grunt tasks
  require('load-grunt-tasks')(grunt);

これっぽいな。
node_modules の中漁って、index.js 読むと

'use strict';
var path = require('path');
var pkgUp = require('pkg-up');
var multimatch = require('multimatch');
var arrify = require('arrify');
var resolvePkg = require('resolve-pkg');

module.exports = function (grunt, opts) {
    opts = opts || {};

    var pattern = arrify(opts.pattern || ['grunt-*', '@*/grunt-*']);
    var config = opts.config || pkgUp.sync();

// 以下略

node_modules 内から、grunt- で始まるタスクを全部読み込む仕様のようで、改めて探してみると「grunt-contlib-coffee」が無い。
お前が犯人か。

D:\sources\web_templates\example_yeaoman>npm install grunt-contrib-coffee --save-dev

で grunt serve っと

f:id:white-azalea:20160208221451p:plain

ファイル編集するたびに勝手にコンパイルしてくれるのは実に楽。

Scala で解く「プログラマ脳を鍛える数学パズル」

病院行ったついでに待ち時間暇すぎて書店で見かけたやつ。

www.amazon.co.jp

これの問2 1000 - 9999 の中で、「351 , 3 * 51 = 153」(計算結果が元の逆順になる)みたいな数字を見つけ出せ。
使っていいのは四則演算のみで、必ず1回は計算すること。

これ、JavaScript/Ruby/Python だと瞬殺できる。
こいつらには eval があるので、四則演算「+-*/(何もなし)」を適当に埋め込んで eval すればいい。

しかしコンパイル言語でそんなファジーな事は残念ながらできない。
なので、真面目に考えた。

基本的に +-/ を入れた時点で、4桁の結果を出すことが不可能である(厳密には可能だが、組み合わせが存在しない)事を考えると、どこに掛け算の演算子を入れるかの世界になってくる。

演算子を入れれるパターンは、4桁という点で下記 7 パターンしか存在しない。

X XXX (1, 3)
XX XX (2, 2)
XXX X (3, 1)
X X XX (1, 1, 2)
X XX X (1, 2, 1)
XX X X (2, 1, 1)
X X X X (1, 1, 1, 1)

の7通りのみ。

っつー事で、substring で分解して、掛け算走らせればOK

object Main extends App {
  val splitPatterns = Seq(
    Seq(1, 3), Seq(2, 2), Seq(3, 1), 
    Seq(1, 1, 2), Seq(1, 2, 1), Seq(2, 1, 1), 
    Seq(1, 1, 1, 1)
  )

  def split(s: String, p: Seq[Int]): Seq[String] =
    s.substring(0, p.head) +: (if (p.tail.isEmpty) Seq.empty else split(s.substring(p.head), p.tail))

  (1000 to 9999).foreach(v => {
    splitPatterns.foreach(p => {
      val origin = v.toString
      val seq = split(origin, p).map(_.toInt)
      if(seq.product == origin.reverse.toInt) {
        println(s"Found Answer: $v ($seq)")
      }
    })
  })
}

結果

[info] Running Main 
Found Answer: 5931 (List(5, 9, 31))
[success] Total time: 4 s, completed 2016/01/17 19:10:21

最初に掛け算以外考えなくて済むというところに気づくと、上記みたいな解になるが、後から四足演算を計算しようと考えた時に、下記みたいな配列作って、計算で再起すべきかとか考えてしまった…。
何かスッキリ実装できる方法がないのかと思わなくはない。

  val operatorFunc: Seq[Function2[Int, Int, Int]] = Seq(
    (a, b) => a * b, (a, b) => a - b, (a, b) => a / b
  )

split を tail call にするのもまだ慣れていないせいかなかなか上手く行ってない…。
別に深くなるわけでもないので、気にしなかったのだが。

というか、他の言語は「逆ポーランド記法使う」ってそれむしろどうやるのかわからなかった…。

JavaScript(CoffeeScript) で似非 Ctrl + F 作ってみた。

まじかと思ったんだけど、マジだったので驚いてる。

shertan.me

ちょっと待て基本だろ常考
こんなもん、Windows XP 初期の時代からデフォルトだぜ?

Keyboard Shortcuts for Internet Explorer 6

記憶が確かなら、Windows 98 時代からあった気もする。

まぁ、言ってもしょうがない。
仕方ないので、それっぽいものを作ってみた。

github.com

f:id:white-azalea:20151025153314p:plain

いつも通り、Play2.4 なので、Java8 入ってれば動くはず。

続きを読む

Play2.4 でネストしたJsonをバインドする

何で今更かというと、これつかえねーかなと思ったから。

最初は、Forms でのバインドをしようとしたのだけど、早速フォーラムが見つかりました。
キーワードは Play recursive form

https://groups.google.com/forum/#!topic/play-framework/Ya1hPmR2QtY

この中で、「Form はそもそもHttpRequestを前提にしてんだからそんな事すんじゃねぇ」と言われていて、「Play Json 使え」とも誰か言ってた。
そこで、キーワード変更で、「Play recursive json」でググったところ、

ScalaJsonCombinators

を発見。

2.4 になってかなり使いやすくなってきてたので、これでざっくり実装してみた。

import play.api.libs.json._ // JSON library
import play.api.libs.json.Reads._ // Custom validation helpers
import play.api.libs.functional.syntax._ // Combinator syntax


case class Nested(name: String, content:String, friends: Seq[Nested])

implicit lazy val userReads: Reads[Nested] = (
  (__ \ "name").read[String] and
  (__ \ "content").read[String] and
  (__ \ "friends").lazyRead(Reads.seq[Nested](userReads))
)(Nested)

val json = Json.parse("""{"name":"Example", "content":"content", "friends":[{"name":"ChildName", "content":"childContent", "friends":[]}]}""")
val result = json.validate[Nested].asOpt
println(result)

して結果が

Some(Nested(Example,content,List(Nested(ChildName,childContent,List()))))

わーい。
Json で設定ファイルとかできるでw

フォームヘルパーをタグ調整する(Play 2.4.x) with マルチセレクトチェックボックス

Playframework 2.4 において、フィールドコンストラクタの仕様も変わったので、対応したチェックボックスを作ってみる。

Custom Field Constructors

これがなんなのかというと、input とかの helpers の外枠の事。
いつぞや Bootstrap 対応した フォームヘルパーをタグ調整する(Play 2.3.x) - 謎言語使いの徒然 を焼き直す。

Bootstrap インストール

まずは、いろいろやるのが面倒なので、build.sbt に下記を用意する。

libraryDependencies ++= Seq(
  "com.github.aselab" %% "scala-activerecord" % "0.3.1",
  "com.h2database" % "h2" % "1.4.185" % "test",
  "mysql" % "mysql-connector-java" % "5.1.34",

  // この下 3 行を追加
  "org.webjars" %% "webjars-play" % "2.4.0-1",
  "org.webjars" % "jquery" % "1.11.3",
  "org.webjars" % "bootstrap" % "3.3.5",
  evolutions,
  jdbc,
  cache,
  ws,
  specs2 % Test
)

// あとから書くのも面倒なので追加。
TwirlKeys.templateImports += "views.html.commons._"

そしたら、conf/routes に下記を追記

GET     /webjars/*file                    controllers.WebJarAssets.at(file)

app/view/main.scala.html も Bootstrap 取り込み。

@(title: String)(content: Html)

<!DOCTYPE html>

<html lang="en">
    <head>
        <title>@title</title>
        <link rel="stylesheet" media="screen" href="@routes.Assets.versioned("stylesheets/main.css")">
        <link rel="shortcut icon" type="image/png" href="@routes.Assets.versioned("images/favicon.png")">
        <!-- custom for bootstrap -->
        <link rel='stylesheet' href='@routes.WebJarAssets.at(WebJarAssets.locate("css/bootstrap.min.css"))'>
        <script type='text/javascript' src='@routes.WebJarAssets.at(WebJarAssets.locate("jquery.min.js"))'></script>
        <script type='text/javascript' src='@routes.WebJarAssets.at(WebJarAssets.locate("js/bootstrap.min.js"))'></script>
    </head>
    <body>
        <!-- Wrap bootstrap container -->
        <div class="container-fluid">@content</div>
    </body>
</html>

跡はTodoリストでも勝手に書いて。
ちなみに、Bootstrap の入力フォームは、各 input タグに class="form-control" 入れないとデザイン整わないので注意。

カスタムフィールドコンストラクタの作成

と言っても言うほどの事もないか?
app/views/commons/bootstrapFields.scala.html 作って、下記を置く。

@(elements: helper.FieldElements)
<div class="form-group @if(elements.hasErrors){has-error}">
    <label for="@elements.id" class="col-sm-2 control-label">@elements.label</label>
    <div class="col-sm-10">
        @elements.input
    </div>
    @if(!elements.infos.isEmpty){
    <div class="col-sm-10 col-sm-offset-2">
    <span class="help-inline">
      @for(info <- elements.infos){
      <span class="label label-warning">@info</span>
      }
    </span>
    </div>
    }

    @if(elements.hasErrors) {
    <div class="col-sm-12">
        <div class="alert alert-danger">
            <ul>
                @for(msg <- elements.errors){
                <li>@msg</li>
                }
            </ul>
        </div>
    </div>
    }
</div>

そして、パッケージオブジェクトを作成する。

package views.html

package object commons {
  import views.html.helper.FieldConstructor
  implicit val bootStrapFieldConstructor = FieldConstructor(bootstrapFields.f)
}

import 設定は、build.sbt に入れてあるので、後は普通に helpers.input とか使うだけ。

マルチセレクトチェックボックス

デフォルトのヘルパーにいないので、頑張って作ってみた。
え?頑張ってないじゃないかって?実は問題が一つあるんだけど、後で話そう。

@(field: play.api.data.Field, keyValues: Map[String, String])(implicit messages: play.api.i18n.Messages)

@helper.input(field) { (id, name, value, htmlArgs) =>
    @defining(keyValues.toList.zipWithIndex){ results =>
        @for(kv_i <- results){
        <label class="checkbox-inline">
            <input id="@{id}_@{kv_i._2}" name="@{name}[@{kv_i._2}]" type="checkbox"
                   value="@{kv_i._1._2}" @if(value.exists(v => {keyValues.exists(_._2 == v)})){selected}> @kv_i._1._1
        </label>
        }
    }
}

使い方的には、フォームを下記の定義にして

  val checkBoxes = {
    import play.api.data.Forms._
    Form(single("multiSelect" -> list(longNumber)))
  }

テンプレート上でこう

@multipleCheckBox(checkBoxes("multiSelect"), Map("first" -> "1", "second" -> "2", "third" -> "3"))

そして、問題はなんなのかというと、バインドしたフォームを指定してもチェックが付かないという事。
解決しようと 3 時間くらい粘ったけど、結局未解決。

知ってる方は是非 scala - Playframework2.4 の Form で list(number) をバインドした時のフィールドコンストラクタ挙動で、value 値が取得できない。 - スタック・オーバーフロー

追記。

上記リンク先で、無事解決しました。

kawty さんありがとうございます。

Mac で ansible 基礎設定やってみた。

Ansible のことをちまちまやったので、その件メモ。
ネカフェでやってるんだけど、近くのネカフェは速度が遅すぎてこれ以上はちょっと厳しかった。

Install

と言ってもこれだけ。

$ brew install ansible

ちなみにバージョンは

$ ansible --version
ansible 1.9.2
  configured module search path = None

OSS でよくある話だけど、後方互換ってなくなるから…

セットアップ先環境

テストだから Vagrant で環境作った。
自分の環境は 1.7.1 で、バージョンアップしたらコマンド変わってるかもわからん。
あと、VirtualBox4.3.30 とちょっと古め。新しいやつ推奨かな。
環境もかなり適当に Cent6 を A list of base boxes for Vagrant - Vagrantbox.es から拾ってきた。

vagrant box add cent6.6 [https://github.com/2creatives/vagrant-centos/releases/download/v6.5.3/centos65-x86_64-20140116.box]
vagrant init centos6.6

成功したら、Vagrantfile というファイルが出来上がるので、ssh 用の設定を追加。

  # Create a forwarded port mapping which allows access to a specific port
  # within the machine from a port on the host machine. In the example below,
  # accessing "localhost:8080" will access port 80 on the guest machine.
  # config.vm.network "forwarded_port", guest: 80, host: 8080

  # Create a private network, which allows host-only access to the machine
  # using a specific IP.
  # ここの設定を有効化。サンプルでは下記のIP
  config.vm.network "private_network", ip: "192.168.33.10"

最後に vagrant up で起動する。

Ansible hosts 設定

ansible は基本的に専用の hosts ファイルで接続先を設定する。
mac で、Homebrew 経由の場合、/usr/local/etc/ansible/hosts に書いてある。

なんか指定しないと怒られる雰囲気だったので、下記のように指定。

[servers]
192.168.33.10

ここまできたら、まずは vagrant up を実行し、起動後に vagrant ssh-config --host vagrant >> ~/.ssh/configssh 設定を設定ファイルに流し込んでおく。
その後、~/.ssh/config を開くと、下記のような設定ができている。

Host vagrant
  HostName 127.0.0.1
  User vagrant
  Port 2222
  UserKnownHostsFile /dev/null
  StrictHostKeyChecking no
  PasswordAuthentication no
  IdentityFile /Users/hoge/Documents/projects/play_2.4_activerecord/ansibles/.vagrant/machines/default/virtualbox/private_key
  IdentitiesOnly yes
  LogLevel FATAL

重要なのは「IdentityFile」で、これを元に、下記のような定義を作っておく。

Host 192.168.33.*
  IdentityFile /Users/hoge/Documents/projects/play_2.4_activerecord/ansibles/.vagrant/machines/default/virtualbox/private_key
  User vagrant
  PasswordAuthentication no

これで設定完了。
一応 ssh 192.168.33.10 が通るかだけ確認しておくと良い。

続きを読む