# Repositories can be used in Popcorn app to manage your data persistence.
# Here an example with a book management app:
#
-# ~~~
+# ~~~nitish
# # First we declare the `Book` class. It has to be serializable so it can be used
# # within a `Repository`.
#
# import popcorn
# import popcorn::pop_repos
+# import popcorn::pop_json
#
# # Serializable book representation.
# class Book
# serialize
-# super Jsonable
#
# # Book uniq ID
# var id: String = (new MongoObjectId).id is serialize_as "_id"
# redef fun to_s do return title
# redef fun ==(o) do return o isa SELF and id == o.id
# redef fun hash do return id.hash
-# redef fun to_json do return serialize_to_json
# end
#
# # We then need to subclass the `MongoRepository` to provide Book specific services.
# # Let's wrap it all together in a Popcorn app:
#
# # Init database
-# var mongo = new MongoClient("mongodb://localhost:27017/")
+# var mongo = new MongoClient("mongodb://mongo:27017/")
# var db = mongo.database("tests_app_{100000.rand}")
# var coll = db.collection("books")
#
# ~~~
module pop_repos
+import popcorn::pop_config
import serialization
-import json::serialization
+import json
import mongodb::queries
+redef class AppConfig
+
+ # Default database host string for MongoDb
+ var default_db_host = "mongodb://mongo:27017/"
+
+ # Default database hostname
+ var default_db_name = "popcorn"
+
+ # MongoDb host name
+ var opt_db_host = new OptionString("MongoDb host", "--db-host")
+
+ # MongoDb database name
+ var opt_db_name = new OptionString("MongoDb database name", "--db-name")
+
+ # MongoDB server used for data persistence
+ fun db_host: String do return opt_db_host.value or else ini["db.host"] or else default_db_host
+
+ # MongoDB DB used for data persistence
+ fun db_name: String do return opt_db_name.value or else ini["db.name"] or else default_db_name
+
+ init do
+ super
+ add_option(opt_db_host, opt_db_name)
+ end
+
+ # Mongo db client
+ var client = new MongoClient(db_host) is lazy
+
+ # Mongo db instance
+ var db: MongoDb = client.database(db_name) is lazy
+end
+
# A Repository is an object that can store serialized instances.
#
# Repository is the base class of all kind of persistance processes. It offers
# Find all instances based on `query`
#
# Using `query` == null will retrieve all the document in the repository.
- fun find_all(query: nullable QUERY): Array[E] is abstract
+ fun find_all(query: nullable QUERY, skip, limit: nullable Int): Array[E] is abstract
+
+ # Count instances that matches `query`
+ fun count(query: nullable QUERY): Int is abstract
# Save an `instance`
fun save(instance: E): Bool is abstract
# Remove the instance based on `query`
fun remove(query: nullable QUERY): Bool is abstract
+ # Remove all the instances matching on `query`
+ fun remove_all(query: nullable QUERY): Bool is abstract
+
# Remove all instances
fun clear: Bool is abstract
# A Repository that uses MongoDB as backend.
#
-# ~~~
+# ~~~nitish
# import popcorn
-# import popcorn::pop_routes
+# import popcorn::pop_repos
+# import popcorn::pop_json
#
# # First, let's create a User abstraction:
#
# # Serializable user representation.
# class User
+# super RepoObject
# serialize
-# super Jsonable
-#
-# # User uniq ID
-# var id: String = (new MongoObjectId).id is serialize_as "_id"
#
# # User login
# var login: String
# var password: String is writable
#
# redef fun to_s do return login
-# redef fun ==(o) do return o isa SELF and id == o.id
-# redef fun hash do return id.hash
-# redef fun to_json do return serialize_to_json
# end
#
# # We then need to subclass the `MongoRepository` to provide User specific services:
# # The repository can then be used with User instances:
#
# # Init database
-# var mongo = new MongoClient("mongodb://localhost:27017/")
+# var mongo = new MongoClient("mongodb://mongo:27017/")
# var db = mongo.database("tests")
# var coll = db.collection("test_pop_repo_{100000.rand}")
#
return deserialize(res.to_json)
end
- redef fun find_all(query) do
+ redef fun find_all(query, skip, limit) do
var res = new Array[E]
- for e in collection.find_all(query or else new JsonObject) do
+ for e in collection.find_all(query or else new JsonObject, skip, limit) do
res.add deserialize(e.to_json).as(E)
end
return res
end
+ redef fun count(query) do
+ return collection.count(query or else new JsonObject)
+ end
+
redef fun save(item) do
var json = serialize(item).as(String)
var obj = json.parse_json.as(JsonObject)
return collection.remove(query or else new JsonObject)
end
+ redef fun remove_all(query) do
+ return collection.remove_all(query or else new JsonObject)
+ end
+
redef fun clear do return collection.drop
# Perform an aggregation query over the repo.
end
end
+# Base serializable entity that can go into a JsonRepository
+#
+# Provide boiler plate implementation of all object serializable to json.
+#
+# `id` is used as a primary key for `find_by_id`.
+#
+# Subclassing RepoObject makes it easy to create a serializable class:
+# ~~~
+# import popcorn::pop_repos
+#
+# class Album
+# super RepoObject
+# serialize
+#
+# var title: String
+# var price: Float
+# end
+# ~~~
+#
+# Do not forget the `serialize` annotation else the fields will not be serialized.
+#
+# It is also possible to redefine the `id` primary key to use your own:
+# ~~~
+# import popcorn::pop_repos
+#
+# class Order
+# super RepoObject
+# serialize
+#
+# redef var id = "order-{get_time}"
+#
+# # ...
+#
+# end
+# ~~~
+abstract class RepoObject
+ serialize
+
+ # `self` unique id.
+ #
+ # This attribute is serialized under the key `_id` to be used
+ # as primary key by MongoDb
+ var id: String = (new MongoObjectId).id is writable, serialize_as "_id"
+
+ # Base object comparison on ID
+ #
+ # Because multiple deserialization can exists of the same instance,
+ # we use the ID to determine if two object are the same.
+ redef fun ==(o) do return o isa SELF and id == o.id
+
+ redef fun hash do return id.hash
+ redef fun to_s do return id
+end
+
# JsonObject can be used as a `RepositoryQuery`.
#
# See `mongodb` lib.