picatrix 0.5.0 → 0.5.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +17 -37
- data/Rakefile +35 -0
- data/bin/console +8 -4
- data/bin/post-receive.sh +3 -0
- data/bin/setup +7 -1
- data/generated/zoo.dot +37 -0
- data/generated/zoo.dot.png +0 -0
- data/{lib/picatrix/generated/view_submissions.rb → generated/zoo/resources/animals.rb} +10 -13
- data/generated/zoo/support/mason.rb +114 -0
- data/generated/zoo/support/namespaces.json +7 -0
- data/generated/zoo/support/picatrix.mason.pb.rb +95 -0
- data/generated/zoo/support/zoo.pb.rb +76 -0
- data/generated/zoo/support/zoo.proto +65 -0
- data/generated/zoo/zoo.rb +116 -0
- data/lib/picatrix.rb +25 -10
- data/lib/picatrix/buffy.rb +38 -40
- data/lib/picatrix/cruddy.rb +18 -16
- data/lib/picatrix/jenny.rb +31 -5
- data/lib/picatrix/link_relation_index.rb +1 -1
- data/lib/picatrix/pacman.rb +12 -6
- data/lib/picatrix/protoc.rb +7 -0
- data/lib/picatrix/prototyper.rb +56 -0
- data/lib/picatrix/routes.rb +40 -11
- data/{definitions/mason.proto → lib/picatrix/support/picatrix.mason.proto} +16 -13
- data/lib/picatrix/templates/app.proto +42 -0
- data/lib/picatrix/templates/app.rb +24 -136
- data/lib/picatrix/templates/mason.rb +114 -0
- data/lib/picatrix/templates/namespaces.json +7 -0
- data/lib/picatrix/templates/type.rb +6 -3
- data/lib/picatrix/transform_to_hash.rb +2 -2
- data/lib/picatrix/version.rb +1 -1
- data/notes.md +34 -0
- metadata +20 -10
- data/definitions/app.proto +0 -47
- data/lib/picatrix/fixtures/view_submission.rb +0 -19
- data/lib/picatrix/generated/app.pb.rb +0 -79
- data/lib/picatrix/generated/app.rb +0 -209
- data/lib/picatrix/generated/mason.pb.rb +0 -90
- data/lib/picatrix/generated/mason/namespaces.json +0 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cfdec538080a26d183c8338917e4906c48f12df3
|
4
|
+
data.tar.gz: 9795226f4ca351fda88a3caa0a9bd668e941f766
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d6af26dcf377adbd88a4e7bbf057b0c367a1c4b4b90503f292a1c1f08295562fc9a6f48b3356d4ec10d4b6ba65e0cd2bd74fca7212f2725e60e3488c6df90552
|
7
|
+
data.tar.gz: 6a9cf7fd9b51d021800a8c726f96cc31c4d952b2741e1d01d76abaa9c30e20730a6574c3f305c5dfe58db644b20d64d0d60933ed83685a0828a4129bb641e656
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -9,27 +9,21 @@
|
|
9
9
|
|
10
10
|
An opinionated hypermedia API generator. :grin:
|
11
11
|
|
12
|
-
## Requires
|
12
|
+
## Requires
|
13
13
|
|
14
14
|
- valid `.dot` file with [additional style guidelines](#dot-file-styles)
|
15
15
|
- Protobuf [definitions of inputs/outputs](#protobuf-definitions-of-representations) for all state transitions, by node
|
16
16
|
|
17
17
|
|
18
|
-
## Outputs
|
18
|
+
## Outputs
|
19
19
|
|
20
20
|
- basic Sinatra app and mock objects fitting your schema
|
21
|
-
-
|
22
|
-
-
|
23
|
-
-
|
24
|
-
-
|
21
|
+
- intended to be a scaffold for your client to immediately test against, and maybe for you to build from or to (to as in match its interface in contract)
|
22
|
+
- `application/vnd.mason+json` hypermedia affordances
|
23
|
+
- CRUD + Filter actions are defined for you by default
|
24
|
+
- `$select` and `$expand` URL conventions apply
|
25
25
|
- state diagram image ([see below](https://github.com/mooreniemi/picatrix#sample-image-output))
|
26
26
|
|
27
|
-
## About
|
28
|
-
|
29
|
-
Directed graphs get complicated, but can be divided into simpler subgraphs. Picatrix is currently useful for only a single workflow (and barely that right now, as we're way before a real release). Integrated workflows will involve generating several route sets that will be required into a single Sinatra app. Some workflows may even be representable as DAGs/trees.
|
30
|
-
|
31
|
-
The ideal way to use Picatrix is to do an exercise in designing your API "outside in" where all inputs/outputs are defined, and a state diagram is drawn. This diagram can be translated to a dot file, and the inputs/outputs can be translated into hypermedia assets.
|
32
|
-
|
33
27
|
## Installation
|
34
28
|
|
35
29
|
Install system dependencies (Mac OS sample):
|
@@ -54,6 +48,8 @@ Or install it yourself as:
|
|
54
48
|
|
55
49
|
## getting ready
|
56
50
|
|
51
|
+
- easiest thing to do is use the rake task provided to make yourself a copy of the sample `dot` file which you can edit
|
52
|
+
|
57
53
|
### .dot file styles
|
58
54
|
|
59
55
|
- edge style defaults to `arrowhead=odiamond` to signify default requests are `GET`
|
@@ -71,12 +67,15 @@ Or install it yourself as:
|
|
71
67
|
- from the perspective of the client at any request, what does it need to make the request and have the server fufill its contract?
|
72
68
|
- from the perspective of the server, what might the client want in response for it to make any interesting subsequent requests from this node in the workflow?
|
73
69
|
- inputs will map onto controls, and outputs will map on to representations (dereferenced content)
|
74
|
-
## Usage
|
75
70
|
|
76
|
-
|
71
|
+
## Usage
|
77
72
|
|
78
73
|
```bash
|
79
|
-
$ bin/picapica dg_api.dot app_name
|
74
|
+
$ bin/picapica prep dg_api.dot app_name
|
75
|
+
=> generated/app_name.proto
|
76
|
+
# at this point you flesh out your schema in your proto file
|
77
|
+
$ vi generated/app_name.proto
|
78
|
+
$ bin/picapica g dg_api.dot app_name
|
80
79
|
=> generated/app_name.rb
|
81
80
|
```
|
82
81
|
|
@@ -89,29 +88,10 @@ $ bin/picapica dg_api.dot app_name
|
|
89
88
|
|
90
89
|
### how to interpret the graph
|
91
90
|
|
92
|
-
Nodes and edges translate roughly into application and/or resource states, and state transitions.
|
93
|
-
|
94
|
-
Labels on edges are link relations. Confusingly the actual _URL_ of the state transition (moving to the next state) is still the target node (essentially, think of the state transition name as the same as the name of the ideal output) _and_ its link relation to its source. So, `A (make_B_do_x)-> B` is `B/make_B_do_x`. This is still (obviously) sketchy.
|
95
|
-
|
96
|
-
## Development
|
97
|
-
|
98
|
-
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
99
|
-
|
100
|
-
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
101
|
-
|
102
|
-
### TODO
|
103
|
-
|
104
|
-
process goes: .dot file + `prep` (1)-> proto file from nodes + user input into .proto + `proto` (2)-> ruby representation classes + user input to fixturize (3)-> `g` (4)-> `rackup lib/picatrix/generated/app.rb`
|
105
|
-
|
106
|
-
1. outside in design process produced a diagram, which is written into a .dot file
|
107
|
-
2. all nodes are representations of resources, and thus get proto objects generated for them, here is where the inputs/outputs are formed into a schema
|
108
|
-
3. representation nodes now have ruby classes that reflect the information that will be sent back and forth in messages so we generate the app
|
109
|
-
4. boot up the app
|
110
|
-
|
111
|
-
## Contributing
|
112
|
-
|
113
|
-
Bug reports and pull requests are welcome on [GitHub](https://github.com/mooreniemi/picatrix). This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
91
|
+
Nodes and edges translate roughly into application and/or resource states, and state transitions. Labels on edges are link relations.
|
114
92
|
|
93
|
+
## see also
|
94
|
+
[more notes](more-notes.md)
|
115
95
|
|
116
96
|
## License
|
117
97
|
|
data/Rakefile
CHANGED
@@ -5,3 +5,38 @@ load 'protobuf/tasks/compile.rake'
|
|
5
5
|
RSpec::Core::RakeTask.new(:spec)
|
6
6
|
|
7
7
|
task :default => :spec
|
8
|
+
|
9
|
+
namespace :test do
|
10
|
+
desc "flesh out the proto file so you can generate from it"
|
11
|
+
task :setup do
|
12
|
+
puts "copying app.proto from spec/support/definitions/"
|
13
|
+
`cp spec/support/definitions/app.proto generated/app.proto`
|
14
|
+
end
|
15
|
+
|
16
|
+
desc "regen ruby from protobuf"
|
17
|
+
task :regen do
|
18
|
+
puts "regenerating ruby classes from mock definitions"
|
19
|
+
`protoc -I ./lib/picatrix/support -I ./generated --ruby_out ./generated/support generated/app.proto`
|
20
|
+
end
|
21
|
+
|
22
|
+
desc "serve example app (you may need to run test:setup first)"
|
23
|
+
task :serve do
|
24
|
+
`rackup generated/app.rb`
|
25
|
+
end
|
26
|
+
|
27
|
+
desc "generate new image of example graph"
|
28
|
+
task :png do
|
29
|
+
`dot -Tpng spec/support/dg_api.dot -o spec/support/dg_api.dot.png`
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
namespace :app do
|
34
|
+
desc "generate .dot file to work with"
|
35
|
+
task :dot, [:name] do |t, args|
|
36
|
+
`cp spec/support/dg_api.dot generated/#{args[:name]}.dot`
|
37
|
+
end
|
38
|
+
desc "serve your app by name"
|
39
|
+
task :serve, [:name] do |t, args|
|
40
|
+
`rackup generated/#{args[:name]}.rb`
|
41
|
+
end
|
42
|
+
end
|
data/bin/console
CHANGED
@@ -1,13 +1,17 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
3
|
require "bundler/setup"
|
4
|
+
|
5
|
+
$LOAD_PATH.unshift File.expand_path('../../generated', __FILE__)
|
4
6
|
require "picatrix"
|
5
7
|
|
6
|
-
|
7
|
-
`protoc
|
8
|
+
puts "regen mason"
|
9
|
+
`protoc --ruby_out . lib/picatrix/support/picatrix.mason.proto`
|
10
|
+
|
11
|
+
puts "regen test app"
|
12
|
+
`protoc -I ./lib/picatrix/support -I ./generated --ruby_out ./generated generated/app.proto`
|
8
13
|
|
9
|
-
require './
|
10
|
-
require './lib/picatrix/generated/app.pb.rb'
|
14
|
+
require './generated/app.pb.rb'
|
11
15
|
|
12
16
|
require "pry"
|
13
17
|
Pry.start
|
data/bin/post-receive.sh
ADDED
data/bin/setup
CHANGED
@@ -2,6 +2,12 @@
|
|
2
2
|
set -euo pipefail
|
3
3
|
IFS=$'\n\t'
|
4
4
|
|
5
|
+
echo "installing graphviz"
|
6
|
+
brew install graphviz
|
7
|
+
echo "installing protobuf"
|
8
|
+
brew install protobuf
|
9
|
+
echo "installing bundler"
|
10
|
+
gem install bundler
|
11
|
+
|
5
12
|
bundle install
|
6
13
|
|
7
|
-
# Do any other automated setup that you need to do here
|
data/generated/zoo.dot
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
digraph d {
|
2
|
+
// means default method is GET
|
3
|
+
sep="+25,25";
|
4
|
+
overlap=scalexy;
|
5
|
+
nodesep=0.6;
|
6
|
+
splines=true;
|
7
|
+
graph [overlap = false];
|
8
|
+
edge [
|
9
|
+
arrowhead=odiamond
|
10
|
+
// this is just to make it more readable, set it to anything you want
|
11
|
+
fontcolor=blue
|
12
|
+
]
|
13
|
+
|
14
|
+
// node definitions
|
15
|
+
// interpret shape=point as root
|
16
|
+
root [xlabel="inbound request" shape=point]
|
17
|
+
// shape=folder means collection
|
18
|
+
animals [label="View animals" shape=folder]
|
19
|
+
// shape=rectangle means item
|
20
|
+
animal [label="View animal" shape=rectangle]
|
21
|
+
|
22
|
+
// edge definitions
|
23
|
+
// source -> { target } [label="link relation name"]
|
24
|
+
root -> { animals } [label="newest"]
|
25
|
+
// style=dotted means inline representation
|
26
|
+
animals -> { animal } [label=":item_id"]
|
27
|
+
animals -> { animals } [label="filter"]
|
28
|
+
// style=bold && arrowhead=normal means POST
|
29
|
+
animals -> { animal } [label="create" style=bold arrowhead=normal]
|
30
|
+
// non-CRUD + Filter link relations will also be inflated
|
31
|
+
// animals -> { animals } [label="custom_relation" style=bold arrowhead=normal]
|
32
|
+
animal -> { animals } [label="newest"]
|
33
|
+
// color=red && fontcolor=red means DELETE
|
34
|
+
animals -> { animals } [label="newest" color=red fontcolor=red]
|
35
|
+
// arrowhead=normal means PATCH or PUT (if patch not supported)
|
36
|
+
animal -> { animal } [label="edit" arrowhead=normal]
|
37
|
+
}
|
Binary file
|
@@ -6,24 +6,18 @@ require 'faker'
|
|
6
6
|
# technically, this is just an interface to it
|
7
7
|
# so long as your persistance layer is able to implement
|
8
8
|
# the methods in this fixture, the app will generate
|
9
|
-
class
|
9
|
+
class AnimalsResource
|
10
10
|
# method used to return single item from a collection
|
11
11
|
# creates an instance on the fly
|
12
12
|
# both the find and collection methods use this
|
13
13
|
def self.create(item_id)
|
14
|
-
|
14
|
+
Animal.new(
|
15
15
|
{
|
16
16
|
# any "bare" Proto type will be given an associated Faker
|
17
17
|
|
18
18
|
:id => id = Faker::Number.number(6),
|
19
19
|
|
20
|
-
:
|
21
|
-
|
22
|
-
:photo => Faker::Lorem.word,
|
23
|
-
|
24
|
-
:controls => ViewSubmission::Controls.new(minimal_controls_for(item_id)),
|
25
|
-
|
26
|
-
:attachments => [ViewSubmission::Attachment.new(params = {})],
|
20
|
+
:controls => Animal::Controls.new(minimal_controls_for(item_id)),
|
27
21
|
|
28
22
|
}
|
29
23
|
)
|
@@ -36,14 +30,17 @@ class ViewSubmissionsResource
|
|
36
30
|
rand(10).times {
|
37
31
|
collection << self.find(rand(1000))
|
38
32
|
}
|
39
|
-
|
33
|
+
Animals.new(collection: collection)
|
40
34
|
end
|
41
|
-
|
35
|
+
def self.as_hashes
|
36
|
+
self.all.collection.map(&:to_hash)
|
37
|
+
end
|
38
|
+
# this makes no sense on this class
|
42
39
|
def self.minimal_controls_for(item_id)
|
43
40
|
host = "http://localhost:9292"
|
44
41
|
{
|
45
|
-
self: Mason::Self.new(href: "#{host}/
|
46
|
-
up: Mason::Up.new(href: "#{host}/
|
42
|
+
self: ::Picatrix::Mason::Self.new(href: "#{host}/animals/#{item_id}"),
|
43
|
+
up: ::Picatrix::Mason::Up.new(href: "#{host}/animals")
|
47
44
|
}
|
48
45
|
end
|
49
46
|
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
module Picatrix
|
2
|
+
module Mason
|
3
|
+
def mason_up(type)
|
4
|
+
if type.to_s.pluralize == type
|
5
|
+
proc do |path, params|
|
6
|
+
{
|
7
|
+
up: Mason::Up.new(
|
8
|
+
{
|
9
|
+
href: "http://localhost:9292/#{}"
|
10
|
+
}
|
11
|
+
).to_hash
|
12
|
+
}
|
13
|
+
end
|
14
|
+
else
|
15
|
+
proc do |path, params|
|
16
|
+
{
|
17
|
+
up: Mason::Up.new(
|
18
|
+
{
|
19
|
+
href: "http://localhost:9292/#{path.pluralize}"
|
20
|
+
}
|
21
|
+
).to_hash
|
22
|
+
}
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
def mason_self(item)
|
27
|
+
proc do |path, params|
|
28
|
+
if params[:item_id]
|
29
|
+
{
|
30
|
+
self: Mason::Self.new(
|
31
|
+
{
|
32
|
+
href: "http://localhost:9292/#{path}/#{params[:item_id]}"
|
33
|
+
}
|
34
|
+
).to_hash
|
35
|
+
}
|
36
|
+
else
|
37
|
+
{
|
38
|
+
self: Mason::Self.new(
|
39
|
+
{
|
40
|
+
href: "http://localhost:9292/#{path}"
|
41
|
+
}
|
42
|
+
).to_hash
|
43
|
+
}
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
def edit(item)
|
48
|
+
proc do |path, params|
|
49
|
+
{
|
50
|
+
"z:edit": Mason::Edit.new(
|
51
|
+
{
|
52
|
+
href: "http://localhost:9292/#{path}/#{params[:item_id]}",
|
53
|
+
method: Mason::HTTPVerb::PATCH
|
54
|
+
}
|
55
|
+
).to_hash
|
56
|
+
}
|
57
|
+
end
|
58
|
+
end
|
59
|
+
def remove(item)
|
60
|
+
proc do |path, params|
|
61
|
+
{
|
62
|
+
"z:remove": Mason::Remove.new(
|
63
|
+
{
|
64
|
+
href: "http://localhost:9292/#{path}/#{params[:item_id]}",
|
65
|
+
method: Mason::HTTPVerb::DELETE
|
66
|
+
}
|
67
|
+
).to_hash
|
68
|
+
}
|
69
|
+
end
|
70
|
+
end
|
71
|
+
def create(collection)
|
72
|
+
proc do |path, params|
|
73
|
+
collection_name = collection.to_s.camelize
|
74
|
+
{
|
75
|
+
"z:create": Mason::Create.new(
|
76
|
+
{
|
77
|
+
href: "http://localhost:9292/#{path}",
|
78
|
+
method: Mason::HTTPVerb::POST,
|
79
|
+
title: "Create a new #{collection_name}",
|
80
|
+
create_template: "#{collection_name}CreateTemplate".constantize.new({})
|
81
|
+
}
|
82
|
+
)
|
83
|
+
}
|
84
|
+
end
|
85
|
+
end
|
86
|
+
def filter(collection)
|
87
|
+
proc do |path, params|
|
88
|
+
if params[:newest]
|
89
|
+
{
|
90
|
+
"z:filter": Mason::Filter.new(
|
91
|
+
{
|
92
|
+
href: "http://localhost:9292/#{path}?filter=newest",
|
93
|
+
isHrefTemplate: true,
|
94
|
+
method: Mason::HTTPVerb::GET,
|
95
|
+
title: "Show the newest additions to #{collection}"
|
96
|
+
}
|
97
|
+
).to_hash
|
98
|
+
}
|
99
|
+
else
|
100
|
+
{
|
101
|
+
"z:filter": Mason::Filter.new(
|
102
|
+
{
|
103
|
+
href: "http://localhost:9292/#{path}?$select&{field1}={value1}&{field2}={value2}",
|
104
|
+
isHrefTemplate: true,
|
105
|
+
method: Mason::HTTPVerb::GET,
|
106
|
+
title: "Filter using $select on schema fields"
|
107
|
+
}
|
108
|
+
).to_hash
|
109
|
+
}
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
##
|
4
|
+
# This file is auto-generated. DO NOT EDIT!
|
5
|
+
#
|
6
|
+
require 'protobuf/message'
|
7
|
+
|
8
|
+
module Picatrix
|
9
|
+
module Mason
|
10
|
+
|
11
|
+
##
|
12
|
+
# Enum Classes
|
13
|
+
#
|
14
|
+
class HTTPVerb < ::Protobuf::Enum
|
15
|
+
define :GET, 1
|
16
|
+
define :PUT, 2
|
17
|
+
define :PATCH, 3
|
18
|
+
define :DELETE, 4
|
19
|
+
define :POST, 5
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
##
|
24
|
+
# Message Classes
|
25
|
+
#
|
26
|
+
class Self < ::Protobuf::Message; end
|
27
|
+
class Up < ::Protobuf::Message; end
|
28
|
+
class Filter < ::Protobuf::Message; end
|
29
|
+
class Create < ::Protobuf::Message; end
|
30
|
+
class Edit < ::Protobuf::Message; end
|
31
|
+
class Remove < ::Protobuf::Message; end
|
32
|
+
class Controls < ::Protobuf::Message; end
|
33
|
+
|
34
|
+
|
35
|
+
##
|
36
|
+
# Message Fields
|
37
|
+
#
|
38
|
+
class Self
|
39
|
+
optional :string, :href, 1
|
40
|
+
optional :string, :title, 2
|
41
|
+
end
|
42
|
+
|
43
|
+
class Up
|
44
|
+
optional :string, :href, 1
|
45
|
+
optional :string, :title, 2
|
46
|
+
end
|
47
|
+
|
48
|
+
class Filter
|
49
|
+
optional :string, :href, 1
|
50
|
+
required ::Picatrix::Mason::HTTPVerb, :method, 2, :default => ::Picatrix::Mason::HTTPVerb::GET
|
51
|
+
required :bool, :isHrefTemplate, 3, :default => true
|
52
|
+
optional :string, :title, 4
|
53
|
+
# Extension Fields
|
54
|
+
extensions 10...20
|
55
|
+
end
|
56
|
+
|
57
|
+
class Create
|
58
|
+
optional :string, :href, 1
|
59
|
+
required ::Picatrix::Mason::HTTPVerb, :method, 2, :default => ::Picatrix::Mason::HTTPVerb::POST
|
60
|
+
optional :string, :title, 3
|
61
|
+
optional :string, :encoding, 4
|
62
|
+
# Extension Fields
|
63
|
+
extensions 10...20
|
64
|
+
end
|
65
|
+
|
66
|
+
class Edit
|
67
|
+
optional :string, :href, 1
|
68
|
+
required ::Picatrix::Mason::HTTPVerb, :method, 2, :default => ::Picatrix::Mason::HTTPVerb::PATCH
|
69
|
+
optional :string, :title, 3
|
70
|
+
optional :string, :encoding, 4
|
71
|
+
# Extension Fields
|
72
|
+
extensions 10...20
|
73
|
+
end
|
74
|
+
|
75
|
+
class Remove
|
76
|
+
optional :string, :href, 1
|
77
|
+
required ::Picatrix::Mason::HTTPVerb, :method, 2, :default => ::Picatrix::Mason::HTTPVerb::DELETE
|
78
|
+
optional :string, :title, 3
|
79
|
+
# Extension Fields
|
80
|
+
extensions 10...20
|
81
|
+
end
|
82
|
+
|
83
|
+
class Controls
|
84
|
+
optional ::Picatrix::Mason::Self, :self, 2
|
85
|
+
optional ::Picatrix::Mason::Up, :up, 3
|
86
|
+
optional ::Picatrix::Mason::Edit, :edit, 4
|
87
|
+
optional ::Picatrix::Mason::Remove, :remove, 5
|
88
|
+
optional ::Picatrix::Mason::Create, :create, 6
|
89
|
+
optional ::Picatrix::Mason::Filter, :filter, 7
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
|