Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
10 changes: 10 additions & 0 deletions typewriters/genwriter/genwriter.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ func (m model) Plural() (result string) {
return
}

func (m model) Predicate() (result string) {
result = m.Name + "Predicate"
return
}

// genwriter prepares models for later use in the .Validate() method. It must be called prior.
func (g GenWriter) ensureValidation(t typewriter.Type) error {
if !g.validated[t.String()] {
Expand Down Expand Up @@ -219,6 +224,11 @@ func (g GenWriter) Write(w io.Writer, t typewriter.Type) {
panic(err)
}

tmpl, _ = standardTemplates.Get("predicates")
if err := tmpl.Execute(w, m); err != nil {
panic(err)
}

for _, s := range m.methods {
tmpl, _ := standardTemplates.Get(s) // already validated above
err := tmpl.Execute(w, m)
Expand Down
27 changes: 27 additions & 0 deletions typewriters/genwriter/templates.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,33 @@ type {{.Plural}} []{{.Pointer}}{{.Name}}
`,
},

"predicates": &typewriter.Template{
Text: `// {{.Predicate}} is a function that accepts a {{.Pointer}}{{.Name}} and returns a bool. Used with gen methods below. Use this type where you would use func({{.Pointer}}{{.Name}}) bool.
type {{.Predicate}} func(item {{.Pointer}}{{.Name}}) bool

// And combines two predicates into a new predicate that is satisfied if both of the original predicates are satisfied
func (rcv {{.Predicate}}) And(other {{.Predicate}}) {{.Predicate}} {
return func (item {{.Pointer}}{{.Name}}) bool {
return rcv(item) && other(item)
}
}

// Or combines two predicates into a new predicate that is satisfied if either of the original predicates is satisfied
func (rcv {{.Predicate}}) Or(other {{.Predicate}}) {{.Predicate}} {
return func (item {{.Pointer}}{{.Name}}) bool {
return rcv(item)|| other(item)
}
}

// Not inverts a predicate that is satisfied if the original predicates is not satisfied
func (rcv {{.Predicate}}) Not() {{.Predicate}} {
return func (item {{.Pointer}}{{.Name}}) bool {
return !rcv(item)
}
}
`,
},

"All": &typewriter.Template{
Text: `
// All verifies that all elements of {{.Plural}} return true for the passed func. See: http://clipperhouse.github.io/gen/#All
Expand Down
24 changes: 24 additions & 0 deletions typewriters/genwriter/test/other_gen.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,30 @@ import (
// Others is a slice of type Other, for use with gen methods below. Use this type where you would use []Other. (This is required because slices cannot be method receivers.)
type Others []Other

// OtherPredicate is a function that accepts a Other and returns a bool. Used with gen methods below. Use this type where you would use func(Other) bool.
type OtherPredicate func(item Other) bool

// And combines two predicates into a new predicate that is satisfied if both of the original predicates are satisfied
func (rcv OtherPredicate) And(other OtherPredicate) OtherPredicate {
return func(item Other) bool {
return rcv(item) && other(item)
}
}

// Or combines two predicates into a new predicate that is satisfied if either of the original predicates is satisfied
func (rcv OtherPredicate) Or(other OtherPredicate) OtherPredicate {
return func(item Other) bool {
return rcv(item) || other(item)
}
}

// Not inverts a predicate that is satisfied if the original predicates is not satisfied
func (rcv OtherPredicate) Not() OtherPredicate {
return func(item Other) bool {
return !rcv(item)
}
}

// Max returns the maximum value of Others. In the case of multiple items being equally maximal, the first such element is returned. Returns error if no elements. See: http://clipperhouse.github.io/gen/#Max
func (rcv Others) Max() (result Other, err error) {
l := len(rcv)
Expand Down
24 changes: 24 additions & 0 deletions typewriters/genwriter/test/thing_gen.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,30 @@ import (
// Things is a slice of type Thing, for use with gen methods below. Use this type where you would use []Thing. (This is required because slices cannot be method receivers.)
type Things []Thing

// ThingPredicate is a function that accepts a Thing and returns a bool. Used with gen methods below. Use this type where you would use func(Thing) bool.
type ThingPredicate func(item Thing) bool

// And combines two predicates into a new predicate that is satisfied if both of the original predicates are satisfied
func (rcv ThingPredicate) And(other ThingPredicate) ThingPredicate {
return func(item Thing) bool {
return rcv(item) && other(item)
}
}

// Or combines two predicates into a new predicate that is satisfied if either of the original predicates is satisfied
func (rcv ThingPredicate) Or(other ThingPredicate) ThingPredicate {
return func(item Thing) bool {
return rcv(item) || other(item)
}
}

// Not inverts a predicate that is satisfied if the original predicates is not satisfied
func (rcv ThingPredicate) Not() ThingPredicate {
return func(item Thing) bool {
return !rcv(item)
}
}

// All verifies that all elements of Things return true for the passed func. See: http://clipperhouse.github.io/gen/#All
func (rcv Things) All(fn func(Thing) bool) bool {
for _, v := range rcv {
Expand Down
61 changes: 61 additions & 0 deletions typewriters/genwriter/test/things_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,67 @@ import (
"testing"
)

func TestAnd(t *testing.T) {
True := ThingPredicate(func(x Thing) bool { return true })
False := ThingPredicate(func(x Thing) bool { return false })

ff := False.And(False)
ft := False.And(True)
tt := True.And(True)
tf := True.And(False)

if ff(zero) {
t.Errorf("And FF should not be true")
}
if ft(zero) {
t.Errorf("And FT should not be true")
}
if !tt(zero) {
t.Errorf("And TT should be true")
}
if tf(zero) {
t.Errorf("And TF should not be true")
}
}

func TestOr(t *testing.T) {
True := ThingPredicate(func(x Thing) bool { return true })
False := ThingPredicate(func(x Thing) bool { return false })

ff := False.Or(False)
ft := False.Or(True)
tt := True.Or(True)
tf := True.Or(False)

if ff(zero) {
t.Errorf("Or FF should not be true")
}
if !ft(zero) {
t.Errorf("Or FT should be true")
}
if !tt(zero) {
t.Errorf("Or TT should be true")
}
if !tf(zero) {
t.Errorf("Or TF should be true")
}
}

func TestNot(t *testing.T) {
True := ThingPredicate(func(x Thing) bool { return true })
False := ThingPredicate(func(x Thing) bool { return false })

expectFalse := True.Not()
expectTrue := False.Not()

if expectFalse(zero) {
t.Errorf("Not True should not be true")
}
if !expectTrue(zero) {
t.Errorf("Not False should be true")
}
}

func TestAll(t *testing.T) {
all1 := things.All(func(x Thing) bool {
return x.Name == "First"
Expand Down