Skip to content

Commit 40e4934

Browse files
committed
first commit
0 parents  commit 40e4934

File tree

14 files changed

+1333
-0
lines changed

14 files changed

+1333
-0
lines changed

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
project/project
2+
project/target
3+
target

README.md

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Scala ActiveRecord #
2+
3+
Version 0.1

build.sbt

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
sbtPlugin := true
2+
3+
name := "scala-activerecord"
4+
5+
version := "0.1-SNAPSHOT"
6+
7+
organization := "com.github.aselab"
8+
9+
libraryDependencies ++= Seq(
10+
"org.squeryl" %% "squeryl" % "0.9.5-RC1",
11+
"com.typesafe.config" % "config" % "0.3.0",
12+
"org.specs2" %% "specs2" % "1.9" % "test",
13+
"c3p0" % "c3p0" % "0.9.1.2",
14+
"com.h2database" % "h2" % "1.3.157" % "test"
15+
)
16+
17+
resolvers ++= Seq(
18+
"Typesafe Repository" at "http://repo.typesafe.com/typesafe/releases/"
19+
)
20+
21+
scalacOptions ++= Seq("-deprecation", "-unchecked")
22+
23+
parallelExecution in Test := false
24+
25+
fork in Test := false

project/build.properties

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
sbt.version=0.11.2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.github.aselab.activerecord.annotations;
2+
3+
import java.lang.annotation.*;
4+
5+
@Retention(RetentionPolicy.RUNTIME)
6+
@Target( { ElementType.FIELD })
7+
public @interface Ignore {
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.github.aselab.activerecord.annotations;
2+
3+
import java.lang.annotation.*;
4+
5+
@Retention(RetentionPolicy.RUNTIME)
6+
@Target( { ElementType.FIELD })
7+
public @interface Unique {
8+
}

src/main/scala/CRUDable.scala

+156
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
package com.github.aselab.activerecord
2+
3+
import org.squeryl.annotations.Transient
4+
5+
/**
6+
* CRUDとコールバックのインタフェース
7+
*/
8+
trait CRUDable {
9+
/** 新規インスタンスフラグ */
10+
@Transient
11+
protected var _isNewInstance = true
12+
13+
/** 新規インスタンスフラグ */
14+
def isNewInstance = _isNewInstance
15+
16+
/**
17+
* 保存メソッド.
18+
*
19+
* 新規インスタンスの時はdoCreateを,
20+
* そうでなければdoUpdateを呼び出す.
21+
* 保存前と保存後にコールバックメソッドを呼び出す.
22+
*/
23+
def save(): Boolean = {
24+
val onCreate = isNewInstance
25+
26+
if (onCreate) beforeCreate() else beforeUpdate()
27+
beforeSave()
28+
29+
val result = if (isNewInstance) doCreate() else doUpdate()
30+
31+
if (result) {
32+
if (onCreate) afterCreate() else afterUpdate()
33+
afterSave()
34+
_isNewInstance = false
35+
}
36+
result
37+
}
38+
39+
/**
40+
* 削除メソッド.
41+
* 削除前と削除後にコールバックメソッドを呼び出す.
42+
*/
43+
def delete(): Boolean = !isNewInstance && {
44+
beforeDelete()
45+
46+
val result = doDelete()
47+
48+
if (result) {
49+
afterDelete()
50+
_isNewInstance = true
51+
}
52+
result
53+
}
54+
55+
/**
56+
* 新規保存処理を行うメソッド.
57+
* 保存に成功した場合trueを,失敗した場合falseを返すように実装する.
58+
*/
59+
protected def doCreate(): Boolean
60+
61+
/**
62+
* 更新保存処理を行うメソッド.
63+
* 更新に成功した場合trueを,失敗した場合falseを返すように実装する.
64+
*/
65+
protected def doUpdate(): Boolean
66+
67+
/**
68+
* 削除処理を行うメソッド.
69+
* 削除に成功した場合trueを,失敗した場合falseを返すように実装する.
70+
*/
71+
protected def doDelete(): Boolean
72+
73+
/**
74+
* 保存前のコールバックメソッド.
75+
* デフォルトでは何もしないため,必要があれば派生クラスで
76+
* オーバーライドして実装する.
77+
*
78+
* saveメソッドが呼び出された時,実際の保存メソッドである
79+
* doCreate, doUpdateを実行する前に呼びだされる.
80+
*/
81+
protected def beforeSave() {}
82+
83+
/**
84+
* 保存後のコールバックメソッド.
85+
* デフォルトでは何もしないため,必要があれば派生クラスで
86+
* オーバーライドして実装する.
87+
*
88+
* saveメソッドが呼び出された時,実際の保存メソッドである
89+
* doCreate, doUpdateを実行した後に呼びだされる.
90+
* 保存に失敗した場合は呼び出されない.
91+
*/
92+
protected def afterSave() {}
93+
94+
/**
95+
* 新規作成前のコールバックメソッド.
96+
* デフォルトでは何もしないため,必要があれば派生クラスで
97+
* オーバーライドして実装する.
98+
*
99+
* saveメソッドが呼び出された時,実際の保存メソッドである
100+
* doCreateを実行する前に呼びだされる.
101+
*/
102+
protected def beforeCreate() {}
103+
104+
/**
105+
* 新規作成後のコールバックメソッド.
106+
* デフォルトでは何もしないため,必要があれば派生クラスで
107+
* オーバーライドして実装する.
108+
*
109+
* saveメソッドが呼び出された時,実際の保存メソッドである
110+
* doCreateを実行した後に呼びだされる.
111+
* 保存に失敗した場合は呼び出されない.
112+
*/
113+
protected def afterCreate() {}
114+
115+
/**
116+
* 更新前のコールバックメソッド.
117+
* デフォルトでは何もしないため,必要があれば派生クラスで
118+
* オーバーライドして実装する.
119+
*
120+
* saveメソッドが呼び出された時,実際の保存メソッドである
121+
* doUpdateを実行する前に呼びだされる.
122+
*/
123+
protected def beforeUpdate() {}
124+
125+
/**
126+
* 更新後のコールバックメソッド.
127+
* デフォルトでは何もしないため,必要があれば派生クラスで
128+
* オーバーライドして実装する.
129+
*
130+
* saveメソッドが呼び出された時,実際の保存メソッドである
131+
* doUpdateを実行した後に呼びだされる.
132+
* 保存に失敗した場合は呼び出されない.
133+
*/
134+
protected def afterUpdate() {}
135+
136+
/**
137+
* 削除前のコールバックメソッド.
138+
* デフォルトでは何もしないため,必要があれば派生クラスで
139+
* オーバーライドして実装する.
140+
*
141+
* deleteメソッドが呼び出された時,実際の削除メソッドである
142+
* doDeleteを実行する前に呼びだされる.
143+
*/
144+
protected def beforeDelete() {}
145+
146+
/**
147+
* 削除後のコールバックメソッド.
148+
* デフォルトでは何もしないため,必要があれば派生クラスで
149+
* オーバーライドして実装する.
150+
*
151+
* deleteメソッドが呼び出された時,実際の削除メソッドである
152+
* doDeleteを実行した後に呼びだされる.
153+
* 削除に失敗した場合は呼び出されない.
154+
*/
155+
protected def afterDelete() {}
156+
}

src/main/scala/annotations.scala

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package com.github.aselab.activerecord
2+
3+
import org.squeryl.annotations._
4+
5+
/** アノテーション定義 */
6+
object Annotations {
7+
import annotation.target._
8+
9+
/**
10+
* 無視フィールドアノテーション.
11+
* フォーム検証対象外のフィールドに付加する.
12+
*/
13+
type Ignore = annotations.Ignore @field
14+
15+
/**
16+
* ユニークアノテーション.
17+
*/
18+
type Unique = annotations.Unique @field
19+
}

src/main/scala/reflections.scala

+104
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
package com.github.aselab.activerecord
2+
3+
import java.lang.annotation.Annotation
4+
import java.lang.reflect.Field
5+
import annotations._
6+
7+
/**
8+
* フィールド情報モデル
9+
* @param name フィールド名
10+
* @param fieldType フィールドタイプ
11+
* @param isOption Option型かどうか
12+
* @param isSeq シーケンスかどうか
13+
* @param annotations アノテーションリスト
14+
*/
15+
case class FieldInfo(
16+
name: String, fieldType: Class[_],
17+
isOption: Boolean, isSeq: Boolean,
18+
annotations: Seq[Annotation] = Nil
19+
) {
20+
private lazy val annotationMap = annotations.map {
21+
a => (a.annotationType.getSimpleName, a)
22+
}.toMap
23+
24+
/** Ignoreフィールドかどうか */
25+
lazy val ignored = annotationMap.isDefinedAt("Ignore")
26+
/** Uniqueフィールドかどうか */
27+
lazy val unique = annotationMap.isDefinedAt("Unique")
28+
}
29+
30+
/** フィールド情報オブジェクト */
31+
object FieldInfo {
32+
def apply(name: String, value: Any, field: Option[Field]): FieldInfo = value match {
33+
case Some(v) => apply(name, v, None).copy(isOption = true)
34+
case None => throw ConventionException.optionValueMustBeSome
35+
36+
case l: Traversable[_] => l.toSeq match {
37+
case Seq(v, _*) => apply(name, v, None).copy(isSeq = true)
38+
case Nil => throw ConventionException.traversableValueMustNotBeNil
39+
}
40+
41+
case v: Any => FieldInfo(name, v.getClass, false, false)
42+
case v =>
43+
val fieldType = field.map(_.getType).getOrElse(
44+
throw ConventionException.cannotDetectType(v))
45+
FieldInfo(name, fieldType, false, false)
46+
}
47+
48+
def apply(name: String, value: Any): FieldInfo = apply(name, value, None)
49+
50+
def apply(field: Field, value: Any): FieldInfo =
51+
apply(field.getName, value, Some(field)).copy(annotations = field.getAnnotations.toSeq)
52+
}
53+
54+
/** リフレクション用ユーティリティ */
55+
trait ReflectionUtil {
56+
/**
57+
* クラスからコンパニオンオブジェクトを返す.
58+
* @param className クラス名
59+
*/
60+
def classToCompanion(className: String): Any = {
61+
val cc = Class.forName(className + "$")
62+
cc.getField("MODULE$").get(cc)
63+
}
64+
65+
/**
66+
* クラスからコンパニオンオブジェクトを返す.
67+
* @param c クラス
68+
*/
69+
def classToCompanion(c: Class[_]): Any = classToCompanion(c.getName)
70+
71+
/**
72+
* クラスからコンパニオンオブジェクトを返す.
73+
* @param c 任意のオブジェクト
74+
*/
75+
def companionToClass(c: Any) = Class.forName(c.getClass.getName.dropRight(1))
76+
77+
/**
78+
* 任意のオブジェクトのフィールド値をリフレクションで取得/変更できるようにする
79+
* 暗黙変換メソッド.
80+
*/
81+
implicit def toReflectable(o: Any) = new {
82+
val c = o.getClass
83+
84+
/**
85+
* 値を取得する.
86+
* @param name フィールド名
87+
*/
88+
def getValue[T](name: String) = c.getMethod(name).invoke(o).asInstanceOf[T]
89+
90+
/**
91+
* 値を設定する.
92+
* @param name フィールド名
93+
* @param value 設定する値
94+
*/
95+
def setValue(name: String, value: Any) = {
96+
val f = c.getDeclaredField(name)
97+
f.setAccessible(true)
98+
f.set(o, value)
99+
}
100+
}
101+
}
102+
103+
/** リフレクション用ユーティリティ */
104+
object ReflectionUtil extends ReflectionUtil

0 commit comments

Comments
 (0)