skinny事始め
scalaのフレームワークで簡単にとっつきやすそうのを探したらskinnyが良さそうだったので、触り始めた際のメモを残しておきたいと思います。
skinnyインストール(mac)
プロジェクト作成から起動
$ skinny new プロジェクト名 $ cd プロジェクト名 $ ./skinny run
ブラウザから'http://localhost:8080/‘にアクセスしてページが表示されることを確認
ビルド
jetty内臓のjar生成
$ ./skinny package:standalone
デフォルト設定ではstandalone-build/target/scala-x.xxに実行可能jarが出力されることを確認済み
起動
$ ./skinny run
起動スクリプトにてjettyをcontainer:restartしているのを確認
デバッグ
$ ./skinny debug
sbt debugで5005ポート指定でjettyを起動しておりideaから5005ポート指定でリモートでバックできるのを確認。
開発
コントローラー
package controller import skinny._ class RootController extends ApplicationController { def index = { set("name", "jjug") render("/root/index") } }
set(“name”, “jjug”)の部分でリクエストスコープにkey=nameでjjugをセットしている。renderではsspのページを指定している。sspはscalateのテンプレートエンジンで以下を参考にする。 http://scalate.github.io/scalate/documentation/ssp-reference.html#syntax
<%@val name: String%> <h3>Hello ${name}!</h3> <hr/> <p> Your first Skinny app works! </p>
上記のようなsspであればリクエストスコープせセットしたnameがApplicationControllerで指定したjjugに置換して表示される。
ルート
skinnyでURLからメソッドを呼び出す場合、ApplicationControllerを拡張したクラス内にメソッドを追加しておく。
class RootController extends ApplicationController { def index = { set("name", "jjug") render("/root/index") } def ssp_test = { set("name", "jjug") render("/root/ssp_test") } }
それから、サーブレットコンテキストに紐付ける。
package controller import skinny._ import skinny.controller.AssetsController object Controllers { def mount(ctx: ServletContext): Unit = { root.mount(ctx) AssetsController.mount(ctx) } object root extends RootController with Routes { val indexUrl = get("/")(index).as('index) val sspTestUrl = get("/ssp_test/?")(ssp_test)as('ssp_test) } }
上記の例ではURLとメソッドをマッピングさせたrootオブジェクトをroot.mount(ctx)でサーブレットのコンテキストに追加している。get(“/ssp_test/?”)(ssp_test)as(‘ssp_test)の最初のssp_testの方はメソッドの実態でas('ssp_test)の部分はbeforeAction,afterActionで必要になるようで詳しくは以下を参照する。 http://skinny-framework.org/documentation/controller-and-routes.html
Jsonレスポンスを返す
spray-josnでjson形式のレスポンスを返してみる。一応skinny自身のutilにもjsonのパーサーは入っているのだが、とりあえずspray-jsonの方で試してみる。 まずbuild.sbtに以下を追記する
libraryDependencies += "io.spray" %% "spray-json" % "1.3.1"
それから暗黙の型変換で戻り値の形式を指定した上で以下のようにjsonレスポンスを返してみる。
class RootController extends ApplicationController { implicit object AnyJsonFormat extends JsonFormat[Any] { def write(x: Any) = x match { case n: Int => JsNumber(n) case s: String => JsString(s) case b: Boolean if b == true => JsTrue case b: Boolean if b == false => JsFalse } def read(value: JsValue) = value match { case JsNumber(n) => n.intValue() case JsString(s) => s case JsTrue => true case JsFalse => false } } def index = { set("name", "jjug") render("/root/index") } def ssp_test = { contentType = formats("json") val map = Map("japan" -> "tokyo", "america" -> "chicago", "denmark" -> "kopenhagen") map.toJson } }
vue.js使用時にvue-resourceでhttpリクエストを投げてみる
vue-resourceでhttpリクエストを投げてみた
公式では以下のようにvue-resourceは公式推奨からは外すけど、ajaxはVue自身が管理すべき領域でないからということで、使うべきではないとかそういった意味合いではないようだ。
https://jp.vuejs.org/2016/11/03/retiring-vue-resource/
インストール
$ npm install vue-resource
試してみる
jsファイル内に以下の追記を行いvue-resourceを有効化しておく
import VueResource from 'vue-resource' Vue.use(VueResource)
それからgetリクエストを投げる場合は、以下のようになる。
this.$http.get(リクエストURL').then( response => { console.info(response.body) }, response => { //エラー時の処理 }) }
簡単に動作確認はできた。
Vueの公式ではhttpリクエスト周りは自分で好きなの選んでということだったが、とりあえずはvue-resourceを使っとけば良いのかと思います。
vue.jsにVue Materialを適用してみた
Vue Material(https://vuematerial.github.io/#/)を使用してマテリアルデザインを適用した際のメモ
インストール
公式の手順に従えば簡単に使えるようになる。
https://vuematerial.github.io/#/getting-started
$ npm install vue-material --save $ yarn add vue-material
vue-materialはyarnでインストールするので、まだ入っていない場合は"npm install -g yarn"でyarnを実行できるようにしておく。
インストールできたらmain.jsなど最初の方で読み込まれるjs内で以下を実行してVue Materialのコンポーネントを使えるようにしておく。
import VueMaterial from 'vue-material' Vue.use(VueMaterial)
ただこれだけだとコンポーネントに対してcssが適用されないので、以下を追記してnode_module内のcssを読み込ませておく。
require('vue-material/dist/vue-material.css') または import 'vue-material/dist/vue-material.css'
使ってみる
Vue Materialを使う準備はできたので実際に使ってみる。試しに以下のようなコンポーネントを準備した。
<template> <div id="header"> <md-toolbar> <h1 class="md-title">アプリケーションタイトル</h1> <md-button class="md-raised" v-on:click="greet(event)">実行</md-button> </md-toolbar> </div> </template> <script> export default { data () { return { } }, methods: { greet: function (event) { alert('Hello !') } } } </script> <style scoped> .md-raised { margin-left: 30px; } </style>
実行してみるとマテリアルデザインが適用されたボタンが表示されたはずである。ボタンを押したらgreetメソッドが実行されると思ったのだが反応がなかった。 これについては公式にドキュメント(https://jp.vuejs.org/v2/guide/components.html)がありまして、どうやらカスタムコンポーネントのルート要素 にネイティブイベントをバインディングする場合.nativeを追記する必要があるようです。 それを踏まえて以下の修正を加えるとボタンクリックでメソッドが実行できることを確認できました。
<md-button class="md-raised" v-on:click="greet(event)">実行</md-button> ↓ <md-button class="md-raised" v-on:click.native="greet(event)">実行</md-button>
vue.jsを触ってみた
vue.jsとは
公式ページを引用すると以下のようになっています。
Vue (発音は / v j u ː / 、 view と同様)はユーザーインターフェイスを構築するための プログレッシブフレームワークです。他の一枚板(モノリシック: monolithic)なフレームワークとは 異なり、Vue は初めから少しづつ適用していけるように設計されています。中核となるライブラリは view 層だけに焦点を当てているため、Vue.js を使い始めたり、他のライブラリや既存のプロジェクト に統合したりすることはとても簡単です。一方、モダンなツールやサポートライブラリと併せて利用 することで、洗練されたシングルページアプリケーションを開発することも可能です。
印象
reactの開発を行った際は始める際にgulpやwebpackなど開発用のツールの知識を事前に学んだりする必要があったのですが、vueではvue-cliでテンプレートのプロジェクトから開始できるのがすごい便利でした(reactについても現在ではテンプレートプロジェクト作成ができるようだ"https://facebook.github.io/react/blog/2016/07/22/create-apps-with-no-configuration.html")。一応fluxぽく実装することもできるようですが、それをやるならreactで良さそうな印象。fluxを使うまでもない単純なwebアプリとか作るのであればvue.jsを選べば良さそうに思います。
vue-cliによるプロジェクトの作成
ではvue-cliを使ってvue-cliのテンプレートプロジェクトを作ってみたいと思います。
インストール
$ npm install -g vue-cli
プロジェクト作成
$ vue init テンプレート名 プロジェクト名
vue-cliで使えるテンプレートは以下で確認できる。
https://github.com/vuejs-templates
起動
$ cd プロジェクト名
$ npm install
$ npm run dev
package.jsonを確認してみると"npm run dev"で"node build/dev-server.js"を実行していることがわかります。"webpack-dev-server"のタスクを直接実行するのではなくnode起動時にwebpackを実行するようにしているようです。初期状態でautOpenBroserが有効になっているのでnpm run dev実行で自動でブラウザが開くようになっています。
ビルド
$ npm run build
jsファイルのビルドが行われます。その際の設定などはだいたいconfig/index.jsにまとめられているようです。ビルド後のbase urlを変更したい場合はbuild.assetPublicPathを変更したら良いみたいです。
// see http://vuejs-templates.github.io/webpack for documentation. var path = require('path') module.exports = { build: { env: require('./prod.env'), index: path.resolve(__dirname, '../dist/index.html'), assetsRoot: path.resolve(__dirname, '../dist'), assetsSubDirectory: 'static', assetsPublicPath: '/', productionSourceMap: true, // Gzip off by default as many popular static hosts such as // Surge or Netlify already gzip all static assets for you. // Before setting to `true`, make sure to: // npm install --save-dev compression-webpack-plugin productionGzip: false, productionGzipExtensions: ['js', 'css'], // Run the build command with an extra argument to // View the bundle analyzer report after build finishes: // `npm run build --report` // Set to `true` or `false` to always turn it on or off bundleAnalyzerReport: process.env.npm_config_report }, dev: { env: require('./dev.env'), port: 8080, autoOpenBrowser: true, assetsSubDirectory: 'static', assetsPublicPath: '/', proxyTable: {}, // CSS Sourcemaps off by default because relative paths are "buggy" // with this option, according to the CSS-Loader README // (https://github.com/webpack/css-loader#sourcemaps) // In our experience, they generally work as expected, // just be aware of this issue when enabling this option. cssSourceMap: false } }
試しにコンポーネントを追加してみる
vueでの開発の流れを把握するためコンポーネントを追加してみる。
まずwebpack起動時にアクセスするページについてだが、これはindex.htmlになる。
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>my-project</title> </head> <body> <div id="app"></div> <!-- built files will be auto injected --> </body> </html>
ただindex.html内にはjavascriptを読み込む部分がないのだが、これはnode側がjavascriptを自動で返すようにしているためであり、 該当する設定はbuild/webpack.base.config.jsの以下の箇所になる。
module.exports = { entry: { app: './src/main.js' },
これにより、node側がindex.htmlとともに読み込むjsファイルをまとめて返すようにしている。
それから、main.js内でルータを読み込んでいるのがわかるので対象のルータ(source/router/index.js)を確認して見る。
import Vue from 'vue' import Router from 'vue-router' import Hello from '@/components/Hello' Vue.use(Router) export default new Router({ routes: [ { path: '/', name: 'Hello', component: Hello } ] })
今回は最初に読み込むコンポーネントを追加したいと思うので、ここのルータに手を加える。とりあえず以下のように修正して見る。
import Vue from 'vue' import Router from 'vue-router' import Hello from '@/components/Hello' import myApp from '@/components/myApp' Vue.use(Router) export default new Router({ routes: [ { path: '/', name: 'myApp', component: myApp }, { path: '/hello', name: 'Hello', component: Hello } ] })
これで"http://localhost:8080/“でアクセスした際にmyAppコンポーネントが表示されるようになるので、今度はそのmyAppコンポーネントを以下のように定義しておく。
src/components/myApp.js
<template> <div > {{msg}} </div> </template> <script> export default { name: 'myApp', data () { return { msg: 'Welcome to my first app' } } } </script> <style scoped> h1, h2 { font-weight: normal; } ul { list-style-type: none; padding: 0; } li { display: inline-block; margin: 0 10px; } a { color: #42b983; } </style>
これでnodeを再起動したら自作したmyAppコンポーネントが表示されるはずである。初期状態だとvueのロゴが表示されていると思うがこれはmain.jsが最初に読み込んでいるコンポーネントであるApp.vue内で指定されているものなので、消す場合はApp.vueを修正したら良い。
公式ページのドキュメントが豊富なのであとはこれを参考にしたら開発を進めていけると思われます。
すでに公開されていて良さそうなコンポーネントは以下で探すことができる。
マテリアルデザインを適用するのであれば、Vue MaterialかVue MDL Documentationのあたりが良さそうな気がします。 Vue Materialの方はインストール時にcssが付いてきてそれをimportして使えば良いので自分はそっちの方が楽で使ってたりします。
WSUS構築メモ
WSUSインストール
- 更新プログラムを保存するためのフォルダを作っておく
- サーバマネージャの"役割と機能の追加"からWSUSを指定
- 役割とサービスでデータベースにWIDを選択
- 更新プログラムの保存先に事前に用意したフォルダを指定
WSUS設定
- WSUSの同期スケジュールを設定 日本では深夜2-3時くらいに更新プログラムの配信が行われるから深夜4時とかに同期するのが多そう
1回のアップデートでダウンロードに失敗する場合は同期回数を増やしておく
ドメイングループポリシーでWSUS管理対象にする
ドメイングループポリシーを変更してWSUSの管理対象にする
1. ドメインコントローラーで"グループポリシーの管理"を実行
2. WSUS管理対象のOUに以下のグループポリシーオブジェクトを指定
コンピューターの管理-管理用テンプレート-Windowsコンポーネント-WindowsUpdateを表示
“自動更新を構成する"を"有効"にし自動更新の構成を"3-自動ダウンロードしインストールを通知"に変更する
"イントラネットのMicrosoft更新サービスの場所を指定する"を有効にするそれからWSUSサーバの場所を指定(ポートはデフォルトだと8530)
3. "ActiveDirectoryユーザとコンピュータ"でWSUS管理対象のOUに管理対象のコンピュータを移する
クライアント端末のグループポリシーでWSUS管理対象にする
クライアント端末のグループポリシーでもWSUS管理対象に変更できる
更新プログラムの自動承認
- WSUS管理コンソールのオプション-自動承認をクリック
- 自動承認対象のグループ、承認対象の分類を選ぶ等する
更新プログラムの手動承認
更新プログラムのインストール、削除ともに承認を行うことができる 1. WSUS管理コンソールの更新プログラムをクリック 2. 対象のプログラムを選択して承認をクリック 3. インストールまたは削除を指定して承認実行
その他
重くなったらインデックスの構築やキャッシュ削除、メモリ設定とかの調整は必要になりそう
WSUSに困らせられた記録
WSUS管理コンソールに繋がらなくなった
症状
WSUS管理コンソールに繋がらないということで確認したところ"予期しないパケット形式のためハンドシェイクに失敗しました"とのエラー表示で繋がらない
Microsoftのフォーラムに同様の書き込みがあり、どうやら最近WindowsUpdateを行った際にインストールされたKB3159706が原因でWSUSの管理コンソールに繋がらなくなったようだ。
→OS再起動だけではWSUSの管理コンソールに繋がらない、KB3159706アンインストール後はWSUS管理コンソールにつながるようになったことを確認
KB3159706はwindows10がupdateを行う際に必要になるのでインストールが必須になるようなので以下のWSUSサポートチームのブログを参照にインストール対応を実施
https://blogs.technet.microsoft.com/jpwsus/2016/05/26/kb3159706/
とりあえず無事にインストールできたからよかったけど、これはWindowsの不具合ではないのか
WSUS管理コンソールで削除した端末を再度管理対象にできない
症状 WSUS管理コンソールから端末削除後、グループポリシーを編集し再度WSUS管理対象としたが管理コンソールに表示されなくなった
クライアント端末のローカルグループポリシーでアップデートサーバにWSUSを指定していたのだが、ドメインコントローラのグループポリシーでアップデートさーばをWSUSに指定できるか確認するた以下の手順の操作を行った。
1. ローカルグループポリシー変更してWindows Update関連の設定をみ構成に変更
2. WSUS管理コンソールで対象端末を削除
3. ドメインコントローラーのポリシー変更して対象端末がupdate時にWSUSを見にいくように変更
これでWSUSの管理コンソールに端末に対象端末が表示されると思ったけど、表示されない。で、確認したところ以下のページにレジストリ消せな直るとの情報が
https://support.microsoft.com/ja-jp/help/903262/a-windows-2000-based,-windows-server-2003-based,-or-windows-xp-based-computer-that-was-set-up-by-using-a-windows-2000,-windows-server-2003,-or-windows-xp-image-does-not-appear-in-the-wsus-console
で対応してちょっと待ったら治った
グループポリシーとWSUSの管理対象端末が同期できない作りになっているんだろーなーと思いました。ドメインコントローラの端末で管理対象端末に適用されているグループポリシー情報を管理できたら簡単そうな気もするんですがそうはいかないのだろう。せめてWSUS管理コンソールからの削除時間と端末に適用されるグループポリシーの変更時間からとかうまく整合性とってもらいたいとか思ったりする。
Scalaで新しく制御構造を作成する
Scalaで新しい制御構造を自作する
Scalaは関数型言語にあるため関数が第一級のオブジェクトとなります。そのためJavaなどのオブジェクト指向言語と比べてどんな違いがあるかというと、関数を引数にして別の関数を呼び出すことができます。俗に言う高階関数というやつです。
試しに用意した関数を引数として受け取るサンプルは以下のようになります。
def listFunc(lines: List[String])(op:String => Boolean) = { for { line <- lines if op(line) } yield line + "\n" }
(op:String => Boolean) の部分がStringを引数で受け取りBooleanを受け取る関数を引数で使うように定義している。(op:String => Boolean)の部分は(op: Function[String, Boolean])と書き換えることもできます。それから関数を引数として渡している部分は以下のようになります。
def grepText(fileName: String, text: String): String = { val lines = Source.fromFile(fileName).getLines().toList val result = listFunc(lines) { line => line.contains(text) } result.mkString }
高階関数を利用することで処理のみを切り替えるだけということができます。上記の例ではline.contains(text)の部分が異なる関数を利用することで制御の構造はそのままで処理を切り替えることができます。また同一の処理を別の部分でも再利用が行えるためコードの再利用性はjavaなどに比べて大分上がると思います。
参考までに今回用意した高階関数を利用したgrepコマンドの全体像は以下のようになります。
import scala.io.Source /** * * @param file ファイル名 * @param text 検索対象テキスト * @param lineNum 行番号を表示するかどうか */ case class SGrepArgs(file:String, text: String, lineNum: Boolean ) object SGrep { def parseArgs(args: List[String]): Option[SGrepArgs] = { var file: String = "" var text: String = "" var lineNum:Boolean = false def parseArgsHelper(args: List[String]): Unit = { args match { case "-n" :: x ::rest => { lineNum = if(x equals "true"){true} else{false} parseArgsHelper(rest) } case x :: y :: rest => { file = x text = y parseArgsHelper(rest) } case _ => {} } } parseArgsHelper(args) Some(SGrepArgs(file, text, lineNum)) } def main(args:Array[String]): Unit ={ val sGrepArgs = parseArgs(args.toList) sGrepArgs match { case Some(x) => grep(x) case _ => {} } } def grep(sGrepArgs: SGrepArgs) = { println(grepText(sGrepArgs.file, sGrepArgs.text)) } def grepText(fileName: String, text: String): String = { val lines = Source.fromFile(fileName).getLines().toList val result = listFunc(lines) { line => line.contains(text) } result.mkString } def listFunc(lines: List[String])(op:String => Boolean) = { for { line <- lines if op(line) } yield line + "\n" } }