Add initial project files
This commit is contained in:
37
pkg/people/birthday.go
Normal file
37
pkg/people/birthday.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package people
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Declaring birthday layout constant
|
||||
const birthdayLayout = "2006-01-02" // YYYY-MM-DD - is default birthday layout
|
||||
|
||||
// Birthday is a custom encapsulation type for time.Time to handle birthdays
|
||||
// by decoding and encoding from and to JSON format
|
||||
type Birthday struct {
|
||||
time.Time // Embedding time.Time
|
||||
err error // Time parsing error
|
||||
}
|
||||
|
||||
// UnmarshalJSON decodes a date text to time.Time object
|
||||
func (t *Birthday) UnmarshalJSON(bytes []byte) (err error) {
|
||||
// 1. Decode the bytes into an int64
|
||||
// 2. Parse the unix timestamp
|
||||
|
||||
// Is value quoted?
|
||||
if rune(bytes[0]) == '"' || rune(bytes[0]) == '\'' {
|
||||
bytes = bytes[1 : len(bytes)-1] // remove quotes
|
||||
}
|
||||
|
||||
// Parse string to time.Time
|
||||
t.Time, t.err = time.Parse(birthdayLayout, string(bytes))
|
||||
t.err = err
|
||||
return
|
||||
}
|
||||
|
||||
// MarshalJSON encodes a time.Time object to date JSON text
|
||||
func (t Birthday) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(t.Time.Format(birthdayLayout))
|
||||
}
|
||||
7
pkg/people/identity.go
Normal file
7
pkg/people/identity.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package people
|
||||
|
||||
// Identity represents the true identity of a person
|
||||
type Identity struct {
|
||||
FirstName string `json:"firstName" deesee:"encode"`
|
||||
LastName string `json:"lastName" deesee:"encode"`
|
||||
}
|
||||
76
pkg/people/person.go
Normal file
76
pkg/people/person.go
Normal file
@@ -0,0 +1,76 @@
|
||||
package people
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
// Person data representation.
|
||||
//
|
||||
// NOTE:
|
||||
//
|
||||
// The reason the people package was created is reusability.
|
||||
//
|
||||
// Personalities are used more often and are more general
|
||||
// in applications than superheroes.
|
||||
//
|
||||
// # Some additional reflection:
|
||||
//
|
||||
// * Superheroes are a special case of people.
|
||||
// * Normal persons are not superheros at all, but:
|
||||
// - An ordinary person, over time, can gain superpowers
|
||||
// - Do we need to handle them as no superhero?
|
||||
// - A superhero can lose his superpowers.
|
||||
// - But he was already using them, so does that mean he's no longer a superhero?
|
||||
// - Batman, as example has, no superpowers, so he is not a DeeSee "superhero".
|
||||
//
|
||||
// Additional to think about, is that People, a man,
|
||||
// can have many personalities(natural, juridical, fantasy, artistic, fake),
|
||||
// also superheros can become transformation(magically) to another person(becomes additional personalities),
|
||||
// so the person becomes more superpowers? (rhetorical question)
|
||||
//
|
||||
// .
|
||||
// Anyway, to work anything about, there is a `Person` a struct which has a Method `IsSuperHero() bool.
|
||||
type Person struct {
|
||||
// Name of a Person
|
||||
Name string `json:"name"`
|
||||
|
||||
// Identity of a Person.
|
||||
// Sure, people can have many identities, fakes too.
|
||||
// But at the time we handle only one-to-one person to identity relation
|
||||
Identity *Identity `json:"identity"`
|
||||
|
||||
// SuperPowers list
|
||||
SuperPowers *SuperPowers `json:"superpowers"`
|
||||
|
||||
// Birthday formatted as `YYYY-MM-DD` text string, there is no time zone and time.
|
||||
// The format-Layout is defined in internal `birthdayLayout` constant
|
||||
Birthday *Birthday `json:"birthday"`
|
||||
}
|
||||
|
||||
// IsSuperHero the person?
|
||||
func (p *Person) IsSuperHero() bool {
|
||||
return p.SuperPowers != nil && len(*p.SuperPowers) > 0
|
||||
}
|
||||
|
||||
// Has person the superpower?
|
||||
// In other words: is the person a superhero at all?
|
||||
func (p *Person) Has(power string) bool {
|
||||
// The excessive use of the IsSuperHero() method is intentional, but for clarity,
|
||||
// because it better describes what's going on.
|
||||
//
|
||||
// In the real world we deal with optimization of methods
|
||||
// and of course it's better not to do so...
|
||||
return p.IsSuperHero() && p.SuperPowers.Contains(power)
|
||||
}
|
||||
|
||||
// Marshal marshals the person to JSON
|
||||
func (p *Person) Marshal() ([]byte, error) {
|
||||
return json.Marshal(p)
|
||||
|
||||
}
|
||||
|
||||
func Unmarshal(js string) (p *Person, err error) {
|
||||
p = &Person{}
|
||||
err = json.Unmarshal([]byte(js), p)
|
||||
return
|
||||
}
|
||||
48
pkg/people/person_test.go
Normal file
48
pkg/people/person_test.go
Normal file
@@ -0,0 +1,48 @@
|
||||
package people
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestPersonModel tests the Unmarshalling and properties.
|
||||
func TestPersonModel(t *testing.T) {
|
||||
p, err := Unmarshal(`{"name": "batman","identity": {"firstName": "bruce","lastName": "wayne"},"birthday": "1915-04-17"}`)
|
||||
if err != nil {
|
||||
t.Fatalf("Unmarshal() error = %v", err)
|
||||
}
|
||||
|
||||
if p.Name != "batman" ||
|
||||
p.Identity.FirstName != "bruce" ||
|
||||
p.Identity.LastName != "wayne" ||
|
||||
p.Birthday.Year() != 1915 {
|
||||
t.Fatalf("Unmarshal() error")
|
||||
}
|
||||
|
||||
if p.IsSuperHero() {
|
||||
t.Fatalf("Batman has no superpowers")
|
||||
}
|
||||
|
||||
js, err := p.Marshal()
|
||||
if err != nil {
|
||||
t.Fatalf("Marshal() error = %v", err)
|
||||
}
|
||||
|
||||
if !strings.Contains(string(js), `"birthday":"1915-04-17"`) {
|
||||
t.Fatalf("Marshal() birthday error")
|
||||
}
|
||||
|
||||
// Batman with superpowers
|
||||
p, err = Unmarshal(`{"name": "batman","identity": {"firstName": "bruce","lastName": "wayne"},"birthday": "1915-04-17","superpowers": ["money","intelligence"]}`)
|
||||
if err != nil {
|
||||
t.Fatalf("Unmarshal() error = %v", err)
|
||||
}
|
||||
|
||||
if !p.IsSuperHero() || !p.Has("money") || !p.Has("intelligence") {
|
||||
t.Fatalf("Batman has some superpowers as well")
|
||||
}
|
||||
|
||||
if p.Has("flight") {
|
||||
t.Fatalf("Batman has no superpowers")
|
||||
}
|
||||
}
|
||||
19
pkg/people/power.go
Normal file
19
pkg/people/power.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package people
|
||||
|
||||
import "strings"
|
||||
|
||||
// SuperPowers list of an SuperHero.
|
||||
// Normal person has no super-power?
|
||||
// Perhaps we just don't know enough to distinguish the qualities in each individual person. Sad but true.
|
||||
type SuperPowers []string
|
||||
|
||||
// Contains superpower list the strength?
|
||||
// Not go idiomatic? Okay, we can discuss it.
|
||||
func (s *SuperPowers) Contains(power string) bool {
|
||||
for _, p := range *s {
|
||||
if strings.EqualFold(p, power) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
Reference in New Issue
Block a user