Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add factory functionality #235

Merged
merged 23 commits into from
Jul 17, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
e08285b
add `make:factory` command
kkumar-gcc Jul 1, 2023
9ad3f85
Merge branch 'goravel:master' into factory
kkumar-gcc Jul 6, 2023
d2120a1
add factory in orm facade
kkumar-gcc Jul 7, 2023
a86f0a4
Merge branch 'goravel:master' into factory
kkumar-gcc Jul 7, 2023
722d446
add query in factory
kkumar-gcc Jul 7, 2023
2551d72
Merge branch 'master' into factory
kkumar-gcc Jul 8, 2023
b9438b2
implement factory interface
kkumar-gcc Jul 8, 2023
b0f1822
Merge remote-tracking branch 'origin/factory' into factory
kkumar-gcc Jul 8, 2023
ccfe5ca
implement `make` method
kkumar-gcc Jul 9, 2023
6b3fe87
Merge branch 'master' into factory
kkumar-gcc Jul 12, 2023
64d0323
remove unnecessary methods
kkumar-gcc Jul 12, 2023
6812f35
generate mockery for factory contract
kkumar-gcc Jul 12, 2023
17656e1
add test for orm's factory method
kkumar-gcc Jul 12, 2023
e881b63
add tests for factory
kkumar-gcc Jul 14, 2023
6a402d6
Merge branch 'master' into factory
kkumar-gcc Jul 15, 2023
e677c2c
clean-up
kkumar-gcc Jul 15, 2023
a70b996
add test cases for `getRawAttributes` method
kkumar-gcc Jul 17, 2023
6c22c83
missing commit message (couldn't think of one)
kkumar-gcc Jul 17, 2023
55d9f37
change name of function in factory stub
kkumar-gcc Jul 17, 2023
ce094d2
change name of function in factory stub
kkumar-gcc Jul 17, 2023
b2ffccf
change name of function in factory stub
kkumar-gcc Jul 17, 2023
bb6f5ea
change return type of definition method
kkumar-gcc Jul 17, 2023
c374462
skip tests for factory when using docker
kkumar-gcc Jul 17, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions contracts/database/orm/factory.go
kkumar-gcc marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

S: Need to update the orm mock.

Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package orm

import "github.com/brianvoe/gofakeit/v6"

//go:generate mockery --name=Factory
type Factory interface {
Definition() any
New(attributes ...map[string]any) Factory
Times(count int) Factory
Count(count int) Factory
kkumar-gcc marked this conversation as resolved.
Show resolved Hide resolved
Configure() Factory
Raw() any
kkumar-gcc marked this conversation as resolved.
Show resolved Hide resolved
CreateOne() error
CreateOneQuietly() error
CreateMany() error
CreateManyQuietly() error
Create() error
CreateQuietly() error
kkumar-gcc marked this conversation as resolved.
Show resolved Hide resolved
Store() error
MakeOne() Factory
Make() Factory
kkumar-gcc marked this conversation as resolved.
Show resolved Hide resolved
MakeInstance() Factory
GetExpandedAttributes() map[string]any
GetRawAttributes() any
Faker() *gofakeit.Faker
ExpandAttributes(definition map[string]interface{}) map[string]interface{}
Set() Factory
Model(value any) Factory
NewInstance(attributes ...map[string]any) Factory
kkumar-gcc marked this conversation as resolved.
Show resolved Hide resolved
}
1 change: 1 addition & 0 deletions contracts/database/orm/orm.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ type Orm interface {
Connection(name string) Orm
DB() (*sql.DB, error)
Query() Query
Factory() Factory
Observe(model any, observer Observer)
Transaction(txFunc func(tx Transaction) error) error
WithContext(ctx context.Context) Orm
Expand Down
98 changes: 98 additions & 0 deletions database/console/factory_make_command.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package console

import (
"os"
"path/filepath"
"strings"

"github.com/gookit/color"

"github.com/goravel/framework/contracts/console"
"github.com/goravel/framework/contracts/console/command"
"github.com/goravel/framework/support/file"
"github.com/goravel/framework/support/str"
)

type FactoryMakeCommand struct {
}

func NewFactoryMakeCommand() *FactoryMakeCommand {
return &FactoryMakeCommand{}
}

// Signature The name and signature of the console command.
func (receiver *FactoryMakeCommand) Signature() string {
return "make:factory"
}

// Description The console command description.
func (receiver *FactoryMakeCommand) Description() string {
return "Create a new factory class"
}

// Extend The console command extend.
func (receiver *FactoryMakeCommand) Extend() command.Extend {
return command.Extend{
Category: "make",
}
}

// Handle Execute the console command.
func (receiver *FactoryMakeCommand) Handle(ctx console.Context) error {
name := ctx.Argument(0)
if name == "" {
color.Redln("Not enough arguments (missing: name)")

return nil
}

if err := file.Create(receiver.getPath(name), receiver.populateStub(receiver.getStub(), name)); err != nil {
return err
}

color.Greenln("Factory created successfully")

return nil
}

func (receiver *FactoryMakeCommand) getStub() string {
return Stubs{}.Factory()
}

// populateStub Populate the place-holders in the command stub.
func (receiver *FactoryMakeCommand) populateStub(stub string, name string) string {
modelName, packageName, _ := receiver.parseName(name)

stub = strings.ReplaceAll(stub, "DummyFactory", str.Case2Camel(modelName))
stub = strings.ReplaceAll(stub, "DummyPackage", packageName)

return stub
}

// getPath Get the full path to the command.
func (receiver *FactoryMakeCommand) getPath(name string) string {
pwd, _ := os.Getwd()

modelName, _, folderPath := receiver.parseName(name)

return filepath.Join(pwd, "database", "factories", folderPath, str.Camel2Case(modelName)+".go")
}

// parseName Parse the name to get the model name, package name and folder path.
func (receiver *FactoryMakeCommand) parseName(name string) (string, string, string) {
kkumar-gcc marked this conversation as resolved.
Show resolved Hide resolved
name = strings.TrimSuffix(name, ".go")

segments := strings.Split(name, "/")

modelName := segments[len(segments)-1]

packageName := "factories"
folderPath := ""

if len(segments) > 1 {
folderPath = filepath.Join(segments[:len(segments)-1]...)
packageName = segments[len(segments)-2]
}

return modelName, packageName, folderPath
}
31 changes: 31 additions & 0 deletions database/console/factory_make_command_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package console

import (
"testing"

"github.com/stretchr/testify/assert"

consolemocks "github.com/goravel/framework/contracts/console/mocks"
"github.com/goravel/framework/support/file"
)

func TestFactoryMakeCommand(t *testing.T) {
factoryMakeCommand := &FactoryMakeCommand{}
mockContext := &consolemocks.Context{}
mockContext.On("Argument", 0).Return("").Once()
assert.Nil(t, factoryMakeCommand.Handle(mockContext))

mockContext.On("Argument", 0).Return("UserFactory").Once()
assert.Nil(t, factoryMakeCommand.Handle(mockContext))
assert.True(t, file.Exists("database/factories/user_factory.go"))
assert.True(t, file.Contain("database/factories/user_factory.go", "package factories"))
assert.True(t, file.Contain("database/factories/user_factory.go", "type UserFactory struct"))
assert.Nil(t, file.Remove("database"))

mockContext.On("Argument", 0).Return("subdir/DemoFactory").Once()
assert.Nil(t, factoryMakeCommand.Handle(mockContext))
assert.True(t, file.Exists("database/factories/subdir/demo_factory.go"))
assert.True(t, file.Contain("database/factories/subdir/demo_factory.go", "package subdir"))
assert.True(t, file.Contain("database/factories/subdir/demo_factory.go", "type DemoFactory struct"))
assert.Nil(t, file.Remove("database"))
}
13 changes: 13 additions & 0 deletions database/console/stubs.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,16 @@ func (s *DummySeeder) Run() error {
}
`
}

func (r Stubs) Factory() string {
return `package DummyPackage

type DummyFactory struct {
}

// Defination Define the model's default state.
func (f *DummyFactory) Defination() {

}
`
}
166 changes: 166 additions & 0 deletions database/gorm/factory.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
package gorm

import (
"fmt"
"reflect"

"github.com/brianvoe/gofakeit/v6"
"github.com/gookit/color"
ormcontract "github.com/goravel/framework/contracts/database/orm"
)

type FactoryImpl struct {
model any // model to generate
count int // number of models to generate
facker *gofakeit.Faker // faker instance
}

func NewFactoryImpl() *FactoryImpl {
return &FactoryImpl{
count: 1,
facker: gofakeit.New(0),
}
}

func (f *FactoryImpl) New(attributes ...map[string]any) ormcontract.Factory {
return f.NewInstance(attributes...).Configure()
}

func (f *FactoryImpl) Times(count int) ormcontract.Factory {
return f.New().Count(count)
}

func (f *FactoryImpl) Configure() ormcontract.Factory {
return f
}

func (f *FactoryImpl) Raw() any {
return nil
}

func (f *FactoryImpl) CreateOne() error {
return nil
}

func (f *FactoryImpl) CreateOneQuietly() error {
return nil
}

func (f *FactoryImpl) CreateMany() error {
return nil
}

func (f *FactoryImpl) CreateManyQuietly() error {
return nil
}

func (f *FactoryImpl) Create() error {
color.Redf("Create %v", f.model)
return nil
}

func (f *FactoryImpl) CreateQuietly() error {
return nil
}

func (f *FactoryImpl) Store() error {
return nil
}

func (f *FactoryImpl) MakeOne() ormcontract.Factory {
return nil
}

func (f *FactoryImpl) Make() ormcontract.Factory {
return nil
}

func (f *FactoryImpl) MakeInstance() ormcontract.Factory {
return nil
}

func (f *FactoryImpl) GetExpandedAttributes() map[string]any {
return nil
}

func (f *FactoryImpl) GetRawAttributes() any {
modelFactoryMethod := reflect.ValueOf(f.model).MethodByName("Factory")

if modelFactoryMethod.IsValid() {
factoryResult := modelFactoryMethod.Call(nil)
if len(factoryResult) > 0 {
factoryInstance, ok := factoryResult[0].Interface().(ormcontract.Factory)
if ok {
definitionMethod := reflect.ValueOf(factoryInstance).MethodByName("Definition")
if definitionMethod.IsValid() {
definitionResult := definitionMethod.Call(nil)
if len(definitionResult) > 0 {
definition := definitionResult[0].Interface()
fmt.Printf("%#v\n", definition) // Print the definition in a human-readable format
return definition
}
}
}
}
}

definition := f.Definition()
return definition
}

func (f *FactoryImpl) Faker() *gofakeit.Faker { // Adjust this line to retrieve the Faker instance from your DI container
// return gofakeit.New(0)
return f.facker
}

func (f *FactoryImpl) ExpandAttributes(definition map[string]interface{}) map[string]interface{} {
expandedAttributes := make(map[string]interface{})

for key, attribute := range definition {
switch attr := attribute.(type) {
case func(map[string]interface{}) interface{}:
// Evaluate the callable attribute
expandedAttribute := attr(definition)
expandedAttributes[key] = expandedAttribute
default:
expandedAttributes[key] = attr
}
}

return expandedAttributes
}

func (f *FactoryImpl) Set() ormcontract.Factory {
return f
}

func (f *FactoryImpl) Count(count int) ormcontract.Factory {
return f.NewInstance(map[string]any{"count": count})
}

func (f *FactoryImpl) NewInstance(attributes ...map[string]any) ormcontract.Factory {
instance := &FactoryImpl{
count: f.count,
model: f.model,
}

if len(attributes) > 0 {
attr := attributes[0]
if count, ok := attr["count"].(int); ok {
instance.count = count
}
if model, ok := attr["model"]; ok {
instance.model = model
}
kkumar-gcc marked this conversation as resolved.
Show resolved Hide resolved
}

return instance
}

func (f *FactoryImpl) Model(value any) ormcontract.Factory {
return f.NewInstance(map[string]any{"model": value})
}

func (f *FactoryImpl) Definition() any {
return nil
}
4 changes: 4 additions & 0 deletions database/orm.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ func (r *OrmImpl) Query() ormcontract.Query {
return r.query
}

func (r *OrmImpl) Factory() ormcontract.Factory {
return databasegorm.NewFactoryImpl()
}

func (r *OrmImpl) Observe(model any, observer ormcontract.Observer) {
orm.Observers = append(orm.Observers, orm.Observer{
Model: model,
Expand Down
1 change: 1 addition & 0 deletions database/service_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,5 +52,6 @@ func (database *ServiceProvider) registerCommands(app foundation.Application) {
console.NewObserverMakeCommand(),
console.NewSeedCommand(config, seeder),
console.NewSeederMakeCommand(),
console.NewFactoryMakeCommand(),
})
}
Loading