PersistentのMigrationが便利そうだ

Persistentを触ってみている

{-# LANGUAGE QuasiQuotes, TemplateHaskell, TypeFamilies, OverloadedStrings #-}
{-# LANGUAGE GADTs, FlexibleContexts #-}
import Database.Persist
import Database.Persist.Sqlite
import Database.Persist.TH
import Control.Monad.IO.Class (liftIO)

share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persist|
Person
    name String
    age Int Maybe
    loc String default="Japan"
    deriving Show
|]

main :: IO ()
main = withSqliteConn "test.db" $ runSqlConn $ do
    runMigration migrateAll
    johnId <- insert $ Person "kzfm" (Just 20) "Fuji"
    liftIO $ print johnId

実行するとデータベースが作られる。実行されるSQLはごく普通のcreate文

CREATE TABLE "Person"("id" INTEGER PRIMARY KEY,"name" VARCHAR NOT NULL,"age" INTEGER NULL)

続いてEntityを変更してみる。locを追加してdefaultも付けておく。ついでにinsertのとこも修正しておく

share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persist|
Person
    name String
    age Int Maybe
    loc String default="Japan"
    deriving Show
|]

main :: IO ()
main = withSqliteConn "test.db" $ runSqlConn $ do
    runMigration migrateAll
    johnId <- insert $ Person "kzfm" (Just 20) "Fuji"
    liftIO $ print johnId

バックアップテーブルにデータが移され、もとのテーブルが変更された後、データが戻されて移行が無事に完了する。

CREATE TEMP TABLE "Person_backup"("id" INTEGER PRIMARY KEY,"name" VARCHAR NOT NULL,"age" INTEGER NULL,"loc" VARCHAR NOT NULL DEFAULT "Japan")
INSERT INTO "Person_backup"("id","name","age") SELECT "id","name","age" FROM "Person"
DROP TABLE "Person"
CREATE TABLE "Person"("id" INTEGER PRIMARY KEY,"name" VARCHAR NOT NULL,"age" INTEGER NULL,"loc" VARCHAR NOT NULL DEFAULT "Japan")
INSERT INTO "Person" SELECT "id","name","age","loc" FROM "Person_backup"
DROP TABLE "Person_backup"

Entityのデフォルトはデータ移行時のもとのカラムに付加される値であって、insertで省略できるわけではないので、insertは明示的に値を入れないといけない。databaseを見てみるともとのデータのlocにはEntityのデフォルトで指定したJapanという値が入っていることがわかる。

sqlite> select * from person;
1|kzfm|20|Japan
2|kzfm|20|Fuji

ちなみにEntityのデフォルトを指定しないとマイグレーションに失敗するが、Maybe型にしておけばnullが入るので移行できる。

share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persist|
Person
    name String
    age Int Maybe
    loc String Maybe
    deriving Show
|]

開発している時にスキーマの変更はそれなりに苦痛を伴う作業なので、ある程度面倒見てくれるのはありがたい。ダメなときはダメって言ってくれるし。

Persistentのマイグレーションは保守的なので安心らしい。次のケースで自動的にスキーマの変更がなされる。

  • フィールドのデータタイプが変更された時 。ただし変更できる時のみ
  • (The datatype of a field changed. However, the database may object to this modification if the data cannot be translated.)
  • フィールドが追加された時で、フィールドがnot nullの場合はdefaultが指定されている時
  • (A field was added. However, if the field is not null, no default value is supplied (we'll discuss defaults later) and there is already data in the database, the database will not allow this to happen.)
  • フィールドがnot nullからnullに変更された時。逆のケースも試みるがデータベース次第
  • (A field is converted from not null to null. In the opposite case, Persistent will attempt the conversion, contingent upon the database's approval.)
  • 新しいエンティティが追加された時
  • (A brand new entity is added.)

ProductName Developing Web Applications With Haskell and Yesod
Michael Snoyman
Oreilly & Associates Inc / 2805円 ( 2012-05-04 )


Haskellのpersistentが動かない

persistent-1.1.0.1,persistent-template-1.1.1が入っている。

Persistentのsynopsisの例を実行すると

No instance for (Control.Monad.Trans.Resource.MonadResource IO)
  arising from a use of `selectList'
Possible fix:
  add an instance declaration for
  (Control.Monad.Trans.Resource.MonadResource IO)
In a stmt of a 'do' block:
  oneJohnPost <- selectList [BlogPostAuthorId ==. johnId] [LimitTo 1]
In the second argument of `($)', namely
  `do { runMigration migrateAll;
        johnId <- insert $ Person "John Doe" $ Just 35;
        janeId <- insert $ Person "Jane Doe" Nothing;
        insert $ BlogPost "My fr1st p0st" johnId;
        .... }'
In the second argument of `($)', namely
  `runSqlConn
   $ do { runMigration migrateAll;
          johnId <- insert $ Person "John Doe" $ Just 35;
          janeId <- insert $ Person "Jane Doe" Nothing;
          insert $ BlogPost "My fr1st p0st" johnId;
          .... }'

困った。

ProductName Developing Web Applications With Haskell and Yesod
Michael Snoyman
Oreilly & Associates Inc / 2805円 ( 2012-05-04 )


追記 121220

これは動く

{-# LANGUAGE QuasiQuotes, TemplateHaskell, TypeFamilies, OverloadedStrings #-}
{-# LANGUAGE GADTs, FlexibleContexts #-}
import Database.Persist
import Database.Persist.Sqlite
import Database.Persist.TH
import Control.Monad.IO.Class (liftIO)

share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persist|
Person
    name String
    age Int Maybe
    deriving Show
|]

main :: IO ()
main = withSqliteConn "test.db" $ runSqlConn $ do
    runMigration migrateAll
    -- johnId <- insert $ Person "John Doe" $ Just 35
    -- liftIO $ print johnId

insertしたりselectListしたりするとNo instance for (Control.Monad.Trans.Resource.MonadResource IO)が出る。

追記 121220

これか? - Simplified Persistent Types

追記 121220

バージョンを下げたら動いたので当分この組み合わせで使う

  • persistent-1.0.2.2
  • persistent-sqlite-1.0.1
  • persistent-template-1.0.0.2

diagramsが素敵すぎる

diagramsを使ってみた。

{-# LANGUAGE NoMonomorphismRestriction #-}
import Diagrams.Prelude
import Diagrams.Backend.SVG.CmdLine
import Data.Colour

tri c n = dots <> (strokeT edges # lc c # lw 0.2 # fcA (c `withOpacity` 0.5))
  where rows = map (hcat' with { sep = 1 })
             . zipWith replicate [n,n-1..1]
             . repeat
             $ dot c
        dots = decorateTrail (rotateBy (1/6) edge) rows
        edge = fromOffsets . replicate (n-1) $ unitX # scale 3
        edges = close (edge <> rotateBy (1/3) edge <> rotateBy (2/3) edge)

dot c = unitCircle # lw 0 # fc c

rowSpc = height (rotateBy (1/6) $ strutY 1 :: D R2)
row k n s c = hcat' with {sep = 1 + 3*s} (replicate k (tri c n))
law4 k n c1 c2 = vcat' with {sep = rowSpc} (map tRow [1..k])
  where tRow k = (row k n 0 c1 # centerX # alignT)
                 <>
                 (row (k-1) (n-1) 1 c2 # reflectY # centerX # alignT)
exampleRow f = hcat' with {sep = 4} . map (alignB . f)

law4Dia = exampleRow law4' [2..4]
  where law4' k = law4 k 3 purple gold

example = pad 1.1 $ law4Dia # centerXY

main = defaultMain example

これだけで下のような図が作成できる

diagrams

PyJadeでenumerateしたりstrしたい

僕はJade派なのでPythonでもJadeを使うわけだが、jadeテンプレートのなかでゴニョゴニョしたい時にハックっぽいことをしないといけないのは何とかならんかなぁと思っている。

enumerate

配列のインデックス値が欲しい時

for elm in ary
  i = ary.index(elm)

とやっている。

str

Intを文字列にしたい時str関数が使えないので

elm.id.__str__()

とやっている。

pythonの組込み関数使えると嬉しいんだけど、やり方あるのかな?

山を歩こう

そろそろ歩きたいなぁと思って近場の山を探すために買ってみた。

ProductName 新・分県登山ガイド 改訂版21 静岡県の山
加田 勝利
山と渓谷社 / 1995円 ( 2009-12-16 )


浜石岳とか白水山でも登ってみようかな。

そういえば去年のGDDで山系のARアプリが出てたので、「かざすと登れるかどうか判定してくれると嬉しいなぁ」とか言ってみたんだけど、そういうのだったら本と同じ値段(2K)くらいは出してもいいかもと思った。

山渓にはアプリを頑張って欲しい。

Haskellでもmkvirtualenvとworkonが使いたい

Pythonにはvirtualenvwrapperというvirtualenvをより快適につかうためのパッケージがあって、インストールするとmkvirtualenvとworkonというコマンドが使えるようになり、散らばりがちな仮想環境をきちんと管理できるようになるので、一度使うと手放せない。

Haskellでもvirtualenvクローンのvirthualenvを使っているのでmkvirtualenv相当のツールが欲しくなるのは必然。

mkvirthualenvというコマンドを書いた。

import System.Directory
import System.FilePath
import System.Process
import System.Environment

getVirthualenvDir :: IO FilePath
getVirthualenvDir = do
  home <- getHomeDirectory
  return $ home </> ".virthualenv"

main :: IO ()
main = do
  (newenv:_) <- getArgs
  currentDir <- getCurrentDirectory
  vdir <- getVirthualenvDir
  createDirectoryIfMissing True vdir
  createDirectoryIfMissing True $ vdir </> newenv  
  setCurrentDirectory $ vdir </> newenv
  _ <- system "virthualenv" 
  setCurrentDirectory $ currentDir
  return ()

workhonはbashの関数にしてみた。

function workhon(){ source $HOME/.virthualenv/$1/.virthualenv/bin/activate; }

要するに.virthualenvっていうディレクトリでそれぞれの仮想環境を管理するようにしているので、要らなくなったら該当するディレクトリを消せばいいだけ。

例えばHakyll用の仮想環境を用意したい場合、

mkvirthualenv hakyll
workhon hakyll
cabal install hakyll

こんな感じ。仮想環境をぬけるのは

deactivate

入るのがworkhonで出るのがworkhoffじゃなくてdeacivateなのは何でじゃ?っていう疑問も本家Pythonゆずりということで。

macbook airに移行した

移行アシスタントで丸二日かかったので、thundervolt-LANとかそういう有線のケーブルは必須かなと思った。

ちなみにきちんと確認せずに買ったUSB-100の変換アダプタは遅すぎて役に立たなかったが、出張先のホテルでLANケーブルに差す時には重宝するんだろうなぁ。

たった6行のコードでひたすらアイドル水着画像をあつめる(Haskell)

普段はrequests+pyqueryでやるが、今はHaskell強化月間なのでHandsomeSoupを使ってみた。

import Text.HandsomeSoup
import Text.XML.HXT.Core
main = do
  doc <- fromUrl "http://matome.naver.jp/odai/2135350364969742801"
  links <- runX $ doc >>> css ".MTMItemThumb" ! "src"
  mapM_ (putStrLn . show) links

調べてわかったんだが、Haskellって意外とスクレイピング用のライブラリが充実していた。

広島寒い

初めての広島は寒かった。何日か前には雪も降ったらしい。

講義の感触はいまいちわからないんだが、質問はよくされていたので良しとしよう。

僕も色々勉強になったし良かった。

1355316224

100枚スライドを用意していって80枚しか使わなかったのだけど、最後の20枚はopensourceとどう付き合うかとか、勉強会にいこうみたいなキャリアをどう考えるかみたいなどうでもいい話だったので出さなくて良かった。

10年以上この業界にいてその当時と変わったなーと思うことはいくつかあった。

  • IT系に関してベンダー製のソフトウェアを必須とするタスクはほとんどない。
  • (ドッキングシミュレーションのソフトウェアくらい)
  • (あれば便利なのはあるが(PipelinePilotなど))

  • クラスタも今はEC2があるし気軽にランできる

  • (VPSは便利)

  • 教育のためのレールも引かれつつある(見つけにくいだけで)

  • (SNSもかなり重要だったりする)

講演とか講義のために資料をまとめると自分の頭も整理されていい。

macbook airが来た

週末に移行作業する。

1355131337

純正品を買うのもどうかなーと思ったので、planexで。

信者じゃないのでこだわらない。