Skip to content

Instantly share code, notes, and snippets.

@ToniNgethe
Last active December 28, 2022 06:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ToniNgethe/4d2af6374f030c0a0920bacc6c587aa0 to your computer and use it in GitHub Desktop.
Save ToniNgethe/4d2af6374f030c0a0920bacc6c587aa0 to your computer and use it in GitHub Desktop.
A simple key-value plain database for your demo kotlin based projects. Uses .json file for storing the data
data class DataBaseVersion(
val version: String,
val dataModified: String
)
interface JsonParser {
fun <T> parseToString(data: T): String
fun <T> parseToObject(string: String, type: Type): T
fun <T> parseToObject(string: String, classOfT: Class<T>): T
}
class JsonParserImpl : JsonParser {
private val gson = Gson()
override fun <T> parseToString(data: T): String = gson.toJson(data)
override fun <T> parseToObject(string: String, type: Type): T {
return gson.fromJson(string, type)
}
override fun <T> parseToObject(string: String, classOfT: Class<T>): T {
return gson.fromJson(string, classOfT)
}
}
class Database : JsonParser by JsonParserImpl() {
private var dataBaseName = "database"
lateinit var databaseFile: File
private set
class DataBaseBuilder() {
private val database = Database()
fun setDbName(name: String): DataBaseBuilder {
database.dataBaseName = name
return this
}
fun buildDb(): Database {
database.syncDatabase()
return database
}
}
// check if file exists
// 1. if not, create it
// 2. if available continue
private fun syncDatabase() {
try {
databaseFile = File("$dataBaseName.json")
if (databaseFile.exists()) {
println("Database already exists")
return
}
val createFile = databaseFile.createNewFile()
if (createFile) {
val databaseVersion = DataBaseVersion(version = "1.0.0", dataModified = "${Date().time}")
writeToFile("database", parseToString(databaseVersion))
println("Database sync was a success")
} else
throw Exception("Unable to create database file")
} catch (e: Exception) {
println("Unable to sync database: ${e.toString()}")
}
}
// convert data of type T to json string
// then store in terms of string
inline fun <reified T> writeData(key: String, data: T) {
try {
if (databaseFile == null) {
throw Exception("Database file is not yet set up")
}
writeToFile(key, parseToString(data))
println("Data saved")
} catch (e: Exception) {
e.printStackTrace()
println("Unable to write data")
}
}
inline fun <reified T> getDataUsingKey(key: String): T? {
return try {
val allData = readAllData()
val data = allData[key]
if (data != null) {
parseToObject(data, T::class.java)
} else
null
} catch (e: Exception) {
null
}
}
inline fun <reified T> getListOUsingKey(key: String): ArrayList<T>? {
return try {
val allData = readAllData()
val data = allData[key]
if (data != null) {
val type = TypeToken.getParameterized(ArrayList::class.java, T::class.java).type
Gson().fromJson<ArrayList<T>>(data, type)
} else
null
} catch (e: Exception) {
null
}
}
@Synchronized
fun writeToFile(key: String, dataToWrite: String) {
var fileWriter: OutputStreamWriter? = null
var bufferWriter: BufferedWriter? = null
try {
var previousData = readAllData()
previousData[key] = dataToWrite
fileWriter = databaseFile.writer()
bufferWriter = BufferedWriter(fileWriter)
bufferWriter.write(parseToString(previousData))
bufferWriter.flush()
} catch (e: Exception) {
e.printStackTrace()
} finally {
try {
fileWriter?.close()
bufferWriter?.close()
} catch (e: Exception) {
e.printStackTrace()
}
}
}
@Synchronized
fun readAllData(): HashMap<String, String> {
var bufferedReader: BufferedReader? = null
return try {
bufferedReader = databaseFile.bufferedReader()
val data = bufferedReader.readText()
if (data.isEmpty() || data.isBlank()) {
HashMap<String, String>()
} else {
val type = object : TypeToken<HashMap<String, String>>() {}.type
parseToObject(data, type)
}
} catch (e: Exception) {
e.printStackTrace()
HashMap<String, String>()
} finally {
bufferedReader?.close()
}
}
}
class DatabaseTest {
private lateinit var database: Database
@BeforeEach
fun setUp() {
database = Database.DataBaseBuilder()
.setDbName("test")
.buildDb()
}
@AfterEach
fun tearDown() {
database.databaseFile.delete()
}
@Test
fun `should pass given the database file was created`() {
assertNotNull(database.databaseFile)
}
private val mockData = JsonObject().apply {
addProperty("name", "Florence")
addProperty("age", "90")
}
@Test
fun `should pass given single data is inserted successfully`() {
database.writeData<JsonObject>("user", mockData)
val data = database.getDataUsingKey<JsonObject>("user")
assertNotNull(data)
assertEquals(data?.get("name")?.asString, "Florence")
}
@Test
fun `should return null given no data is available by that key`() {
val data = database.getDataUsingKey<JsonObject>("user_1")
assertNull(data)
}
@Test
fun `should return list of objects given its available`() {
val mockDataList = listOf<JsonObject>(mockData)
database.writeData("user_1", mockDataList)
val dataList = database.getListOUsingKey<JsonObject>("user_1")
assertNotNull( dataList )
assertEquals( dataList?.size, 1 )
assertContains( dataList!!, mockData )
}
}
data class Person(
val name: String, val age: Int
)
fun main(args: Array<String>) {
val database = Database.DataBaseBuilder()
.setDbName("bobo_res")
.buildDb()
database.writeData("person", listOf( Person(name = "toni", age = 22) ))
database.writeData("user", Person(name = "toni", age = 22))
val user = database.getDataUsingKey<Person>("user")
println( user?.name )
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment