activity/streams
2019-02-24 16:27:02 +01:00
..
impl/activitystreams Add AppendType and PrependType 2019-02-15 23:54:09 +01:00
values Update generated code for tests. 2019-02-04 22:29:04 +01:00
vocab Add AppendType and PrependType 2019-02-15 23:54:09 +01:00
gen_consts.go Generate constants for type and property names 2019-02-15 23:11:08 +01:00
gen_doc.go
gen_init.go Add orderedItems property and properly propagate it. 2019-02-06 22:18:08 +01:00
gen_json_resolver.go Regenerate code that is collision resistant to extensions. 2019-02-09 21:56:00 +01:00
gen_manager.go Regenerate code that is collision resistant to extensions. 2019-02-09 21:56:00 +01:00
gen_pkg_activitystreams_disjoint.go Update generated code for tests. 2019-02-04 22:29:04 +01:00
gen_pkg_activitystreams_extendedby.go Update generated code for tests. 2019-02-04 22:29:04 +01:00
gen_pkg_activitystreams_extends.go Regenerate code that is collision resistant to extensions. 2019-02-09 21:56:00 +01:00
gen_pkg_activitystreams_property_constructors.go Regenerate code that is collision resistant to extensions. 2019-02-09 21:56:00 +01:00
gen_pkg_activitystreams_type_constructors.go Regenerate code that is collision resistant to extensions. 2019-02-09 21:56:00 +01:00
gen_resolver_utils.go Generate generic ToType function. 2019-02-15 22:47:59 +01:00
gen_type_predicated_resolver.go Change GetName to GetTypeName 2019-02-15 22:15:25 +01:00
gen_type_resolver.go Change GetName to GetTypeName 2019-02-15 22:15:25 +01:00
README.md Update READMEs 2019-02-24 16:27:02 +01:00
streams_manual_data_test.go Regenerate code that is collision resistant to extensions. 2019-02-09 21:56:00 +01:00
streams_test.go Regenerate code that is collision resistant to extensions. 2019-02-09 21:56:00 +01:00
vocab_test.go Deduplicate tests, increase coverage in the process. 2019-02-09 13:47:51 +01:00

streams

ActivityStreams vocabularies automatically code-generated with astool.

How To Use

go get github.com/go-fed/streams

All generated types and properties are interfaces in github.com/go-fed/streams/vocab, but note that the constructors and supporting functions live in github.com/go-fed/streams.

To create a type and set properties:

var actorURL *url.URL

// A new "Create" Activity.
create := streams.NewActivityStreamsCreate()
// A new "actor" property.
actor := streams.NewActivityStreamsActorProperty()
actor.AppendIRI(actorURL)
// Set the "actor" property on the "Create" Activity.
create.SetActivityStreamsActor(actor)

To process properties on a type:

// Returns true if the "Update" has at least one "object" with an IRI value.
func hasObjectWithIRIValue(update vocab.ActivityStreamsUpdate) bool {
  objectProperty := update.GetActivityStreamsObject()
  // Any property may be nil if it was empty in the original JSON or simply not
  // set after creation.
  if objectProperty == nil {
    return false
  }
  // The "object" property is non-functional: it could have multiple values. The
  // generated code has slightly different methods for a functional property
  // versus a non-functional one.
  //
  // While it may be easy to ignore multiple values in other languages
  // (accidentally, or purposefully), go-fed is designed to make it hard to do
  // so.
  for iter := objectProperty.Begin(); iter != objectProperty.End(); iter = iter.Next() {
    // If this particular value is an IRI, return true.
    if iter.IsIRI() {
      return true
    }
  }
  // All values are literal embedded values and not IRIs.
  return false
}

The ActivityStreams type hierarchy of "extends" and "disjoint" is not the same as Object Oriented inheritance. Helper functions are provided to guarantee that an application's logic is sound:

thing := // Pick a type from streams.NewActivityStreams<Type>()
if streams.ActivityStreamsObjectIsDisjointWith(thing) {
  fmt.Printf("The \"Object\" type is Disjoint with the %T type.\n", thing)
}
if streams.ActivityStreamsLinkIsExtendedBy(thing) {
  fmt.Printf("The %T type Extends from the \"Link\" type.\n", thing)
}
if streams.ActivityStreamsActivityExtends(thing) {
  fmt.Printf("The \"Activity\" type extends from the %T type.\n", thing)
}

When given a generic JSON payload, it can be resolved to a concrete type by creating a streams.JSONResolver and giving it a callback function that accepts the interesting concrete type:

// Callbacks must be in the form:
//   func(context.Context, <TypeInterface>) error
createCallback := func(c context.Context, create vocab.ActivityStreamsCreate) error {
  // Do something with 'create'
  return error
}
updateCallback := func(c context.Context, update vocab.ActivityStreamsUpdate) error {
  // Do something with 'update'
  return error
}
jsonResolver, err := streams.NewJSONResolver(createCallback, updateCallback)
if err != nil {
  // Something in the setup was wrong. For example, a callback has an
  // unsupported signature and would never be called
  panic(err)
}
// Create a context, which allows you to pass data opaquely through the
// JSONResolver.
c := context.Background()
// Example 15 of the ActivityStreams specification.
b := []byte(`{
  "@context": "https://www.w3.org/ns/activitystreams",
  "summary": "Sally created a note",
  "type": "Create",
  "actor": {
    "type": "Person",
    "name": "Sally"
  },
  "object": {
    "type": "Note",
    "name": "A Simple Note",
    "content": "This is a simple note"
  }
}`)
var jsonMap map[string]interface{}
if err = json.Unmarshal(b, &jsonMap); err != nil {
  panic(err)
}
// The createCallback function will be called.
err = jsonResolver.Resolve(c, jsonMap)
if err != nil && !streams.IsUnmatchedErr(err) {
  // Something went wrong
  panic(err)
} else if streams.IsUnmatchedErr(err) {
  // Everything went right but the callback didn't match or the ActivityStreams
  // type is one that wasn't code generated.
  fmt.Println("No match: ", err)
}

A streams.TypeResolver is similar but uses the golang types instead. It accepts the generic vocab.Type. This is the abstraction when needing to handle any ActivityStreams type. The function ToType can convert a JSON-decoded-map into this kind of value if needed.

A streams.PredicatedTypeResolver lets you apply a boolean predicate function that acts as a check whether a callback is allowed to be invoked.

FAQ

Why Are Empty Properties Nil And Not Zero-Valued?

Due to implementation design decisions, it would require a lot of plumbing to ensure this would work properly. It would also require allocation of a non-trivial amount of memory.