ちょっと悩んだのでメモ
- NG
#!/bin/env ruby -Ku s="MAC'D" ss=s.gsub("'","\\\'") print ss # MACDD
#!/bin/env ruby -Ku s="MAC'D" ss=s.gsub("'","\\'") print ss # MACDD
- OK
#!/bin/env ruby -Ku s="MAC'D" ss=s.gsub("'","\\\\'") print ss # MAC\'D
ちょっと悩んだのでメモ
#!/bin/env ruby -Ku s="MAC'D" ss=s.gsub("'","\\\'") print ss # MACDD
#!/bin/env ruby -Ku s="MAC'D" ss=s.gsub("'","\\'") print ss # MACDD
#!/bin/env ruby -Ku s="MAC'D" ss=s.gsub("'","\\\\'") print ss # MAC\'D
どうやらWindowsでもシンボリックリンクが作成できるようです。Vista以降。
というのもScalaのベータ版を使ってみたいがためいちいち入れ替えるのもあれなので、うまくできないかなと探していたところ以下の記事を発見
http://blog.livedoor.jp/s_imai_jp/archives/51360129.html
C:\Program Files (x86)>path PATH=...;C:\Program Files (x86)\scala\\bin ;C:\play-2.1.3;C:\Program Files\Java\jdk1.7.0_40\bin;... C:\Program Files (x86)>scala -version Scala code runner version 2.10.2 -- Copyright 2002-2013, LAMP/EPFL C:\Program Files (x86)>mklink /d scala scala-2.11.0-M4 scala <> scala-2.11.0-M4 のシンボリック リンクが作成されました C:\Program Files (x86)>scala -version Scala code runner version 2.11.0-M4 -- Copyright 2002-2013, LAMP/EPFL
ちょっと困ったのでメモ
通常JavaのInetAddress.getLocalHost().getHostAddress()から取得できるIPアドレスは127.0.0.1
これではちょっと使えません
ネットワークインターフェースを調べるとちゃんとした?IPアドレスが取得できます
import java.net.*; import java.util.*; public class test{ public static void main(String[] argv)throws Exception{ Enumerationn = NetworkInterface.getNetworkInterfaces(); while (n.hasMoreElements()){ NetworkInterface e = n.nextElement(); Enumeration a = e.getInetAddresses(); while ( a.hasMoreElements()){ InetAddress addr = a.nextElement(); if (!addr.getHostAddress().equals("127.0.0.1")) System.out.println(addr.getHostAddress()); } } } }
メモ
sbt > compile > package
こちらの記事を参考にJavascriptでサブドメインの異なるサイト間でクッキーの恭有してみました。
http://d.hatena.ne.jp/dirablue/20090506/1241607961
ポイントはHTMLを設置するパスを同じにすることとdomain指定の前にドットをつけることでしょうか?
あとIDやパスワードをクッキーに保存してはいけません^^);
意外とわからなかったのでメモ。
PlayFrameworkのセッションはCookieになります。ブラウザを閉じるまで有効なので、従来のservletなどで実現してきたsessionの観念はありません。
これが意外と困ってしまうので(タイムアウトした際にログアウトさせるなど)Cookieに時間を持たせることによりにたようなものを実装してみました
GET / controllers.Application.index GET /login controllers.Application.login POST /login controllers.Application.submit
package controllers import play.api._ import play.api.mvc._ import helper.Session import helper.Login import play.api.data._ import play.api.data.Forms._ import play.api.data.validation.Constraints._ case class User(id:String,password:String) object Application extends Controller { val userForm = Form[User]( mapping( "id" ->nonEmptyText, "password"->text(minLength=6)) ((id,password)=>User(id,password))((user:User)=>Some(user.id,user.password)) ) def index = Action {request=>{ if(Session.check(request))Ok(views.html.index("LoginOK")) else Redirect(routes.Application.login) } } def login=Action{ Ok(views.html.login(userForm)) } def submit = Action { implicit request => userForm.bindFromRequest.fold( errors => BadRequest(views.html.login(errors)), user => { if(Login.check(request,user.id,user.password))Redirect(routes.Application.index).withSession(Session.set(request)) else BadRequest(views.html.login(userForm)) } ) } }
本来ならばログインIDとパスワードとつきあわせてチェックするのですがとりあえず必ずOKになるように
package helper import play.api._ import play.api.mvc._ object Login { def check(req:Request[AnyContent],id:String,password:String):Boolean={ true // NO check } }
クッキーに保存した時刻と現在時刻を比較し、ログアウトの判定
package helper import play.api._ import play.api.mvc._ object Session { val MAX_MILLISEC:Long=1000*60 // time out val SESSION_KEY="play2" def check(req:Request[AnyContent]):Boolean={ req.session.get(SESSION_KEY).map{ user=>{ val cookieDate=user.toLong val nowDate=new java.util.Date().getTime(); if(nowDate-cookieDatenew java.util.Date().getTime().toString()) } // ログアウトボタンを押したらこれを呼ぶ、未実装 def del(req:Request[AnyContent])={ req.session - SESSION_KEY } }
@(message: String) @message
@(userForm:Form[User]) @import helper._ @helper.form(action=routes.Application.submit){ @inputText( userForm("id"), '_label -> "Username", '_help -> "Please choose a valid username.", '_error -> userForm.globalError ) @inputPassword( userForm("password"), '_label -> "Password", '_help -> "A password must be at least 6 characters. " ) }
ログイン後1分放置するとログイン画面に戻ります
実際の業務では、サンプルなどでよく見かける、データの作成、更新、参照、削除という流れは実際ではあまりなく、既にあるデータを参照するだけとか祖いうのが多いかと思います。
前回までで、既存データベースに入っているデータを取得することができたので、JSON形式で出力してみます。
sjsonやPlay標準のJSONなど何種類かあるようですが、ちょっと試したところうまくいかなかったので、GSONを使用することにしました。
あらかじめGSONをダウンロードしておきeclipseのパスに追加しておきます。
http://code.google.com/p/google-gson/
use data ; create table price( code varchar(10) not null, date datetime not null, open int , high int, low int, close int ) ; create unique index price1 on price(code,date) ;
db.default.driver=com.mysql.jdbc.Driver db.default.url="jdbc:mysql://localhost/data" db.default.user=root db.default.password= slick.default="models.*" evolutionplugin=disabled
GET /data/price/:code controllers.Data.OHLC(code:String)
package controllers import play.api._ import play.api.mvc._ import models._ import com.google.gson._ object Data extends Controller { val gson:Gson=new GsonBuilder().setDateFormat("yyyy-MM-dd").create // 日付のフォーマットをしておく def OHLC(code:String)=Action { Ok(views.html.json(gson.toJson(Prices.select(code)))) } }
package models import scala.slick.driver.H2Driver.simple._ import Database.threadLocalSession import play.api.db._ import play.api.Play._ import scala.slick.jdbc.{GetResult, StaticQuery => Q} import Q.interpolation case class Price(code:String,date:java.sql.Date,open:Int,high:Int,low:Int,close:Int) extends Ordered[Price]{ def compare(that:Price):Int={ date.getTime().compare(that.date.getTime()) } } object Prices extends Table[Price]("price") { val DATE_TERM=180 // 180日分とる def code=column[String]("code",O.PrimaryKey) def date=column[java.sql.Date]("date",O.PrimaryKey) def open=column[Int]("open") def high=column[Int]("high") def low=column[Int]("low") def close=column[Int]("close") def * = code ~ date ~ open ~ high ~ low ~ close <> (Price,Price.unapply _) implicit val getPrice=GetResult(rs=>Price(rs<<,rs<<,rs<<,rs<<,rs<<,rs<<)) def select(code:String)= connectDB{ val list=sql"select * from price where code=$code order by date desc limit $DATE_TERM".as[PriceHistAdj].list.sorted // sort 逆順なので並べ替える、GSON用にArrayList化 val ary=new java.util.ArrayList[Price] for(l<-list)ary.add(l) ary } def connectDB[Any](f: => Any): Any = { Database.forDataSource(DB.getDataSource("data")) withSession { f } } }
jsonp用
@(json:String) callback(@json);
既存のデータベースにアクセスしてみます。
DBのコネクションをPlayFrameworkから与えることでモデル側でコネクションを作成しません
create table OHLC( code varchar(10) not null, date datetime not null, open int null, high int null, low int null, close int null, unique key OHLC1(code,date) )
db.default.driver=org.gjt.mm.mysql.Driver db.default.url="jdbc:mysql://localhost/live?useOldAliasMetadataBehavior=true" db.default.user=root db.default.password="" slick.default="models.*" evolutionplugin=disabled
GET /:code controllers.Application.price(code:String)
package controllers import play.api._ import play.api.mvc._ import play.api.data._ import play.api.data.Forms._ import models._ object Application extends Controller { def price(code:String) = Action{ Ok(views.html.index(Price.select(code))) } }
package models import scala.slick.driver.H2Driver.simple._ import Database.threadLocalSession import play.api.db._ import play.api.Play._ import scala.slick.jdbc.{GetResult, StaticQuery => Q} import Q.interpolation case class Price(code:String,date:java.sql.Date,open:Int,high:Int,low:Int,close:Int) object Prices extends Table[Price]("OHLC"){ def scode=column[String]("code",O.PrimaryKey) def date=column[java.sql.Date]("date",O.PrimaryKey) def open=column[Int]("open") def high=column[Int]("high") def low=column[Int]("low") def close=column[Int]("close") def * = code ~ date ~ open ~ high ~ low ~ close <> (Price,Price.unapply _) implicit val getPrice=GetResult(rs=>Price(rs.nextString,rs.nextDate,rs.nextInt,rs.nextInt,rs.nextInt,rs.nextInt)) def price(code:String):List[Price]= connectDB{ sql"select * from OHLC where code=$code".as[Price].list } def connectDB[Any](f: => Any): Any = { Database.forDataSource(DB.getDataSource("default")) withSession { f } } }
@(prices: List[Price]) @prices.map { price => @price.code, @price.date @price.open
}
ScalaでPlayFrameworkを使う場合にDBアクセスにはanormとslickがあるようです。
slickはscalaQueryと呼ばれていたものが進化した模様。
scala2.9まではscalaQuery,それ以降はslickらしいです
http://d.hatena.ne.jp/tototoshi/20121204/1354615421
Build.scalaに必要なモジュールを書いてインストールするのが一般的らしいですが、うまくいかなかったので手動インストールで作成してみます。
https://github.com/freekh/play-slick
からZIPでダウンロードし解凍
$ cd slick-1.0.1 $ sbt > compile > package $ find .|grep jar ./slick-testkit/target/scala-2.10/slick-testkit_2.10-1.0.1.jar ./target/root/scala-2.10/root_2.10-1.0.1.jar ./target/scala-2.10/slick_2.10-1.0.1.jar
プラグインなのでこちらも入れます
https://github.com/freekh/play-slick
$ cd play-slick-master $ sbt > compile > package $ find .|grep jar ./target/scala-2.10/play-slick_2.10-0.3.3-SNAPSHOT.jar
$ play new sample _ _ _ __ | | __ _ _ _| | | '_ \| |/ _' | || |_| | __/|_|\____|\__ (_) |_| |__/ play! 2.1.2 (using Java 1.7.0_25 and Scala 2.10.0), http://www.playframework.org The new application will be created in /Users/admin/Documents/workspace/play_sample/sample What is the application name? [sample] > sample Which template do you want to use for this new application? 1 - Create a simple Scala application 2 - Create a simple Java application > 1 OK, application sample is created. Have fun! $ cd sample $ play eclipse $ mkdir lib $ cp slick-1.0.1/target/scala-2.10/slick_2.10-1.0.1.jar . $ cp play-slick-master/target/scala-2.10/play-slick_2.10-0.3.3-SNAPSHOT.jar .
http://takashima0411.hatenablog.com/entry/2012/11/28/231817
こちらの方のサンプルをそのままいただきました
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) } }
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 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: List[Task], taskForm: Form[String]) @import helper._@tasks.size task(s)
# Routes # This file defines all application routes (Higher priority routes first) # ~~~~ # Home page GET / controllers.Application.index GET /index controllers.Application.index GET /tasks controllers.Application.tasks GET /newTask controllers.Application.newTask GET /deleteTask/:id controllers.Application.deleteTask(id:Long) # Map static resources from the /public folder to the /assets URL path GET /assets/*file controllers.Assets.at(path="/public", file)
db.default.driver=org.h2.Driver db.default.url="jdbc:h2:mem:play" slick.default="models.*" # Evolutions # ~~~~~ # You can disable evolutions if needed evolutionplugin=disabled
これでとりあえず動きます