2013年1月13日日曜日

GitHub リポジトリ内のソースコードの検索を行う

GitHubのリポジトリであのメソッドの定義を確認したい、ということが少なからずあるが、標準の検索で引っかからず仕方なくfork/cloneする・・・めんどくさい!と思ったことはないだろうか。

そんなときに仕えるのがAdvanced Searchのオプション。リポジトリ内のソースは以下のように検索できる。

repo:lift/framework  AddUserMenusAfter
例:Liftフレームワーク内のAddIserMenusAfterの定義を検索

repo:ユーザー名/リポジトリ名 検索キーワード で検索ができる。上記の例をそのまま検索窓に入れるときちんと検索されるはず(なぜか何個も引っかかるが・・・)。

これ以外にもいくつか使えるオプションがあり、それは こちら 参照(使えんオプションが多いのは気のせい?)。

これでわざわざローカルに落とす手間が省けます。

<参考>
Search code inside a Github project


2013年1月11日金曜日

TryAndError Appharbor でEntityDataModelを使用する

AppharborでEntityDataModelを使用する際にはまったポイントについてつらつらと書いていきます。

1.DateTime型を使ったらエラーで怒られる
これはAppharborとは関係ないが、モデルでDatetime型の項目を使用していると登録時に「Datetime2 データ型から datetime データ型への変換の結果、範囲外の値になりました」というエラーが出る

原因はよくわからんが、Nullを許可するDatetime?型だとこれを回避できるらしい(VB.NETだとNullable)。

2.DBが最新でないみたいなエラーが起きる
モデルを変更後、マイグレーションをしていないとこのエラーが起こる。

The model backing the 'YourContext' context has changed since the database was created. Consider using Code First Migrations to update the database (http://go.microsoft.com/fwlink/?LinkId=238269).

マイグレーションの方法は こちら 参考。EntityDataModelインストール済みなら以下のコマンドを打てばいい。

Add-Migration マイグレーション名
Update-Database 


3.本番であるAppharborと開発環境でConnectionStringを変えたい
これにはWeb.Release.Configを使用する。デフォルトで作成されているので中身を見れば記入方法は分かるが、Release環境時にWeb.Configの値を置き換えることが可能。

 <add name="MyDB" connectionString="Data Source=ReleaseSQLServer;Initial Catalog=MyReleaseDB;Integrated Security=True" 
  xdt:Transform="SetAttributes" xdt:Locator="Match(name)"/>

ポイントは青字部分で、これでWeb.Config上のnameが一致するものを置き換えてくれる。


4.テストプロジェクトがあるがデプロイ時自動で走るのでこれをやめたい
Appharborへデプロイする際は、テストプロジェクトがあるときそれが実行される。これはこれでいいのだが、DBへアクセスするテストがある場合有難迷惑なのでデプロイ時テストプロジェクトをはずしたいことがある。

こうしたときは、既存のソリューションファイル(.sln)をコピーし Appharbor.sln というファイルを作成し、これを編集しテストプロジェクトを除外する。

Appharbor上では、Appharbor.slnがあればそちらが優先される。


5.ProviderManifestTokenから文字列が返されませんでしたエラーが出る
EntityDataModelはデフォルトでデータベースの作りかえを行うが、普通Appharbor上で使用するDBのアドオンでそんなこと許されるわけないので、DBの作り返しを行わないよう、Global.aspxのApplication_Start でInitializorの設定を行う。

Database.SetInitializer<YourContext>(new CreateDatabaseIfNotExists<YourContext>());

CreateDatabaseIfNotExistsが肝。あとは、ConnectionString aliasにconnectionStringのnameを設定しておく。

とりあえずこれで解決した。こちらのサポートのやり取りを参照。



まだ追記するかもしれないが、とりあえずこれくらいか・・・。こういうところはさくっと行ってほしいんだがな~


2013年1月10日木曜日

OracleからSqlServerへ接続する

OracleからSqlServerへ接続する方法をまとめ。基本的にはODBCデータソースとDBLinkを使用する。イメージ的には、 SqlServer -> ODBCデータソース -> DBLink -> Oracle という形になる。

1.ODBCデータソースの登録
コントロールパネル > 管理ツール > データ ソース (ODBC) を選択。
ここでデータソースの追加ボタンを押し、SqlServerかSqlServerNativeClientのどちらかでデータソースを作成する。
接続するサーバーは サーバー名(IPアドレス)\インスタンス名 が基本だが、サーバー名だけでも良いようだ。SqlServerでインスタンスを作成すると自動的に作成されるようなので、SqlServerを立てている側のサーバーのデータソース設定を見ると正確。

他のログイン設定は、適宜設定。


2.Oracle Database Gatewayの登録
ORACLE_HOME\hs\admin のフォルダにDatabaseGatewayの設定ファイルがある。
ファイル名は init<SID>.ora とする。SIDは任意の名称。dg4・・・とするのが慣例?のようだ(dgはDatabaseGatewayの略と思われる)。

内容は以下の通り。

HS_FDS_CONNECT_INFO = <odbc data_source_name>
HS_FDS_TRACE_LEVEL = <trace_level>

<odbc data_source_name>は先ほど作成したデータソース名、<trace_level>はOFFかONを設定。ONにしておくとoracle_home:[tg4rdb.log].にログを書き出してくれるので、ONにしておくのを推奨。


3.リスナーへの登録
OracleのリスナーにGatewayを登録する。ORACLE_HOME\network\admin にあるlistener.oraにGateway用の以下の記述を追記。

SID_LIST_LISTENER=
   (SID_LIST=
      (SID_DESC= 
         (SID_NAME=gateway_sid)
         (ORACLE_HOME=oracle_home_directory)
         (PROGRAM=dg4odbc)
      )
   )

gateway_sidは2で設定したSID(ファイル名の一部)。oracle_home_directoryはoracle_homeの値となる。

この設定を終えたのち、リスナーの再起動を行う。
※一度リスナーの再起動に失敗したことがあった。プロセスを見てみるとODBCデータソースがリスナーが起動するのに必要なポートを占有していたようなので、これを切ったところうまく立ち上がった(普通に起きる出来事なのかは定かでない・・・)。


4.接続名の登録
listener.oraと同フォルダにあるtnsnames.oraで定義されているOracleの接続名に追記を行う。

SQL_SRV=
   (DESCRIPTION=
    (ADDRESS= (PROTOCOL=TCP)(HOST=odbc_data_source_host)(PORT=1521))
    (CONNECT_DATA=(SID=gateway_sid))
    (HS=OK)
)

ホスト名が、相手先のSqlServerのホストでなく、ODBCデータソースのホスト(要するに自サーバー)である点に注意。


5.DBLinkの登録
ここまでくればあと一歩である。
以下のコマンドでDBlinkを作成する。なお、作成にはCREATE DATABASE LINK関連の権限が必要なため注意(こちら参考)。

CREATE PUBLIC DATABASE LINK dblink_name CONNECT TO user_name IDENTIFIED BY password USING 'service_name';

サービス名は文字列としてシングルクォーテーションでくくらないといけない点に注意。PUBLICをつけるかどうかは場合によりけり。

これでDBLINKの作成が完了したので、以下のSQL文で動作確認を行う。

select 'X' from dual@dblink_name

これで出力が返ってくれば完成!である。


※一度相手先のSqlServerがとまっていたことがあり、このときOracle側のリスナーも起動が失敗した。再現実験ができていないのだが、もしかしたらリスナー起動時にGatewayの生死判定があり、相手先が止まっていた場合自分も起動しないという迷惑判定があるかもしれないので要注意。


<参考>
Configuring Oracle Database Gateway for ODBC

D Heterogeneous Services Initialization Parameters



2013年1月2日水曜日

Scala Lift lift_basic を読み解く View編

Scala Lift を理解するに当たり、一通りの機能を備えたサンプルであるlift_basicを元にその動作を追ってみる。

Liftのソースコードはこちら。これを書いている時点では2.5-M3が最新である。
Lift Download

ダウンロードしたzipなりを解凍すると中にlift_basic、lift_blankなどのフォルダがある。これらがそれぞれテンプレートとなっており、sbtのコマンドで動かすことができる(動かす方法はダウンロードページ参照)。

0.Liftの特徴
これは様々な解説サイトがあるのでそちらの方が詳しかろうが、「ビューファースト」であるという一言に尽きると思う。
Ruby On Railsなどの最近のフレームワークはモデルに合わせて画面を作るというイメージだが、Liftは画面を作りモデルをそこに適用する、というイメージである。

ユーザーの目に触れる画面は一刻も早く手を付け最も時間をかけたいところであるが、既存のフレームワークでは「モデルがないと画面がつくれない。待って。」とならざるを得ない。それを解決するのがビューファーストであり、それを支援するLiftフレームワークなのである。

(※ビジネスモデルに通ずるモデルの構成をまずきちんと考えるべきという説もあり、この辺りはケースバイケース)。


1.LiftのView
そんなわけで、Liftを読み解くに当たりまず確認するのはやはり画面である。

lift_basic/src/main/webapp/index.html
<!DOCTYPE html>
<html>
  <head>
    <meta content="text/html; charset=UTF-8" http-equiv="content-type" />
    <title>Home</title>
  </head>
  <body class="lift:content_id=main">
    <div id="main" class="lift:surround?with=default;at=content">
      <h2>Welcome to your project!</h2>
      <p>
<span class="lift:helloWorld.howdy">
 Welcome to your Lift app at <span id="time">Time goes here</span>
</span>
      </p>
    </div>
  </body>
</html>

Scalaは関数型言語の機能を持っているが、このページも関数のように処理される。アバウトに言ってしまうとclassの(lift:で始まる)値=「関数」、classの設定されているHTMLエレメント(divなど)=「引数」となっている。

1.1 スニペット
手近なところで以下の部分を見てみる。

<span class="lift:helloWorld.howdy">
  Welcome to your Lift app at <span id="time">Time goes here</span>
</span>

これは内部要素をHellowWorldクラスのhowdyメソッドで処理してください、という意味である。このように特定の要素を処理するためのクラス(とメソッド?)をスニペット(snippet)と呼ぶ。

lift_basic/src/main/scala/code/snippet/HelloWorld.scala のスニペットを見てみると・・・
class HelloWorld {

  lazy val date: Box[Date] = DependencyFactory.inject[Date] // inject the date

  // replace the contents of the element with id "time" with the date
  def howdy = "#time *" #> date.map(_.toString)

  /*
   lazy val date: Date = DependencyFactory.time.vend // create the date via factory

   def howdy = "#time *" #> date.toString
   */
}

(Boxなどはとりあえず無視し) defで始まるhowdyメソッドの定義に着目する。
"#time *" #> となっているのは、idがtimeのところだけ、という指定である。このほかにもCSSセレクタのような指定が可能である(CSS Selector Transforms 参照)。
このセレクタのようなメソッドはどうやらStringクラスのメソッドが拡張されているようで、セレクタに該当する部分を引数(この場合date.toString)に置換する関数(CssSel)を返しているようだ。

この部分を手動で書くなら、HTMLエレメント(NodeSeq)を受け取って(編集後の)HTMLエレメント(NodeSeq)を返す関数を定義することになる。以下がその例である。

//メソッド定義
 def howdy2(xhtml:NodeSeq):NodeSeq = bind("tag",xhtml,"param1" -> <span>Hellow</span>)

//htmlでの呼び出し
 <div class="lift:helloWorld.howdy2">
    <b><tag:param1 ></tag:param1></b>
 </div>
bind関数によって、対象HTMLエレメント内のtag:param1要素を<span>Hellow</span>に置換するという意味になる。
なお、置換するHTMLエレメントは上記のようにspanタグなどで囲まなくてもSHtmlという関数が用意されており、こちらを使用すると楽。 SHTMLのAPI


1.2 テンプレート
HTMLエレメントには、このスニペット以外にもテンプレートを適用することができる。これが以下の部分である。

    <div id="main" class="lift:surround?with=default;at=content">

surroundはページのテンプレートの指定である。このテンプレートは、lift_basic\src\main\webapp\templates-hidden に配置されており、ここではwith=default; default.htmlを使用すると宣言している。また、at=content の指定によりこのdiv内の要素をdefault.html内のid=content要素に適用するとしている。
これは、surroundという関数にwith、atというデフォルトテンプレート、置換先の要素を指定する引数を渡したかのようでもある。

この部分は <body class="lift:content_id=main"> によりいわば「このページのメイン関数はid=mainである」と指定されており、ページのレンダリングを行う際の処理の開始点となる(※誤りある可能性有。大体そんな感じととらえてください)。


LiftにおけるViewの概要はこんな感じとなる。