README.md (5113B)
1 # streams 2 3 ActivityStreams vocabularies automatically code-generated with `astool`. 4 5 ## Reference & Tutorial 6 7 The [go-fed website](https://go-fed.org/) contains tutorials and reference 8 materials, in addition to the rest of this README. 9 10 ## How To Use 11 12 ``` 13 go get github.com/go-fed/activity 14 ``` 15 16 All generated types and properties are interfaces in 17 `github.com/go-fed/streams/vocab`, but note that the constructors and supporting 18 functions live in `github.com/go-fed/streams`. 19 20 To create a type and set properties: 21 22 ```golang 23 var actorURL *url.URL = // ... 24 25 // A new "Create" Activity. 26 create := streams.NewActivityStreamsCreate() 27 // A new "actor" property. 28 actor := streams.NewActivityStreamsActorProperty() 29 actor.AppendIRI(actorURL) 30 // Set the "actor" property on the "Create" Activity. 31 create.SetActivityStreamsActor(actor) 32 ``` 33 34 To process properties on a type: 35 36 ```golang 37 // Returns true if the "Update" has at least one "object" with an IRI value. 38 func hasObjectWithIRIValue(update vocab.ActivityStreamsUpdate) bool { 39 objectProperty := update.GetActivityStreamsObject() 40 // Any property may be nil if it was either empty in the original JSON or 41 // never set on the golang type. 42 if objectProperty == nil { 43 return false 44 } 45 // The "object" property is non-functional: it could have multiple values. The 46 // generated code has slightly different methods for a functional property 47 // versus a non-functional one. 48 // 49 // While it may be easy to ignore multiple values in other languages 50 // (accidentally or purposefully), go-fed is designed to make it hard to do 51 // so. 52 for iter := objectProperty.Begin(); iter != objectProperty.End(); iter = iter.Next() { 53 // If this particular value is an IRI, return true. 54 if iter.IsIRI() { 55 return true 56 } 57 } 58 // All values are literal embedded values and not IRIs. 59 return false 60 } 61 ``` 62 63 The ActivityStreams type hierarchy of "extends" and "disjoint" is not the same 64 as the Object Oriented definition of inheritance. It is also not the same as 65 golang's interface duck-typing. Helper functions are provided to guarantee that 66 an application's logic can correctly apply the type hierarchy. 67 68 ```golang 69 thing := // Pick a type from streams.NewActivityStreams<Type>() 70 if streams.ActivityStreamsObjectIsDisjointWith(thing) { 71 fmt.Printf("The \"Object\" type is Disjoint with the %T type.\n", thing) 72 } 73 if streams.ActivityStreamsLinkIsExtendedBy(thing) { 74 fmt.Printf("The %T type Extends from the \"Link\" type.\n", thing) 75 } 76 if streams.ActivityStreamsActivityExtends(thing) { 77 fmt.Printf("The \"Activity\" type extends from the %T type.\n", thing) 78 } 79 ``` 80 81 When given a generic JSON payload, it can be resolved to a concrete type by 82 creating a `streams.JSONResolver` and giving it a callback function that accepts 83 the interesting concrete type: 84 85 ```golang 86 // Callbacks must be in the form: 87 // func(context.Context, <TypeInterface>) error 88 createCallback := func(c context.Context, create vocab.ActivityStreamsCreate) error { 89 // Do something with 'create' 90 fmt.Printf("createCallback called: %T\n", create) 91 return nil 92 } 93 updateCallback := func(c context.Context, update vocab.ActivityStreamsUpdate) error { 94 // Do something with 'update' 95 fmt.Printf("updateCallback called: %T\n", update) 96 return nil 97 } 98 jsonResolver, err := streams.NewJSONResolver(createCallback, updateCallback) 99 if err != nil { 100 // Something in the setup was wrong. For example, a callback has an 101 // unsupported signature and would never be called 102 panic(err) 103 } 104 // Create a context, which allows you to pass data opaquely through the 105 // JSONResolver. 106 c := context.Background() 107 // Example 15 of the ActivityStreams specification. 108 b := []byte(`{ 109 "@context": "https://www.w3.org/ns/activitystreams", 110 "summary": "Sally created a note", 111 "type": "Create", 112 "actor": { 113 "type": "Person", 114 "name": "Sally" 115 }, 116 "object": { 117 "type": "Note", 118 "name": "A Simple Note", 119 "content": "This is a simple note" 120 } 121 }`) 122 var jsonMap map[string]interface{} 123 if err = json.Unmarshal(b, &jsonMap); err != nil { 124 panic(err) 125 } 126 // The createCallback function will be called. 127 err = jsonResolver.Resolve(c, jsonMap) 128 if err != nil && !streams.IsUnmatchedErr(err) { 129 // Something went wrong 130 panic(err) 131 } else if streams.IsUnmatchedErr(err) { 132 // Everything went right but the callback didn't match or the ActivityStreams 133 // type is one that wasn't code generated. 134 fmt.Println("No match: ", err) 135 } 136 ``` 137 138 A `streams.TypeResolver` is similar but uses the golang types instead. It 139 accepts the generic `vocab.Type`. This is the abstraction when needing to handle 140 any ActivityStreams type. The function `ToType` can convert a JSON-decoded-map 141 into this kind of value if needed. 142 143 A `streams.PredicatedTypeResolver` lets you apply a boolean predicate function 144 that acts as a check whether a callback is allowed to be invoked. 145 146 ## FAQ 147 148 ### Why Are Empty Properties Nil And Not Zero-Valued? 149 150 Due to implementation design decisions, it would require a lot of plumbing to 151 ensure this would work properly. It would also require allocation of a 152 non-trivial amount of memory.