VSTO(Excel)でアクティブなセルが含まれている範囲を選択する

リボンに追加したボタンを押した際、アクティブセルの含まれる範囲を選択状態にする。

using Excel = Microsoft.Office.Interop.Excel;

// 中略
private void button_Click(object sender, RibbonControlEventArgs e)
        {
             Excel.Window window = e.Control.Context;
             Excel.Range activeCell = window.Application.ActiveCell;
             Excel.Range currentRegion = activeCell.CurrentRegion;
             currentRegion.Select();
         }

Play2+Scalaでのバリデーションについて:verifyingを一度に複数利用する際の注意

verifyingを利用する上で注意すべきこと

先日verifyingを一度に複数利用できることを紹介しました。
記事を作成した時点では気がついていなかったのですが以下の様な場合、注意が必要です。

  val form = (id:String) => Form(
    tuple(
      "old" -> nonEmptyText,
      "new" -> nonEmptyText,
      "confirm" -> nonEmptyText
    ) verifying("新しいパスワードと確認用パスワードが一致していません", result => result match {
      case (old,newPass,confirm) => newPass == confirm
    }) verifying("古いパスワードと新しいパスワードが同じです",result => result match {
      case (old,newPass,confirm) => old != newPass
    }) verifying ("パスワードの更新に失敗しました",result => result match {
      case (old,newPass,confirm) => User.updatePassword(id,old,newPass)
    })
  )   

この例ではユーザーのパスワードの更新を行おうとしています。
ただしこの例では期待通りの動作はしません

もしこのままであった場合、3つの要素に対するバリデーションのどこかでエラーがあった場合でもパスワードが更新されてしまいます

つまりどこかでエラーがあった場合でもエラーがあった場所で評価をやめるわけではなく、すべて評価を行うというわけです。
この問題を改善すると以下のようになります。

  val form = (id:String) => Form(
    tuple(
      "old" -> nonEmptyText,
      "new" -> nonEmptyText,
      "confirm" -> nonEmptyText
    ) verifying("新しいパスワードと確認用パスワードが一致していません", result => result match {
      case (old,newPass,confirm) => newPass == confirm
    }) verifying("古いパスワードと新しいパスワードが同じです",result => result match {
      case (old,newPass,confirm) => old != newPass
    }) verifying ("パスワードの更新に失敗しました",result => result match {
      case (old,newPass,confirm) if( old != newPass && newPass == confirm) =>
        User.updatePassword(id,old,newPass)
      case (old,newPass,confirm) => false
    })
  )   

これで期待通りの動作をするようになります。
ただこれだと最後のバリデーションがあまりかっこよく無いのが…

Play2+Scalaでのバリデーション

Play2+Scalaのバリデーションでいろいろとハマったのでメモ

やりたかったこと

  • 一つのinput要素に複数のバリデーションとエラーメッセージを設定する
  • エラーメッセージを任意の場所に表示する
続きを読む

Scala Conference 2013/Scala Hack-a-thonに参加して来ました

Scala Conference 2013

ScalaConference

ScalaConference2013では以下のプログラムを聞かせて頂きました。

  • Up up and Out: Scaling Software with Akka
  • Akka を用いたデータストリーム処理・解析プラットフォーム
  • BRMS開発でのScala利用事例
  • ランチタイムLT大会
  • リクルーティングセッション
  • Effective Actors
  • オブジェクト指向から視る "Better Java" としての Scala
  • 使ってわかったScalaのここがダメ!Play2によるシステム開発事例
  • Play Framework - The modern web framework that packs a punch

技術的なことなどは他の方がまとめてくださると思うので参加者が少なかったリクルーティングセッションの話をメインに
箇条書きで少々見づらいかもしれませんがご勘弁を…

株式会社アドヴァンストソフトエンジニアリング様
  • 仕事は東京メイン
  • 受託開発
  • 完全プロダクト製
  • この18年と黒字経営と実質的な無借金経営
  • ITSS平均3.2
  • 主要な取引先はNEC
  • Scalaなどを使い始めたのはここ二年ほど
  • ScalaActiveRecordの開発を行なっている
株式会社ドワンゴ
  • リストラをしているのでは?との噂があったがそれは誤解
  • 期間雇用していた方の多くは雇用の終了
  • 現在はほぼ100%社員の内製
  • 以前はエンジニアの働きやすい会社というのが少なかったが最近は増えてきている
  • 現在技術的負債が溜まってしまっているせいでちょっと困ってる
  • BtoCという形の上ではしょうがなく負債が溜まってしまうことがあった
  • 技術的負債生まれたのであればちゃんと解消する
  • そう入っても仕事はいっちゃうんじゃないの?
    • そういう時はサポート室が間に割って入って負債を返済させる
  • コードがかけることが第一
  • 選考の際にはGithubを持っているとなおよし
エムスリー株式会社様
  • 日本国内の医師の80%が加入しているポータルサイトを運営している
  • インターネットを活用しより健康的で経済的な社会へ
  • GithubにScala関連のライブラリを提供
  • Play2.0などを利用
株式会社ENRAPT様
  • 新しい技術を吸収する
  • スタートアップ企業
  • SIではあるが保守やメンテナンスについてはやっていない
  • ツールなどでScalaを採用
  • お客様に納品するものにScalaを使うのはまだ(業務内容的に)難しい
  • Github,WASなどのクラウドサービスを利用している
  • 在宅勤務を可能
ファンコミュニケーションズ株式会社様
  • A8.net,Moba8.net
  • Scalaを使い始めたのはつい最近の話
  • 以前作成したものをScalaで作り替えている
  • 月1万円本や、セミナーに利用可能
  • 就業時間中でも普通にセミナーなどに出ていける
  • 面接は3回
    • なんとScalaConferenseから来たというだけで一次面接を免除!
Demand Side Science 株式会社様
  • DSPを作っている
  • 2013/01から活動開始
  • 技術者は社長を含めて9名
  • DSPとは端的に言ってディスプレイ広告を出したい人向けの広告
  • 大規模な高奥主や広告代理店向け
  • DSPが目指すもの
    • でかい
    • はやい
    • つよい
  • Scalaに決めたのは面白そうだったから
    • LLに比べてパフォーマンスが期待できる
  • Scala経験不問でデマンドサイドをサイエンスしていける仲間を募集中

このセッションで一番気になったのはドワンゴ様のサポート室
ニコニコ動画ではどうしても素早いリリースが必要であり、技術的負債が蓄積しやすい環境にある
それを返済しなくてはならないのだが次々に仕事を頼まれてしまう…
そういう時、サポート室に相談し一定期間技術的負債を返済する期間を設け新しいタスクをシャットアウトしてくれるというシステムらしい

ファンコミュニケーションズ様の毎月1万円分技術書購入、セミナーへの参加費用を会社が負担してくれるというのが素直に羨ましい
このとき購入した技術書はあくまで個人の所有物で退職の際などにも返却の必要はないだとか

Scala Hack-a-thon

いまいち作りたいものが思い浮かばなかったためNinety-Nine Scala ProblemsS-99: Ninety-Nine Scala Problemsを解いていました

お昼からはちょっとしたLTの開始
Jamie氏からスタックトレース等についての話がありました
こちらは当日のTwitterなどを追うと様子がわかるかと思います。

途中で気になったのが職場の人間にScalaを紹介したいのですが…という話
Javaを利用している現場にScalaを導入したいのだがどうやって紹介したものか?
そこで参加者のみなさんから紹介スライドにいろいろアドバイスが欲しいといった内容

スライドはJavaScalaはこんなに文法がにているんですよと言った内容
会場からは現場の人々はJavaをある程度わかっているのだから、
そんば部分を紹介するよりはScalaにしかできないこと、Javaなら大変だけどScalaなら簡単にできることを推したほうが良いのでは?といった意見が

個人的に好きになんでも作っていいといわれると何作ればよいものかと考えこんで手が止まってしまうので
次回参加する際には先になにかネタを考えていったほうがいいなーとちょっと後悔

雑感

大阪周辺ではScalaはまだまだこれから使いはじめるという人が多く開発環境やテスト、実際利用してみての感想を聞くことはあまりありませんでした。
今回のカンファレンスを通してその辺りいろいろ聞くことができたのが大きな収穫

sbtに対する不満であったり、コンパイルの速度の遅さであったりわりとみんな同じとろこで悩んでいるんだなと

こうして全国からScalaに注目している人が集まって熱気を感じることができたのが一番の収穫
70人あまりのキャンセル待ちがでるほどとは…
Scalaはまだまだこれからだと思うのでどこかでコミュニティに貢献出来ればなーっと考えているところ

PlayframeworkでSORMを使う

Playframework2.1でSORMIntroduction - SORMを利用する際sbtで詰まったので解決法をメモ程度に

Build.scala

  val appDependencies = Seq(
    // Add your project dependencies here,
    jdbc,
    anorm,
    "com.github.nikita-volkov" % "sorm" % "0.3.5"  exclude("com.google.guava", "guava-base") exclude("org.slf4j", "slf4j-api")
  )

excludeを利用するとsormが利用しようとしているライブラリを無視?できるようです。
guava-baseとslf4j-apiはplayでははじめから入っているのでここでは無視するだけで大丈夫です。

Play2.1RC+Scala+SlickでTODOチュートリアルを作成する

Play2.1からはデフォルトのORMがAnormからScalaQueryに変更されるって話を聞いたので
PlayFramework2.0のTODOチュートリアルをPlay2.1+Scala+Slickで書き直してみようと思います。

まず2.1RC1の導入ですがこちらから

Play2.0.4で作ったプロジェクトをPlay2.1RC1で利用できるようにするためにはいろいろ変更の必要があります。
が、今回は大人しくPlay2.1RC1で作成したアプリケーションを利用しましょう。

まずSlickの導入です。

object ApplicationBuild extends Build {

  val appName         = "SlickSample"
  val appVersion      = "1.0-SNAPSHOT"

  val appDependencies = Seq(
    // Add your project dependencies here,
    jdbc,
    anorm,
    "com.typesafe" % "slick_2.10.0-RC1" % "0.11.2"
  )


  val main = play.Project(appName, appVersion, appDependencies).settings(
    // Add your own project settings here      
  )

}

appDependencies内に「"com.typesafe" % "slick_2.10.0-RC1" % "0.11.2"」を追加するだけです。簡単ですね。

ではSlickでチュートリアルで作成したTask.scalaを書き換えてみましょう。こうなります。

package models

import scala.slick.driver.H2Driver.simple._
import Database.threadLocalSession

case class Task(id: Long, label: String)

object Tasks extends Table[Task]("TASK") {

  def id = column[Long]("ID", O.PrimaryKey, O.AutoInc)
  def label = column[String]("LABEL", O.NotNull)
  def * = id ~ label <> (Task, Task.unapply _)
  def ins = label returning id

  def all(): List[Task] = connectDB {
    Query(Tasks).sortBy(_.id).list
  }

  def create(label: String) = connectDB {
    Tasks.ins.insert(label)
  }

  def delete(id: Long) = connectDB {
    Tasks.where(_.id === id).delete
  }

  def connectDB[Any](f: => Any): Any = {
    Database.forURL("jdbc:h2:mem:play", driver = "org.h2.Driver") withSession {
      f
    }
  }

}

変更点を上から順に追っていきましょう。
まずimport文です。
Slickを利用する際には下記の二つをインポートすればOKなようです。

import scala.slick.driver.H2Driver.simple._
import Database.threadLocalSession

次にcase classです。
Slickではcase classを利用せず結果をタプルで返すことも出来ますが
今回は出来るだけ以前のソースコードを変更しないようそのままにしておきます。

そして次のobjectがSlickのキモの部分になります。

object Tasks extends Table[Task]("TASK") {

  def id = column[Long]("ID", O.PrimaryKey, O.AutoInc)
  def label = column[String]("LABEL", O.NotNull)
  def * = id ~ label <> (Task, Task.unapply _)
  def ins = label returning id
}

この宣言でテーブルの定義が完了したも同然となります。
Anormで1.sqlで直に書くよりはすっきりした・・・かもしれません。
まずTableを継承することを忘れないように。

objectにdefで宣言されているid,labelがそれぞれTASKテーブルのカラムになります。
O.PrimaryKeyは主キーの宣言に用います。
なお、複合主キーである場合は宣言方法が異なります。
O.AutoIncを利用することでシーケンスを用いてIDを自動採番します。
O.NotNullはNotNull制約の宣言になります。

調べた限りではPrimaryKeyでないがUniqueであるカラムを宣言できないように見えます。
誰かご存知の方がいらしたらご教授ください。
2013/2/4追記

def * はパーサーでセレクト文を利用した際の戻り値の形式をここで決めているようです。

def insはインサートを行う際に利用します。
今回はシーケンスを利用した自動採番を用いているのでこれを宣言しています。

では次に実際にDBでCRUD操作を行う関数です。

  def all(): List[Task] = connectDB {
    Query(Tasks).sortBy(_.id).list
  }

  def create(label: String) = connectDB {
    Tasks.ins.insert(label)
  }

  def delete(id: Long) = connectDB {
    Tasks.where(_.id === id).delete
  }

  def connectDB[Any](f: => Any): Any = {
    Database.forURL("jdbc:h2:mem:play", driver = "org.h2.Driver") withSession {
      f
    }
  }

上記のように一切SQL文を記述することなくAnorm版と同様の結果を取り出せます。
まずconnectDBですがこれはDBアクセスの際必要となる記述を高階関数として宣言しています。
このあたりScalaの便利なところですね。

allではTASKテーブルに登録されている情報をID順に整列し、リスト化して取り出しています。
createではlabelをDBに登録します。
ちなみに以下のように書いても同じように動作します。

Tasks.label.insert(lable)

deleteはwhereによって条件を絞り込み削除。簡単ですね。

なお上記の変更に伴いApplication.scalaも一部変更になります。

package controllers

import play.api._
import play.api.mvc._

import play.api.data._
import play.api.data.Forms._

import models._

object Application extends Controller {

  val taskForm = Form(
    "label" -> nonEmptyText)

  def index = Action {
    Redirect(routes.Application.tasks)
  }

  def tasks = Action {
    Ok(views.html.index(Tasks.all(), taskForm))
  }

  def newTask = Action { implicit request =>
    taskForm.bindFromRequest.fold(
      errors => BadRequest(views.html.index(Tasks.all(), errors)),
      label => {
        Tasks.create(label)
        Redirect(routes.Application.tasks)
      })
  }

  def deleteTask(id: Long) = Action {
    Tasks.delete(id)
    Redirect(routes.Application.tasks)
  }

}

Taskとして呼び出していた部分をTasksに書き換えています。

なおAnormでは1.sqlがアプリケーション起動時に実行されていたので必要ありませんでしたが
今回はアプリケーション起動時にテーブルの作成をGlobal.scalaに記述します。
Global.scalaという名前でapp/に配置してください。

import play.api._
import models._
import scala.slick.driver.H2Driver.simple._
import Database.threadLocalSession

import models._
import java.sql.{Date,Time,Timestamp}

object Global extends GlobalSettings {

  override def onStart(app: Application) {
    Logger.info("Application has started")

    Database.forURL("jdbc:h2:mem:play", driver = "org.h2.Driver") withSession {

      Tasks.ddl.create

    }

  }

  override def onStop(app: Application) {
    Logger.info("Application shutdown...")
  }

}

Tasks.ddl.createでテーブルの作成を行っています。

以上で行うことでSlickを利用してチュートリアルのTODOアプリケーションの完成です。

なかなか便利なSlickですがあまりドキュメントが見当たらず
こちらからテストなどを元に調査している状態です。

Slickを利用する上で注意しなければいけないのはテーブルにカラムが23以上存在する場合です。
23という数字でピンとくるかもしれませんがTuple22問題が直撃してます。
テーブルの宣言でタプル内に23個以上宣言するとコンパイルを通りません。
またcase classを利用した場合ですがこちらも23個以上フィールドをもてないのでアウトです。
これの対策は設計を見直すか、タプルなりcase classをネストするといった方法があるようです。

tuple22問題対策とUnique宣言する方法は調査中です。

追記

PrimaryKeyではないがUniqueを利用したい場合について
以下のようにindexを定義してやることでUniqueをつけることできます。

  def idx = index("idx_a",label,unique = true)

Tuple22問題の解決策について
case classをネストさせる。タプルをネストさせるといった手段が取られているようです。
次のバージョンで解決される…かも?