From 08d4c384393f683d7b9eca0b003a2d4590839b86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=AAnnis=20Dantas?= Date: Mon, 16 Apr 2018 13:07:27 -0300 Subject: [PATCH] refactor(project): Move model and handler to mongo :art: I've refactored the project, putting model and handler package with the other things on mongo package. That makes the job of using the package much easier. --- README.md | 210 +++++++++++++++++-- Taskfile.yml | 16 +- acceptance_test.go | 38 +++- connect.go | 2 + connect_test.go | 4 +- doc.go | 241 ++++++++++++++++++++++ doc_mongo.go | 61 ------ model/documenter.go => documenter.go | 2 +- model/model_test.go => documenter_test.go | 2 +- handler/handle.go => handle.go | 19 +- handler/handle_test.go => handle_test.go | 11 +- handler/README.md | 117 ----------- handler/doc_handle.go | 104 ---------- handler/helpers_test.go | 234 --------------------- helpers_test.go | 97 +++++---- mock_connect.go => mock.go | 130 +++++++++++- model/README.md | 94 --------- model/doc_model.go | 94 --------- model/helpers_test.go | 98 --------- model/mock_util.go | 133 ------------ model/util.go => util.go | 2 +- 21 files changed, 679 insertions(+), 1030 deletions(-) create mode 100644 doc.go delete mode 100644 doc_mongo.go rename model/documenter.go => documenter.go (97%) rename model/model_test.go => documenter_test.go (99%) rename handler/handle.go => handle.go (87%) rename handler/handle_test.go => handle_test.go (98%) delete mode 100644 handler/README.md delete mode 100644 handler/doc_handle.go delete mode 100644 handler/helpers_test.go rename mock_connect.go => mock.go (53%) delete mode 100644 model/README.md delete mode 100644 model/doc_model.go delete mode 100644 model/helpers_test.go delete mode 100644 model/mock_util.go rename model/util.go => util.go (98%) diff --git a/README.md b/README.md index 0298353..a5b004c 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,10 @@ go get github.com/ddspog/mongo ## How to use +This package mask the connection to MongoDB using mgo package. + +This is made with function Connect, that saves Session and Mongo object which will be used later from other packages. Also, I've embedded the Collection, Database and Query types, to allow mocking via interfaces. The embedded was necessary for the functions to use the interfaces as return values, that way, the code can use the original, or generate a mock of them for testing purposes. + The package can be used like this: ```go @@ -47,7 +51,7 @@ variable, but when it's not defined, it uses a default URL: mongodb://localhost:27017/test -## Mocking +### Mocking You can mock some functions of this package, by mocking the mgo called functions mgo.ParseURL and mgo.Dial. Use the MockMongoSetup @@ -66,28 +70,208 @@ if err := mongo.Connect(); err != nil { } ``` +## Documenter + +Mongo package also contain utility functions to help modeling documents. + +The package contains a interface Documenter which contain getters for important attributes to any document on MongoDB: _id, created_on and updated_on. It also contains functions that generates correctly the created_on and updated_on attributes. + +The Documenter can be used like this: + +```go +// Create a type representing the Document type +type Product struct { + IDV bson.ObjectId `json:"_id,omitempty" bson:"_id,omitempty"` + CreatedOnV int64 `json:"created_on,omitempty" bson:"created_on,omitempty"` + UpdatedOnV int64 `json:"updated_on,omitempty" bson:"updated_on,omitempty"` + NameV string `json:"name" form:"name" binding:"required" bson:"name"` + PriceV float32 `json:"price" form:"price" binding:"required" bson:"price"` +} + +// Implement the Documenter interface. +func (p *Product) ID() (id bson.ObjectId) { + id = p.IDV + return +} + +func (p *Product) CreatedOn() (t int64) { + t = p.CreatedOnV + return +} + +func (p *Product) UpdatedOn() (t int64) { + t = p.UpdatedOnV + return +} + +func (p *Product) New() (doc mongo.Documenter) { + doc = &Product{} + return +} + +// On these methods, you can use the functions implemented mongo +// package. +func (p *Product) Map() (out bson.M, err error) { + out, err = mongo.MapDocumenter(p) + return +} + +func (p *Product) Init(in bson.M) (err error) { + var doc mongo.Documenter = p + err = mongo.InitDocumenter(in, &doc) + return +} + +func (p *Product) GenerateID() { + p.IDV = mongo.NewID() +} + +func (p *Product) CalculateCreatedOn() { + p.CreatedOnV = mongo.NowInMilli() +} + +func (p *Product) CalculateUpdatedOn() { + p.UpdatedOnV = mongo.NowInMilli() +} + +// Create a product variable, and try its methods. +p := Product{} +p.CalculateCreatedOn() +t := p.CreatedOn() +``` + +You can also mock some other functions of this package, by mocking some called functions time.Now and bson.NewObjectId. Use the MockModelSetup presented on this package (only in test environment), like: + +```go +create, _ := mongo.NewMockModelSetup(t) +defer create.Finish() + +create.Now().Returns(time.Parse("02-01-2006", "22/12/2006")) +create.NewID().Returns(bson.ObjectIdHex("anyID")) + +var d mongo.Documenter +// Call any needed methods ... +d.GenerateID() +d.CalculateCreatedOn() +``` + +## Handle + +Mongo package also enable creation of Handle, a type that connects to database collections and do some operations. + +The Handle were made to be imported on embedding type, and through overriding of some methods, to implement an adequate Handler for a desired type of Document. The Handle type assumes to operate on a Documenter type, that will contain information about the operation to made with Handle. + +The package should be used to create new types. Use the Handler type for creating embedding types. + +```go +type ProductHandle struct { + *mongo.Handle + DocumentV *product.Product +} +``` + +For each new type, a constructor may be needed, and for that Handler has a basic constructor. + +```go +func New() (p *ProductHandle) { + p = &ProductHandle{ + Handle: mongo.NewHandle(), + DocumentV: product.New(), + } + return +} +``` + +All functions were made to be overridden and rewrite. First thing to do it's creating a Name function. + +```go +func (p *ProductHandle) Name() (n string) { + n = "products" + return +} +``` + +With Name function, the creation of Link method goes as it follows: + +```go +func (p *ProductHandle) Link(db mongo.Databaser) (h *ProductHandle) { + p.Handle.Link(db, p.Name()) + h = p + return +} +``` + +The creation of Insert, Remove and RemoveAll are trivial. Call it with a Document getter function defined like: + +```go +func (p *ProductHandle) Document() (d *product.Product) { + d = p.DocumentV + return +} + +func (p *ProductHandle) Insert() (err error) { + err = p.Handle.Insert(p.Document()) + return +} +``` + +The Clean function is simple and helps a lot: + +```go +func (p *ProductHandle) Clean() { + p.Handle.Clean() + p.DocumentV = product.New() +} +``` + +The Update function uses an id as an argument: + +```go +func (p *ProductHandle) Update(id bson.ObjectId) (err error) { + err = p.Handle.Update(id, p.Document()) + return +} +``` + +The complicated functions are Find and FindAll which requires casting for the Document type: + +```go +func (p *ProductHandle) Find() (prod *product.Product, err error) { + var doc mongo.Documenter = product.New() + err = p.Handle.Find(p.Document(), doc) + prod = doc.(*product.Product) + return +} + +func (p *ProductHandle) FindAll() (proda []*product.Product, err error) { + var da []mongo.Documenter + err = p.Handle.FindAll(p.Document(), &da) + proda = make([]*product.Product, len(da)) + for i := range da { + //noinspection GoNilContainerIndexing + proda[i] = da[i].(*product.Product) + } + return +} +``` + +For all functions written, verification it's advisable. + ## Testing -This package contains a nice coverage with the unit tests, within the -objectives of the project. +This package contains a nice coverage with the unit tests, within the objectives of the project. -The elements, embedded and mocks sub-packages have low coverage because -they fulfill a need to mock mgo elements. These packages only embedded -mgo objects to mock, and by this a lot of unused functions were created -to fulfill interface requisites. +The elements, embedded and mocks sub-packages have low coverage because they fulfill a need to mock mgo elements. These packages only embedded mgo objects to mock, and by this a lot of unused functions were created to fulfill interface requisites. -On the other hand, model, handler and mongo package have full coverage, -being the focus of this project. +On the other hand, model, handler and mongo package have full coverage, being the focus of this project. -The project also contains a set of acceptance tests. I've have set the -test-acceptance task with the commands to run it. These tests requires -a mongo test database to be available. It creates, search and remove -elements from it, being reusable without broking the database. +The project also contains a set of acceptance tests. I've have set the test-acceptance task with the commands to run it. These tests requires a mongo test database to be available. It creates, search and remove elements from it, being reusable without broking the database. ## Contribution This package has some objectives from now: * Being incorporate on mgo package (possible fork) on possible future. +* Incorporate any new ideas about possible improvements. Any interest in help is much appreciated. \ No newline at end of file diff --git a/Taskfile.yml b/Taskfile.yml index 00c0211..a3eb5d6 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -1,17 +1,3 @@ -test-handler: - desc: Run handler tests. - cmds: - - echo "Calling tests handler execution ..." - - go test {{.REPO_PATH}}/handler -v --cover - silent: true - -test-model: - desc: Run model tests. - cmds: - - echo "Calling tests model execution ..." - - go test {{.REPO_PATH}}/model -v --cover - silent: true - test-acceptance: desc: Run acceptance tests with a real mongo instance running. cmds: @@ -34,7 +20,7 @@ cover: - go tool cover -html=coverage.out test: - deps: [test-handler, test-model, test-mongo] + deps: [test-mongo] desc: Run all tests. gen-mock-collection: diff --git a/acceptance_test.go b/acceptance_test.go index f8672a4..a6529bb 100644 --- a/acceptance_test.go +++ b/acceptance_test.go @@ -7,7 +7,7 @@ import ( "os" "testing" - "github.com/ddspog/mongo/model" + "github.com/ddspog/mongo/elements" "github.com/ddspog/mspec/bdd" "github.com/globalsign/mgo/bson" ) @@ -20,6 +20,37 @@ const ( testid04 = "000000007465737469643034" ) +func newDBSocket() (db databaseSocketer) { + db = &databaseSocket{ + db: make(chan elements.Databaser), + quit: make(chan bool), + } + return +} + +type databaseSocketer interface { + DB() elements.Databaser + Close() +} + +type databaseSocket struct { + db chan elements.Databaser + quit chan bool +} + +func (d *databaseSocket) DB() (db elements.Databaser) { + go ConsumeDatabaseOnSession(func(db elements.Databaser) { + d.db <- db + <-d.quit + }) + + return <-d.db +} + +func (d *databaseSocket) Close() { + d.quit <- true +} + // Feature Manipulate data on MongoDB // - As a developer, // - I want to be able to connect and manipulate data from MongoDB, @@ -132,7 +163,7 @@ func Test_Manipulate_data_on_MongoDB(t *testing.T) { }) }) - now := model.NowInMilli() + now := NowInMilli() when(fmt.Sprintf("using p.Update() to change created_on doc with id '%[1]s' to '%[2]v'", newId.Hex(), now), func(it bdd.It) { p.DocumentV = newProduct() @@ -219,7 +250,8 @@ func Test_Real_connection_to_MongoDB(t *testing.T) { assert.NotNil(db) }) - p, err := newProductHandle().Link(db) + p := newProductHandle() + err = p.Link(db) it("should link correctly with products collection", func(assert bdd.Assert) { assert.NoError(err) diff --git a/connect.go b/connect.go index b1dbb10..7dfd80d 100644 --- a/connect.go +++ b/connect.go @@ -76,6 +76,8 @@ func Connect() (err error) { return } +// Disconnect undo the connection made. Preparing package for a new +// connection. func Disconnect() { once = *new(sync.Once) session = new(embedded.Session) diff --git a/connect_test.go b/connect_test.go index 90682ca..4886a5a 100644 --- a/connect_test.go +++ b/connect_test.go @@ -46,7 +46,7 @@ func Test_Connection_with_MongoDB(t *testing.T) { ConsumeDatabaseOnSession(func(db elements.Databaser) { p := newProductHandle() - _, errLink = p.Link(db) + errLink = p.Link(db) n, errCount = p.Count() }) @@ -136,7 +136,7 @@ func Test_Connect_only_with_valid_URLs(t *testing.T) { // - As a developer, // - I want that MockMongoSetup returns an error when receiving a nil test element, // - So that I could restrain the use of this Setup only to tests. -func Test_MockModelSetup_works_only_on_Tests(t *testing.T) { +func Test_MockMongoSetup_works_only_on_Tests(t *testing.T) { given, _, _ := bdd.Sentences() given(t, "the start of the test", func(when bdd.When) { diff --git a/doc.go b/doc.go new file mode 100644 index 0000000..ac721bf --- /dev/null +++ b/doc.go @@ -0,0 +1,241 @@ +// Copyright 2009 Dênnis Dantas de Sousa. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* +Package mongo mask the connection to MongoDB using mgo package. + +This is made with function Connect, that saves Session and Mongo object +which will be used later from other packages. Also, I've embedded the +Collection, Database and Query types, to allow mocking via interfaces. +The embedded was necessary for the functions to use the interfaces as +return values, that way, the code can use the original, or generate +a mock of them for testing purposes. + +The package can be used like this: + + // To connect with MongoDB database. + mongo.Connect() + + // Clone the Session generated with Connect method, to allow use + // on other packages. + s := mongo.Session.Clone() + defer s.Close() + + // You can use mgo known functions with mongo.Session() or + // mongo.Mongo(). If you want to use only the Database object to + // handle the operations on MongoDB with a handler, use: + mongo.ConsumeDatabaseOnSession(func(db elements.Databaser) { + // Make db object available on handlers. + mongo.Link(db) + // ... Do other operations. + }) + +Further usage it's the same way mgo package is used. Look into mgo +docs page: https://godoc.org/github.com/globalsign/mgo + +The Connect function tries to connect to a MONGODB_URL environment +variable, but when it's not defined, it uses a default URL: + + mongodb://localhost:27017/severo-rest-db + +You can mock some functions of this package, by mocking the mgo +called functions mgo.ParseURL and mgo.Dial. Use the MockMongoSetup +presented on this package (only in test environment), like: + + create, _ := mongo.NewMockMongoSetup(t) + defer create.Finish() + + create.ParseURL().Returns(db, nil) + create.Dial().Returns(info, nil) + + // Call any preparations on connection ... + if err := mongo.Connect(); err != nil { + t.fail() + } + + +Documenter + +Mongo package also contain utility functions to help modeling documents. + +The package contains a interface Documenter which contain getters for +important attributes to any document on MongoDB: _id, created_on and +updated_on. It also contains functions that generates correctly the +created_on and updated_on attributes. + +The Documenter can be used like this: + + // Create a type representing the Document type + type Product struct { + IDV bson.ObjectId `json:"_id,omitempty" bson:"_id,omitempty"` + CreatedOnV int64 `json:"created_on,omitempty" bson:"created_on,omitempty"` + UpdatedOnV int64 `json:"updated_on,omitempty" bson:"updated_on,omitempty"` + NameV string `json:"name" form:"name" binding:"required" bson:"name"` + PriceV float32 `json:"price" form:"price" binding:"required" bson:"price"` + } + + // Implement the Documenter interface. + func (p *Product) ID() (id bson.ObjectId) { + id = p.IDV + return + } + + func (p *Product) CreatedOn() (t int64) { + t = p.CreatedOnV + return + } + + func (p *Product) UpdatedOn() (t int64) { + t = p.UpdatedOnV + return + } + + func (p *Product) New() (doc mongo.Documenter) { + doc = &Product{} + return + } + + // On these methods, you can use the functions implemented mongo + // package. + func (p *Product) Map() (out bson.M, err error) { + out, err = mongo.MapDocumenter(p) + return + } + + func (p *Product) Init(in bson.M) (err error) { + var doc mongo.Documenter = p + err = mongo.InitDocumenter(in, &doc) + return + } + + func (p *Product) GenerateID() { + p.IDV = mongo.NewID() + } + + func (p *Product) CalculateCreatedOn() { + p.CreatedOnV = mongo.NowInMilli() + } + + func (p *Product) CalculateUpdatedOn() { + p.UpdatedOnV = mongo.NowInMilli() + } + + // Create a product variable, and try its methods. + p := Product{} + p.CalculateCreatedOn() + t := p.CreatedOn() + +You can also mock some other functions of this package, by mocking some +called functions time.Now and bson.NewObjectId. Use the MockModelSetup +presented on this package (only in test environment), like: + + create, _ := mongo.NewMockModelSetup(t) + defer create.Finish() + + create.Now().Returns(time.Parse("02-01-2006", "22/12/2006")) + create.NewID().Returns(bson.ObjectIdHex("anyID")) + + var d mongo.Documenter + // Call any needed methods ... + d.GenerateID() + d.CalculateCreatedOn() + + +Handle + +Mongo package also enable creation of Handle, a type that connects to +database collections and do some operations. + +The Handle were made to be imported on embedding type, and through +overriding of some methods, to implement an adequate Handler for a +desired type of Document. The Handle type assumes to operate on a +Documenter type, that will contain information about the operation +to made with Handle. + +The package should be used to create new types. Use the Handler type +for creating embedding types. + + type ProductHandle struct { + *mongo.Handle + DocumentV *product.Product + } + +For each new type, a constructor may be needed, and for that Handler +has a basic constructor. + + func New() (p *ProductHandle) { + p = &ProductHandle{ + Handle: mongo.NewHandle(), + DocumentV: product.New(), + } + return + } + +All functions were made to be overridden and rewrite. First thing to do +it's creating a Name function. + + func (p *ProductHandle) Name() (n string) { + n = "products" + return + } + +With Name function, the creation of Link method goes as it follows: + + func (p *ProductHandle) Link(db mongo.Databaser) (h *ProductHandle) { + p.Handle.Link(db, p.Name()) + h = p + return + } + +The creation of Insert, Remove and RemoveAll are trivial. Call it with +a Document getter function defined like: + + func (p *ProductHandle) Document() (d *product.Product) { + d = p.DocumentV + return + } + + func (p *ProductHandle) Insert() (err error) { + err = p.Handle.Insert(p.Document()) + return + } + +The Clean function is simple and helps a lot: + + func (p *ProductHandle) Clean() { + p.Handle.Clean() + p.DocumentV = product.New() + } + +The Update function uses an id as an argument: + + func (p *ProductHandle) Update(id bson.ObjectId) (err error) { + err = p.Handle.Update(id, p.Document()) + return + } + +The complicated functions are Find and FindAll which requires casting +for the Document type: + + func (p *ProductHandle) Find() (prod *product.Product, err error) { + var doc mongo.Documenter = product.New() + err = p.Handle.Find(p.Document(), doc) + prod = doc.(*product.Product) + return + } + + func (p *ProductHandle) FindAll() (proda []*product.Product, err error) { + var da []mongo.Documenter + err = p.Handle.FindAll(p.Document(), &da) + proda = make([]*product.Product, len(da)) + for i := range da { + //noinspection GoNilContainerIndexing + proda[i] = da[i].(*product.Product) + } + return + } + +For all functions written, verification it's advisable. +*/ +package mongo diff --git a/doc_mongo.go b/doc_mongo.go deleted file mode 100644 index ac3eb08..0000000 --- a/doc_mongo.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2009 Dênnis Dantas de Sousa. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -/* -Package mongo mask the connection to MongoDB using mgo package. - -This is made with function Connect, that saves Session and Mongo object -which will be used later from other packages. Also, I've embedded the -Collection, Database and Query types, to allow mocking via interfaces. -The embedded was necessary for the functions to use the interfaces as -return values, that way, the code can use the original, or generate -a mock of them for testing purposes. - -Usage - -The package can be used like this: - - // To connect with MongoDB database. - mongo.Connect() - - // Clone the Session generated with Connect method, to allow use - // on other packages. - s := mongo.Session.Clone() - defer s.Close() - - // You can use mgo known functions with mongo.Session() or - // mongo.Mongo(). If you want to use only the Database object to - // handle the operations on MongoDB with a handler, use: - mongo.ConsumeDatabaseOnSession(func(db elements.Databaser) { - // Make db object available on handlers. - handler.Link(db) - // ... Do other operations. - }) - -Further usage it's the same way mgo package is used. Look into mgo -docs page: https://godoc.org/github.com/globalsign/mgo - -The Connect function tries to connect to a MONGODB_URL environment -variable, but when it's not defined, it uses a default URL: - - mongodb://localhost:27017/severo-rest-db - -Mocking - -You can mock some functions of this package, by mocking the mgo -called functions mgo.ParseURL and mgo.Dial. Use the MockMongoSetup -presented on this package (only in test environment), like: - - create, _ := mongo.NewMockMongoSetup(t) - defer create.Finish() - - create.ParseURL().Returns(db, nil) - create.Dial().Returns(info, nil) - - // Call any preparations on connection ... - if err := mongo.Connect(); err != nil { - t.fail() - } - -*/package mongo diff --git a/model/documenter.go b/documenter.go similarity index 97% rename from model/documenter.go rename to documenter.go index 2773994..0ffbb25 100644 --- a/model/documenter.go +++ b/documenter.go @@ -1,4 +1,4 @@ -package model +package mongo import "github.com/globalsign/mgo/bson" diff --git a/model/model_test.go b/documenter_test.go similarity index 99% rename from model/model_test.go rename to documenter_test.go index fed6f0a..cb883c9 100644 --- a/model/model_test.go +++ b/documenter_test.go @@ -1,4 +1,4 @@ -package model +package mongo import ( "testing" diff --git a/handler/handle.go b/handle.go similarity index 87% rename from handler/handle.go rename to handle.go index 859ec5b..2e6740a 100644 --- a/handler/handle.go +++ b/handle.go @@ -1,10 +1,9 @@ -package handler +package mongo import ( "errors" "github.com/ddspog/mongo/elements" - "github.com/ddspog/mongo/model" "github.com/globalsign/mgo/bson" ) @@ -26,8 +25,8 @@ type Handle struct { SearchMV bson.M } -// New creates a new Handle to be embedded onto handle for other types. -func New() (h *Handle) { +// NewHandle creates a new Handle to be embedded onto handle for other types. +func NewHandle() (h *Handle) { h = &Handle{} return } @@ -66,7 +65,7 @@ func (h *Handle) Count() (n int, err error) { // Find search for a document matching the doc data on collection // connected to Handle. -func (h *Handle) Find(doc model.Documenter, out model.Documenter) (err error) { +func (h *Handle) Find(doc Documenter, out Documenter) (err error) { if err = h.checkLink(); err == nil { var mapped bson.M @@ -88,7 +87,7 @@ func (h *Handle) Find(doc model.Documenter, out model.Documenter) (err error) { // FindAll search for all documents matching the doc data on // collection connected to Handle. -func (h *Handle) FindAll(doc model.Documenter, out *[]model.Documenter) (err error) { +func (h *Handle) FindAll(doc Documenter, out *[]Documenter) (err error) { if err = h.checkLink(); err == nil { var mapped bson.M @@ -101,7 +100,7 @@ func (h *Handle) FindAll(doc model.Documenter, out *[]model.Documenter) (err err if err == nil { var result []interface{} if err = h.collectionV.Find(mapped).All(&result); err == nil { - tempArr := make([]model.Documenter, len(result)) + tempArr := make([]Documenter, len(result)) for i := range result { //noinspection GoNilContainerIndexing tempArr[i] = doc.New() @@ -119,7 +118,7 @@ func (h *Handle) FindAll(doc model.Documenter, out *[]model.Documenter) (err err // Insert puts a new document on collection connected to Handle, using // doc data. -func (h *Handle) Insert(doc model.Documenter) (err error) { +func (h *Handle) Insert(doc Documenter) (err error) { if doc.ID().Hex() == "" { doc.GenerateID() } @@ -150,7 +149,7 @@ func (h *Handle) Remove(id bson.ObjectId) (err error) { // RemoveAll delete all documents on collection connected to Handle, // matching the doc data. -func (h *Handle) RemoveAll(doc model.Documenter) (info *elements.ChangeInfo, err error) { +func (h *Handle) RemoveAll(doc Documenter) (info *elements.ChangeInfo, err error) { if err = h.checkLink(); err == nil { var mapped bson.M @@ -169,7 +168,7 @@ func (h *Handle) RemoveAll(doc model.Documenter) (info *elements.ChangeInfo, err // Update updates a document on collection connected to Handle, // matching id received, updating with the information on doc. -func (h *Handle) Update(id bson.ObjectId, doc model.Documenter) (err error) { +func (h *Handle) Update(id bson.ObjectId, doc Documenter) (err error) { if id.Hex() == "" { err = ErrIDNotDefined } else { diff --git a/handler/handle_test.go b/handle_test.go similarity index 98% rename from handler/handle_test.go rename to handle_test.go index af5f97b..6a709c0 100644 --- a/handler/handle_test.go +++ b/handle_test.go @@ -1,4 +1,4 @@ -package handler +package mongo import ( "fmt" @@ -7,7 +7,6 @@ import ( "github.com/ddspog/mongo/elements" "github.com/ddspog/mongo/mocks" - "github.com/ddspog/mongo/model" "github.com/ddspog/mspec/bdd" "github.com/globalsign/mgo/bson" ) @@ -347,8 +346,8 @@ func Test_Find_various_documents_with_Handle(t *testing.T) { // - So that I can use Handler to insert data. func Test_Insert_documents_with_Handle(t *testing.T) { makeMGO, _ := mocks.NewMockMGOSetup(t) - makeModel, _ := model.NewMockModelSetup(t) - defer Finish(makeMGO, makeModel) + makeModel, _ := NewMockModelSetup(t) + defer finish(makeMGO, makeModel) given, like, s := bdd.Sentences() @@ -464,8 +463,8 @@ func Test_Remove_various_documents_with_Handle(t *testing.T) { // - So that I can use Handler to update data. func Test_Update_documents_with_Handle(t *testing.T) { makeMGO, _ := mocks.NewMockMGOSetup(t) - makeModel, _ := model.NewMockModelSetup(t) - defer Finish(makeMGO, makeModel) + makeModel, _ := NewMockModelSetup(t) + defer finish(makeMGO, makeModel) given, like, s := bdd.Sentences() diff --git a/handler/README.md b/handler/README.md deleted file mode 100644 index f1be89f..0000000 --- a/handler/README.md +++ /dev/null @@ -1,117 +0,0 @@ -# handler [![GoDoc](https://godoc.org/github.com/ddspog/mongo/handler?status.svg)](https://godoc.org/github.com/ddspog/mongo/handler) [![Go Report Card](https://goreportcard.com/badge/github.com/ddspog/mongo/handler)](https://goreportcard.com/report/github.com/ddspog/mongo/handler) [![Commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg)](http://commitizen.github.io/cz-cli/) - -## Overview - -Package handler enable creation of Handle, a type that connects to -database collections and do some operations. - -The Handle were made to be imported on embedding type, and through -overriding of some methods, to implement an adequate Handler for a -desired type of Document. The Handle type assumes to operate on a -model.Documenter type, that will contain information about the -operation to made with Handle. - -## Usage - -The package should be used to create new types. Use the Handler type -for creating embedding types. - -```go -type ProductHandle struct { - *handler.Handle - DocumentV *product.Product -} -``` - -For each new type, a constructor may be needed, and for that Handler -has a basic constructor. - -```go -func New() (p *ProductHandle) { - p = &ProductHandle{ - Handle: handler.New(), - DocumentV: product.New(), - } - return -} -``` - -All functions were made to be overridden and rewrite. First thing to do -it's creating a Name function. - -```go -func (p *ProductHandle) Name() (n string) { - n = "products" - return -} -``` - -With Name function, the creation of Link method goes as it follows: - -```go -func (p *ProductHandle) Link(db mongo.Databaser) (h *ProductHandle) { - p.Handle.Link(db, p.Name()) - h = p - return -} -``` - -The creation of Insert, Remove and RemoveAll are trivial. Call it with -a Document getter function defined like: - -```go -func (p *ProductHandle) Document() (d *product.Product) { - d = p.DocumentV - return -} - -func (p *ProductHandle) Insert() (err error) { - err = p.Handle.Insert(p.Document()) - return -} -``` - -The Clean function is simple and helps a lot: - -```go -func (p *ProductHandle) Clean() { - p.Handle.Clean() - p.DocumentV = NewProduct() -} -``` - -The Update function uses an id as an argument: - -```go -func (p *ProductHandle) Update(id bson.ObjectId) (err error) { - err = p.Handle.Update(id, p.Document()) - return -} -``` - -The complicated functions are Find and FindAll which requires casting -for the Document type: - -```go -func (p *ProductHandle) Find() (prod *product.Product, err error) { - var doc model.Documenter = product.New() - err = p.Handle.Find(p.Document(), &doc) - prod = doc.(*Product) - return -} - -func (p *ProductHandle) FindAll() (proda []*product.Product, err error) { - var da []model.Documenter - err = p.Handle.FindAll(p.Document(), func() model.Documenter { - return product.New() - }, &da) - proda = make([]*product.Product, len(da)) - for i := range da { - //noinspection GoNilContainerIndexing - proda[i] = da[i].(*product.Product) - } - return -} -``` - -For all functions written, verification it's advisable. \ No newline at end of file diff --git a/handler/doc_handle.go b/handler/doc_handle.go deleted file mode 100644 index 5675adc..0000000 --- a/handler/doc_handle.go +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright 2009 Dênnis Dantas de Sousa. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -/* -Package handler enable creation of Handle, a type that connects to -database collections and do some operations. - -The Handle were made to be imported on embedding type, and through -overriding of some methods, to implement an adequate Handler for a -desired type of Document. The Handle type assumes to operate on a -model.Documenter type, that will contain information about the -operation to made with Handle. - -Usage - -The package should be used to create new types. Use the Handler type -for creating embedding types. - - type ProductHandle struct { - *handler.Handle - DocumentV *product.Product - } - -For each new type, a constructor may be needed, and for that Handler -has a basic constructor. - - func New() (p *ProductHandle) { - p = &ProductHandle{ - Handle: handler.New(), - DocumentV: product.New(), - } - return - } - -All functions were made to be overridden and rewrite. First thing to do -it's creating a Name function. - - func (p *ProductHandle) Name() (n string) { - n = "products" - return - } - -With Name function, the creation of Link method goes as it follows: - - func (p *ProductHandle) Link(db mongo.Databaser) (h *ProductHandle) { - p.Handle.Link(db, p.Name()) - h = p - return - } - -The creation of Insert, Remove and RemoveAll are trivial. Call it with -a Document getter function defined like: - - func (p *ProductHandle) Document() (d *product.Product) { - d = p.DocumentV - return - } - - func (p *ProductHandle) Insert() (err error) { - err = p.Handle.Insert(p.Document()) - return - } - -The Clean function is simple and helps a lot: - - func (p *ProductHandle) Clean() { - p.Handle.Clean() - p.DocumentV = NewProduct() - } - -The Update function uses an id as an argument: - - func (p *ProductHandle) Update(id bson.ObjectId) (err error) { - err = p.Handle.Update(id, p.Document()) - return - } - -The complicated functions are Find and FindAll which requires casting -for the Document type: - - func (p *ProductHandle) Find() (prod *product.Product, err error) { - var doc model.Documenter = product.New() - err = p.Handle.Find(p.Document(), &doc) - prod = doc.(*Product) - return - } - - func (p *ProductHandle) FindAll() (proda []*product.Product, err error) { - var da []model.Documenter - err = p.Handle.FindAll(p.Document(), func() model.Documenter { - return product.New() - }, &da) - proda = make([]*product.Product, len(da)) - for i := range da { - //noinspection GoNilContainerIndexing - proda[i] = da[i].(*product.Product) - } - return - } - -For all functions written, verification it's advisable. -*/ -package handler diff --git a/handler/helpers_test.go b/handler/helpers_test.go deleted file mode 100644 index e80d3fd..0000000 --- a/handler/helpers_test.go +++ /dev/null @@ -1,234 +0,0 @@ -package handler - -import ( - "time" - - "github.com/ddspog/mongo/elements" - "github.com/ddspog/mongo/model" - "github.com/globalsign/mgo/bson" -) - -const ( - // Const values to help tests legibility. - testid = "000000000000746573746964" - product1id = "000070726f64756374316964" - product2id = "000070726f64756374326964" - anyReason = "Whatever reason." -) - -// productCollection mimics a collection of products. -var productCollection = []*product{ - newProductStored(), - newProductStored(), -} - -// product it's a simples implementation of Documenter. It contains -// some attributes important to any document on MongoDB. -type product struct { - IDV bson.ObjectId `json:"_id,omitempty" bson:"_id,omitempty"` - CreatedOnV int64 `json:"created_on" bson:"created_on"` - UpdatedOnV int64 `json:"updated_on" bson:"updated_on"` -} - -// newProduct returns a empty product. -func newProduct() (p *product) { - p = &product{} - return -} - -// newProductStored returns a product with some attributes. -func newProductStored() (p *product) { - p = newProduct() - p.GenerateID() - time.Sleep(10 * time.Millisecond) - - p.CalculateCreatedOn() - return -} - -// New creates a new instance of the same Product, used on another -// functions for clone purposes. -func (p *product) New() (doc model.Documenter) { - doc = newProduct() - return -} - -// Map translates a product to a bson.M object, more easily read by mgo -// methods. -func (p *product) Map() (out bson.M, err error) { - out, err = model.MapDocumenter(p) - return -} - -// Init translates a bson.M received, to the product structure. It -// fills the structure fields with the values of each key in the -// bson.M received. -func (p *product) Init(in bson.M) (err error) { - var doc model.Documenter = p - err = model.InitDocumenter(in, &doc) - return -} - -// ID returns the _id attribute of a Document. -func (p *product) ID() (id bson.ObjectId) { - id = p.IDV - return -} - -// CreatedOn returns the created_on attribute of a Document. -func (p *product) CreatedOn() (t int64) { - t = p.CreatedOnV - return -} - -// UpdatedOn returns the updated_on attribute of a Document. -func (p *product) UpdatedOn() (t int64) { - t = p.UpdatedOnV - return -} - -// GenerateID creates a new id for a document. -func (p *product) GenerateID() { - p.IDV = model.NewID() -} - -// CalculateCreatedOn update the created_on attribute with a value -// corresponding to actual time. -func (p *product) CalculateCreatedOn() { - p.CreatedOnV = model.NowInMilli() -} - -// CalculateUpdatedOn update the updated_on attribute with a value -// corresponding to actual time. -func (p *product) CalculateUpdatedOn() { - p.UpdatedOnV = model.NowInMilli() -} - -// productHandler it's an interface describing operations common to -// handler's of MongoDB products collection. -type productHandler interface { - Clean() - Link(elements.Databaser) error - Count() (int, error) - Find() (*product, error) - FindAll() ([]*product, error) - Insert() error - Remove() error - RemoveAll() (*elements.ChangeInfo, error) - Update(bson.ObjectId) error - Document() *product - SearchM() bson.M - Name() string -} - -// productHandle it's a type embedding the Handle struct, it's capable -// of storing products. -type productHandle struct { - *Handle - DocumentV *product -} - -// newProductHandle returns a empty productHandle. -func newProductHandle() (p *productHandle) { - p = &productHandle{ - Handle: New(), - DocumentV: newProduct(), - } - return -} - -// Name returns the name of connection that productHandle can connect. -func (p *productHandle) Name() (n string) { - n = "products" - return -} - -// Link connects the productHandle to collection. -func (p *productHandle) Link(db elements.Databaser) (err error) { - err = p.Handle.Link(db, p.Name()) - return -} - -// Clean documents and search map values. -func (p *productHandle) Clean() { - p.Handle.Clean() - p.DocumentV = newProduct() -} - -// Find search on connected collection for a document matching data -// stored on productHandle and returns it. -func (p *productHandle) Find() (prod *product, err error) { - var doc model.Documenter = newProduct() - err = p.Handle.Find(p.Document(), doc) - prod = doc.(*product) - return -} - -// FindAll search on connected collection for all documents matching -// data stored on productHandle and returns it. -func (p *productHandle) FindAll() (proda []*product, err error) { - var da []model.Documenter - err = p.Handle.FindAll(p.Document(), &da) - proda = make([]*product, len(da)) - for i := range da { - //noinspection GoNilContainerIndexing - proda[i] = da[i].(*product) - } - return -} - -// Insert creates a new document with data stored on productHandle -// and put on connected collection. -func (p *productHandle) Insert() (err error) { - err = p.Handle.Insert(p.Document()) - return -} - -// Remove delete a document from connected collection matching the id -// of data stored on Handle. -func (p *productHandle) Remove() (err error) { - err = p.Handle.Remove(p.Document().ID()) - return -} - -// Remove deletes all document from connected collection matching the -// data stored on Handle. -func (p *productHandle) RemoveAll() (info *elements.ChangeInfo, err error) { - info, err = p.Handle.RemoveAll(p.Document()) - return -} - -// Update updates document from connected collection matching the id -// received, and uses document info to update. -func (p *productHandle) Update(id bson.ObjectId) (err error) { - err = p.Handle.Update(id, p.Document()) - return -} - -// Document returns the Document of Handle with correct type. -func (p *productHandle) Document() (d *product) { - d = p.DocumentV - return -} - -// Finish call Finish() from all objects received. -func Finish(fs ...interface { - Finish() -}) { - for i := range fs { - fs[i].Finish() - } -} - -// timeFmt parses time well formatted. -func timeFmt(s string) (t time.Time) { - t, _ = time.Parse("02-01-2006 15:04:05", s) - return -} - -// expectedNowInMilli returns expected return from NowInMilli function, -// given the time returned by time.Now(). -func expectedNowInMilli(t time.Time) (r int64) { - r = t.UnixNano() / int64(time.Millisecond) - return -} diff --git a/helpers_test.go b/helpers_test.go index 891c034..845ab9f 100644 --- a/helpers_test.go +++ b/helpers_test.go @@ -1,12 +1,26 @@ package mongo import ( + "time" + "github.com/ddspog/mongo/elements" - "github.com/ddspog/mongo/handler" - "github.com/ddspog/mongo/model" "github.com/globalsign/mgo/bson" ) +const ( + // Const values to help tests legibility. + testid = "000000000000746573746964" + product1id = "000070726f64756374316964" + product2id = "000070726f64756374326964" + anyReason = "Whatever reason." +) + +// productCollection mimics a collection of products. +var productCollection = []*product{ + newProductStored(), + newProductStored(), +} + // product it's a type embedding the Document struct. type product struct { IDV bson.ObjectId `json:"_id,omitempty" bson:"_id,omitempty"` @@ -20,9 +34,19 @@ func newProduct() (p *product) { return } +// newProductStored returns a product with some attributes. +func newProductStored() (p *product) { + p = newProduct() + p.GenerateID() + time.Sleep(10 * time.Millisecond) + + p.CalculateCreatedOn() + return +} + // New creates a new instance of the same product, used on another // functions for clone purposes. -func (p *product) New() (doc model.Documenter) { +func (p *product) New() (doc Documenter) { doc = newProduct() return } @@ -30,7 +54,7 @@ func (p *product) New() (doc model.Documenter) { // Map translates a product to a bson.M object, more easily read by mgo // methods. func (p *product) Map() (out bson.M, err error) { - out, err = model.MapDocumenter(p) + out, err = MapDocumenter(p) return } @@ -38,8 +62,8 @@ func (p *product) Map() (out bson.M, err error) { // fills the structure fields with the values of each key in the // bson.M received. func (p *product) Init(in bson.M) (err error) { - var doc model.Documenter = p - err = model.InitDocumenter(in, &doc) + var doc Documenter = p + err = InitDocumenter(in, &doc) return } @@ -63,25 +87,26 @@ func (p *product) UpdatedOn() (t int64) { // GenerateID creates a new id for a document. func (p *product) GenerateID() { - p.IDV = model.NewID() + p.IDV = NewID() } // CalculateCreatedOn update the created_on attribute with a value // corresponding to actual time. func (p *product) CalculateCreatedOn() { - p.CreatedOnV = model.NowInMilli() + p.CreatedOnV = NowInMilli() } // CalculateUpdatedOn update the updated_on attribute with a value // corresponding to actual time. func (p *product) CalculateUpdatedOn() { - p.UpdatedOnV = model.NowInMilli() + p.UpdatedOnV = NowInMilli() } // productHandler it's an interface describing operations common to // handler's of MongoDB Products collection. type productHandler interface { - Link(elements.Databaser) (productHandler, error) + Link(elements.Databaser) error + Clean() Count() (int, error) Find() (*product, error) FindAll() ([]*product, error) @@ -90,20 +115,21 @@ type productHandler interface { RemoveAll() (*elements.ChangeInfo, error) Update(bson.ObjectId) error Document() *product + SearchM() bson.M Name() string } // productHandle it's a type embedding the Handle struct, it's capable // of storing Products. type productHandle struct { - *handler.Handle + *Handle DocumentV *product } // newProductHandle returns a empty productHandle. func newProductHandle() (p *productHandle) { p = &productHandle{ - Handle: handler.New(), + Handle: NewHandle(), DocumentV: newProduct(), } return @@ -116,16 +142,21 @@ func (p *productHandle) Name() (n string) { } // Link connects the productHandle to collection. -func (p *productHandle) Link(db elements.Databaser) (h productHandler, err error) { +func (p *productHandle) Link(db elements.Databaser) (err error) { err = p.Handle.Link(db, p.Name()) - h = p return } +// Clean documents and search map values. +func (p *productHandle) Clean() { + p.Handle.Clean() + p.DocumentV = newProduct() +} + // Find search on connected collection for a document matching data // stored on productHandle and returns it. func (p *productHandle) Find() (prod *product, err error) { - var doc model.Documenter = newProduct() + var doc Documenter = newProduct() err = p.Handle.Find(p.Document(), doc) prod = doc.(*product) return @@ -134,7 +165,7 @@ func (p *productHandle) Find() (prod *product, err error) { // FindAll search on connected collection for all documents matching // data stored on productHandle and returns it. func (p *productHandle) FindAll() (proda []*product, err error) { - var da []model.Documenter + var da []Documenter err = p.Handle.FindAll(p.Document(), &da) proda = make([]*product, len(da)) for i := range da { @@ -191,33 +222,15 @@ func finish(fs ...finisher) { } } -func newDBSocket() (db databaseSocketer) { - db = &databaseSocket{ - db: make(chan elements.Databaser), - quit: make(chan bool), - } +// timeFmt parses time well formatted. +func timeFmt(s string) (t time.Time) { + t, _ = time.Parse("02-01-2006 15:04:05", s) return } -type databaseSocketer interface { - DB() elements.Databaser - Close() -} - -type databaseSocket struct { - db chan elements.Databaser - quit chan bool -} - -func (d *databaseSocket) DB() (db elements.Databaser) { - go ConsumeDatabaseOnSession(func(db elements.Databaser) { - d.db <- db - <-d.quit - }) - - return <-d.db -} - -func (d *databaseSocket) Close() { - d.quit <- true +// expectedNowInMilli returns expected return from NowInMilli function, +// given the time returned by time.Now(). +func expectedNowInMilli(t time.Time) (r int64) { + r = t.UnixNano() / int64(time.Millisecond) + return } diff --git a/mock_connect.go b/mock.go similarity index 53% rename from mock_connect.go rename to mock.go index 8f5327f..f0b8b8e 100644 --- a/mock_connect.go +++ b/mock.go @@ -3,11 +3,14 @@ package mongo import ( "fmt" "testing" + "time" "github.com/ddspog/mongo/elements" + "github.com/globalsign/mgo/bson" ) -// MockMongoSetup it's a setup type for configuring functions mocking. +// MockMongoSetup it's a setup type for configuring mocking for connect +// functions. type MockMongoSetup struct { originalParseURL func(string) (*elements.DialInfo, error) originalDial func(string) (elements.Sessioner, error) @@ -135,3 +138,128 @@ func (f *fakeDial) GetFunction() (fn func(string) (elements.Sessioner, error)) { } return } + +// MockModelSetup it's a setup type for configuring mocking for util +// functions. +type MockModelSetup struct { + originalNow func() time.Time + tempNow *fakeNow + tempNewID *fakeNewID +} + +// NewMockModelSetup returns a new MockModelSetup, already configuring +// a FakeNow function, and a FakeNewID function on the setup. It +// requires a test environment to be running. +func NewMockModelSetup(t *testing.T) (s *MockModelSetup, err error) { + if t == nil { + err = fmt.Errorf("run only on test environment") + } else { + s = &MockModelSetup{ + originalNow: now, + tempNow: newFakeNow(), + tempNewID: newFakeNewID(), + } + s.tempNow.mockModelSetupP = s + s.tempNewID.mockModelSetupP = s + } + return +} + +// Finish restore the functions mocked to the original ones. +func (s *MockModelSetup) Finish() { + now = s.originalNow +} + +// Now returns the fake Now object on this setup. +func (s *MockModelSetup) Now() (f FakeNower) { + f = s.tempNow + return +} + +// NewID returns the fake NewID object on this setup. +func (s *MockModelSetup) NewID() (f FakeNewIDer) { + f = s.tempNewID + return +} + +// updateNow the now function with a mocked one. +func (s *MockModelSetup) updateNow() { + now = s.Now().GetFunction() +} + +// updateNewID the NewID function with a mocked one. +func (s *MockModelSetup) updateNewID() { + newID = s.NewID().GetFunction() +} + +// fakeNow it's a type that enable mocking of function now. +type fakeNow struct { + returnV *time.Time + mockModelSetupP *MockModelSetup +} + +// FakeNower it's a function mocking object, needed for mock purposes. +type FakeNower interface { + Returns(time.Time) + GetFunction() func() time.Time +} + +// newFakeNow returns a new FakeNow object. +func newFakeNow() (f *fakeNow) { + var i time.Time + f = &fakeNow{ + returnV: &i, + } + return +} + +// Returns ensures a value to be returned on calls to now. +func (f *fakeNow) Returns(t time.Time) { + *f.returnV = t + f.mockModelSetupP.updateNow() +} + +// getFunction creates a version of now that returns value demanded. +func (f *fakeNow) GetFunction() (fn func() time.Time) { + fn = func() (t time.Time) { + t = *f.returnV + return + } + return +} + +// fakeNewID it's a type that enable mocking of function NewID. +type fakeNewID struct { + returnV *bson.ObjectId + mockModelSetupP *MockModelSetup +} + +// FakeNewIDer it's a function mocking object, needed for mock purposes. +type FakeNewIDer interface { + Returns(bson.ObjectId) + GetFunction() func() bson.ObjectId +} + +// newFakeNewID returns a new FakeNewID object. +func newFakeNewID() (f *fakeNewID) { + var id = newID() + f = &fakeNewID{ + returnV: &id, + } + return +} + +// Returns ensures a value to be returned on calls to NewID. +func (f *fakeNewID) Returns(id bson.ObjectId) { + *f.returnV = id + f.mockModelSetupP.updateNewID() +} + +// getFunction creates a version of NewID that returns value demanded. +func (f *fakeNewID) GetFunction() (fn func() bson.ObjectId) { + fn = func() (id bson.ObjectId) { + id = *f.returnV + return + } + return +} diff --git a/model/README.md b/model/README.md deleted file mode 100644 index 3d426c2..0000000 --- a/model/README.md +++ /dev/null @@ -1,94 +0,0 @@ -# model [![GoDoc](https://godoc.org/github.com/ddspog/mongo/model?status.svg)](https://godoc.org/github.com/ddspog/mongo/model) [![Go Report Card](https://goreportcard.com/badge/github.com/ddspog/mongo/model)](https://goreportcard.com/report/github.com/ddspog/mongo/model) [![Commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg)](http://commitizen.github.io/cz-cli/) - -## Overview - -Package model contain utility functions to help modeling documents. - -The package contains a interface Documenter which contain getters for -important attributes to any document on MongoDB: \_id, created\_on and -updated\_on. It also contains functions that generates correctly the -created\_on and updated\_on attributes. - -## Usage - -The package can be used like this: - -```go -// Create a type embedding the Document type -type Product struct { - IDV bson.ObjectId `json:"_id,omitempty" bson:"_id,omitempty"` - CreatedOnV int64 `json:"created_on,omitempty" bson:"created_on,omitempty"` - UpdatedOnV int64 `json:"updated_on,omitempty" bson:"updated_on,omitempty"` - NameV string `json:"name" form:"name" binding:"required" bson:"name"` - PriceV float32 `json:"price" form:"price" binding:"required" bson:"price"` -} - -// Implement the Documenter interface. -func (p *Product) ID() (id bson.ObjectId) { - id = p.IDV - return -} - -func (p *Product) CreatedOn() (t int64) { - t = p.CreatedOnV - return -} - -func (p *Product) UpdatedOn() (t int64) { - t = p.UpdatedOnV - return -} - -func (p *Product) New() (doc model.Documenter) { - doc = &Product{} - return -} - -// On these methods, you can use the functions implemented on this -// model package. -func (p *Product) Map() (out bson.M, err error) { - out, err = model.MapDocumenter(p) - return -} - -func (p *Product) Init(in bson.M) (err error) { - var doc model.Documenter = p - err = model.InitDocumenter(in, &doc) - return -} - -func (p *Product) GenerateID() { - p.IDV = model.NewID() -} - -func (p *Product) CalculateCreatedOn() { - p.CreatedOnV = model.NowInMilli() -} - -func (p *Product) CalculateUpdatedOn() { - p.UpdatedOnV = model.NowInMilli() -} - -// Create a product variable, and try its methods. -p := Product{} -p.CalculateCreatedOn() -t := p.CreatedOn() -``` - -## Mocking - -You can mock some functions of this package, by mocking some -called functions time.Now and bson.NewObjectId. Use the MockModelSetup presented on this package (only in test environment), like: - -```go -create, _ := model.NewMockModelSetup(t) -defer create.Finish() - -create.Now().Returns(time.Parse("02-01-2006", "22/12/2006")) -create.NewID().Returns(bson.ObjectIdHex("anyID")) - -var d model.Documenter -// Call any needed methods ... -d.GenerateID() -d.CalculateCreatedOn() -``` \ No newline at end of file diff --git a/model/doc_model.go b/model/doc_model.go deleted file mode 100644 index 5385501..0000000 --- a/model/doc_model.go +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright 2009 Dênnis Dantas de Sousa. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -/* -Package model contain utility functions to help modeling documents. - -The package contains a interface Documenter which contain getters for -important attributes to any document on MongoDB: _id, created_on and -updated_on. It also contains functions that generates correctly the -created_on and updated_on attributes. - -Usage - -The package can be used like this: - - // Create a type embedding the Document type - type Product struct { - IDV bson.ObjectId `json:"_id,omitempty" bson:"_id,omitempty"` - CreatedOnV int64 `json:"created_on,omitempty" bson:"created_on,omitempty"` - UpdatedOnV int64 `json:"updated_on,omitempty" bson:"updated_on,omitempty"` - NameV string `json:"name" form:"name" binding:"required" bson:"name"` - PriceV float32 `json:"price" form:"price" binding:"required" bson:"price"` - } - - // Implement the Documenter interface. - func (p *Product) ID() (id bson.ObjectId) { - id = p.IDV - return - } - - func (p *Product) CreatedOn() (t int64) { - t = p.CreatedOnV - return - } - - func (p *Product) UpdatedOn() (t int64) { - t = p.UpdatedOnV - return - } - - func (p *Product) New() (doc model.Documenter) { - doc = &Product{} - return - } - - // On these methods, you can use the functions implemented on this - // model package. - func (p *Product) Map() (out bson.M, err error) { - out, err = model.MapDocumenter(p) - return - } - - func (p *Product) Init(in bson.M) (err error) { - var doc model.Documenter = p - err = model.InitDocumenter(in, &doc) - return - } - - func (p *Product) GenerateID() { - p.IDV = model.NewID() - } - - func (p *Product) CalculateCreatedOn() { - p.CreatedOnV = model.NowInMilli() - } - - func (p *Product) CalculateUpdatedOn() { - p.UpdatedOnV = model.NowInMilli() - } - - // Create a product variable, and try its methods. - p := Product{} - p.CalculateCreatedOn() - t := p.CreatedOn() - -Mocking - -You can mock some functions of this package, by mocking some -called functions time.Now and bson.NewObjectId. Use the MockModelSetup -presented on this package (only in test environment), like: - - create, _ := model.NewMockModelSetup(t) - defer create.Finish() - - create.Now().Returns(time.Parse("02-01-2006", "22/12/2006")) - create.NewID().Returns(bson.ObjectIdHex("anyID")) - - var d model.Documenter - // Call any needed methods ... - d.GenerateID() - d.CalculateCreatedOn() -*/ -package model diff --git a/model/helpers_test.go b/model/helpers_test.go deleted file mode 100644 index 5a5aad2..0000000 --- a/model/helpers_test.go +++ /dev/null @@ -1,98 +0,0 @@ -package model - -import ( - "time" - - "github.com/globalsign/mgo/bson" -) - -const ( - // Const values to help tests legibility. - testid = "000000000000746573746964" - product1id = "000070726f64756374316964" - product2id = "000070726f64756374326964" -) - -// product it's a type implementing the Documenter interface. -type product struct { - IDV bson.ObjectId `json:"_id,omitempty" bson:"_id,omitempty"` - CreatedOnV int64 `json:"created_on" bson:"created_on"` - UpdatedOnV int64 `json:"updated_on" bson:"updated_on"` -} - -// newProduct returns a empty product. -func newProduct() (p *product) { - p = &product{} - return -} - -// New creates a new instance of the same Product, used on another -// functions for clone purposes. -func (p *product) New() (doc Documenter) { - doc = newProduct() - return -} - -// Map translates a product to a bson.M object, more easily read by mgo -// methods. -func (p *product) Map() (out bson.M, err error) { - out, err = MapDocumenter(p) - return -} - -// Init translates a bson.M received, to the product structure. It -// fills the structure fields with the values of each key in the -// bson.M received. -func (p *product) Init(in bson.M) (err error) { - var doc Documenter = p - err = InitDocumenter(in, &doc) - return -} - -// ID returns the _id attribute of a Document. -func (p *product) ID() (id bson.ObjectId) { - id = p.IDV - return -} - -// CreatedOn returns the created_on attribute of a Document. -func (p *product) CreatedOn() (t int64) { - t = p.CreatedOnV - return -} - -// UpdatedOn returns the updated_on attribute of a Document. -func (p *product) UpdatedOn() (t int64) { - t = p.UpdatedOnV - return -} - -// GenerateID creates a new id for a document. -func (p *product) GenerateID() { - p.IDV = NewID() -} - -// CalculateCreatedOn update the created_on attribute with a value -// corresponding to actual time. -func (p *product) CalculateCreatedOn() { - p.CreatedOnV = NowInMilli() -} - -// CalculateUpdatedOn update the updated_on attribute with a value -// corresponding to actual time. -func (p *product) CalculateUpdatedOn() { - p.UpdatedOnV = NowInMilli() -} - -// timeFmt parses time well formatted. -func timeFmt(s string) (t time.Time) { - t, _ = time.Parse("02-01-2006 15:04:05", s) - return -} - -// expectedNowInMilli returns expected return from NowInMilli function, -// given the time returned by time.Now(). -func expectedNowInMilli(t time.Time) (r int64) { - r = t.UnixNano() / int64(time.Millisecond) - return -} diff --git a/model/mock_util.go b/model/mock_util.go deleted file mode 100644 index a6d8a54..0000000 --- a/model/mock_util.go +++ /dev/null @@ -1,133 +0,0 @@ -package model - -import ( - "fmt" - "testing" - "time" - - "github.com/globalsign/mgo/bson" -) - -// MockModelSetup it's a setup type for configuring functions mocking. -type MockModelSetup struct { - originalNow func() time.Time - tempNow *fakeNow - tempNewID *fakeNewID -} - -// NewMockModelSetup returns a new MockModelSetup, already configuring -// a FakeNow function, and a FakeNewID function on the setup. It -// requires a test environment to be running. -func NewMockModelSetup(t *testing.T) (s *MockModelSetup, err error) { - if t == nil { - err = fmt.Errorf("run only on test environment") - } else { - s = &MockModelSetup{ - originalNow: now, - tempNow: newFakeNow(), - tempNewID: newFakeNewID(), - } - s.tempNow.mockModelSetupP = s - s.tempNewID.mockModelSetupP = s - } - return -} - -// Finish restore the functions mocked to the original ones. -func (s *MockModelSetup) Finish() { - now = s.originalNow -} - -// Now returns the fake Now object on this setup. -func (s *MockModelSetup) Now() (f FakeNower) { - f = s.tempNow - return -} - -// NewID returns the fake NewID object on this setup. -func (s *MockModelSetup) NewID() (f FakeNewIDer) { - f = s.tempNewID - return -} - -// updateNow the now function with a mocked one. -func (s *MockModelSetup) updateNow() { - now = s.Now().GetFunction() -} - -// updateNewID the NewID function with a mocked one. -func (s *MockModelSetup) updateNewID() { - newID = s.NewID().GetFunction() -} - -// fakeNow it's a type that enable mocking of function now. -type fakeNow struct { - returnV *time.Time - mockModelSetupP *MockModelSetup -} - -// FakeNower it's a function mocking object, needed for mock purposes. -type FakeNower interface { - Returns(time.Time) - GetFunction() func() time.Time -} - -// newFakeNow returns a new FakeNow object. -func newFakeNow() (f *fakeNow) { - var i time.Time - f = &fakeNow{ - returnV: &i, - } - return -} - -// Returns ensures a value to be returned on calls to now. -func (f *fakeNow) Returns(t time.Time) { - *f.returnV = t - f.mockModelSetupP.updateNow() -} - -// getFunction creates a version of now that returns value demanded. -func (f *fakeNow) GetFunction() (fn func() time.Time) { - fn = func() (t time.Time) { - t = *f.returnV - return - } - return -} - -// fakeNewID it's a type that enable mocking of function NewID. -type fakeNewID struct { - returnV *bson.ObjectId - mockModelSetupP *MockModelSetup -} - -// FakeNewIDer it's a function mocking object, needed for mock purposes. -type FakeNewIDer interface { - Returns(bson.ObjectId) - GetFunction() func() bson.ObjectId -} - -// newFakeNewID returns a new FakeNewID object. -func newFakeNewID() (f *fakeNewID) { - var id = newID() - f = &fakeNewID{ - returnV: &id, - } - return -} - -// Returns ensures a value to be returned on calls to NewID. -func (f *fakeNewID) Returns(id bson.ObjectId) { - *f.returnV = id - f.mockModelSetupP.updateNewID() -} - -// getFunction creates a version of NewID that returns value demanded. -func (f *fakeNewID) GetFunction() (fn func() bson.ObjectId) { - fn = func() (id bson.ObjectId) { - id = *f.returnV - return - } - return -} diff --git a/model/util.go b/util.go similarity index 98% rename from model/util.go rename to util.go index 63d9579..3b3262d 100644 --- a/model/util.go +++ b/util.go @@ -1,4 +1,4 @@ -package model +package mongo import ( "time"