Continuing tutorial

This commit is contained in:
Netscape Navigator 2020-06-12 08:37:48 -05:00
parent d17c5c64e9
commit 0fe8f56b7a
4 changed files with 65 additions and 68 deletions

View File

@ -14,12 +14,12 @@ Email `contact` at `vaporsoft.xyz` to ask questions or get involved. Your feedba
# Caveats # Caveats
* Current windows support is unknown (and unlikely to work in current state). Please report bugs. * Implementation is assumed to be stable, but does not have much production use.
* Not published to RubyGems yet (see installation instructions below) * Windows support is unknown (and unlikely to work in current state). Please report bugs.
* Single threaded use is assumed. Built for a single user per OS process. Many design tradeoffs were made around that use case. * Single threaded use is assumed. Built for a single user per OS process. Many design tradeoffs were made around that use case.
* Bundling operations need performance tuning. Optimizations are planned and help is welcome. * Bundling operations need performance tuning. Optimizations are planned and help is welcome.
# Installation # Installation - Bundler (Easy)
Add this to you Gemfile: Add this to you Gemfile:
@ -46,23 +46,16 @@ See `kitchen_sink.sh` examples.
# Current Status # Current Status
- [ ] CLI is wrapping `FILE.` and `FEED.` multihahshes in "string quotes". Why?
- [ ] Update Dev docs in protocol spec to reflect changes to `lipmaa` header.
- [ ] Update spec document CLI usage examples to reflect API changes in 2020. - [ ] Update spec document CLI usage examples to reflect API changes in 2020.
- [ ] Ability to unblock someone.
- [ ] Ability to delete a key from a draft.
- [ ] Update Dev docs in protocol spec to reflect changes to `lipmaa` header.
- [ ] 100% class / module documentation - [ ] 100% class / module documentation
- [ ] Run a [terminology extraction tool](https://www.visualthesaurus.com/vocabgrabber/#) on the documentation and write a glossary of terms. - [ ] Run a [terminology extraction tool](https://www.visualthesaurus.com/vocabgrabber/#) on the documentation and write a glossary of terms.
- [ ] Publish to RubyGems - [ ] Ability to list all blocked users.
- [ ] Ability to unblock a user.
# Optimizations # "Nice to Have"
- [ ] add parsers and validators for all CLI inputs
- [ ] Make the switch to LevelDB, RocksDB, [UNQLite](https://unqlite.org/features.html) or similar (currently using Ruby PStore).
- [ ] Reduce whole darn repo into single module to aide portability. `::Helpers` module is OK.
- [ ] Update the bundles.md document once `bundle consume` works.
- [ ] Performance benchmarks (Do this second to last!)
- [ ] Performance tuning (Do this last!)
# New Features / Road Map
- [ ] Support partial verification via `lipmaa` property. - [ ] Support partial verification via `lipmaa` property.
- [ ] Add `--since=`/`--until=` args to `bundle create` for sending partial / "slice" bundles. - [ ] Add `--since=`/`--until=` args to `bundle create` for sending partial / "slice" bundles.

View File

@ -101,7 +101,7 @@ module Pigeon
desc "append", "Add a key/value pair to the current DRAFT" desc "append", "Add a key/value pair to the current DRAFT"
def append(key, raw_value = "") def append(key, raw_value = "")
v = raw_value != "" ? raw_value : STDIN.read v = raw_value != "" ? raw_value : STDIN.read.chomp
if db.get_draft if db.get_draft
db.update_draft(key, v) db.update_draft(key, v)
puts db.get_draft.render_as_draft puts db.get_draft.render_as_draft
@ -191,10 +191,10 @@ module Pigeon
desc "identity SUBCOMMAND ...ARGS", "Manage `.pgn` identity" desc "identity SUBCOMMAND ...ARGS", "Manage `.pgn` identity"
subcommand "identity", Identity subcommand "identity", Identity
desc "message SUBCOMMAND ...ARGS", "Manage blob storage" desc "message SUBCOMMAND ...ARGS", "Manage text-based messages"
subcommand "message", PigeonMessage subcommand "message", PigeonMessage
desc "peer SUBCOMMAND ...ARGS", "Manage blob storage" desc "peer SUBCOMMAND ...ARGS", "Manage blob (file) storage"
subcommand "peer", Peer subcommand "peer", Peer
end end
end end

View File

@ -55,7 +55,7 @@ module Pigeon
STRING_KEYS_ONLY = "String keys only" STRING_KEYS_ONLY = "String keys only"
MISSING_BODY = "BODY CANT BE EMPTY" MISSING_BODY = "BODY CANT BE EMPTY"
STILL_HAVE_DRAFT = "RESET DRAFT (%s) FIRST (db.delete_current_draft)" STILL_HAVE_DRAFT = "RESET DRAFT (%s) FIRST (db.delete_current_draft)"
MISSING_DRAFT = "NO DRAFT. CREATE ONE FIRST. Call db.new_draft(kind, body)" MISSING_DRAFT = "NO DRAFT. CREATE ONE FIRST. Call db.new_draft(kind:, body:)"
RUNAWAY_LOOP = "RUNAWAY LOOP DETECTED" RUNAWAY_LOOP = "RUNAWAY LOOP DETECTED"
# Constants for internal use only: # Constants for internal use only:

View File

@ -12,13 +12,12 @@ This document will teach you how to:
* Build messages using drafts. * Build messages using drafts.
* Manage and query existing messages. * Manage and query existing messages.
* Replicate a database among peers. * Replicate a database among peers.
* Go beyond simple text messages and attach files to messages. * Attach binary files to messages.
* Communicate with remote databases using "bundles". * Communicate with remote databases using "bundles".
This guide assumes you are familiar with Ruby and the Pigeon Protocol. For an introduction to the protocol, see our protocol specification [here](https://tildegit.org/PigeonProtocolConsortium/protocol_spec). This guide assumes you are familiar with Ruby and the Pigeon Protocol. For an introduction to the protocol, see our protocol specification [here](https://tildegit.org/PigeonProtocolConsortium/protocol_spec).
Pigeon strive to have a "natural" API rather than a simple one. We will cover the API methods listed below. These are the only methods you will need to know to build a pigeon-based application: Below is a list of all methods needed to run a Pigeon node. Pigeon strives to have a _natural_ API rather than a simple one, which means you may not need to know every single method to operate a node successfully.
**BLOB METHODS:** `#add_blob`,`#get_blob` **BLOB METHODS:** `#add_blob`,`#get_blob`
@ -32,7 +31,7 @@ Pigeon strive to have a "natural" API rather than a simple one. We will cover th
**PEER METHODS:** `#all_peers`, `#add_peer`, `#remove_peer`, `#all_blocks`, `#block_peer`, `#peer_blocked` **PEER METHODS:** `#all_peers`, `#add_peer`, `#remove_peer`, `#all_blocks`, `#block_peer`, `#peer_blocked`
Once you understand the methods listed above, you will have everything you need to start writing Pigeon-based applications. Please let us know what you build! Send an email to `contact` at `vaporsoft.xyz` with your progress. **Note to application developers:** Please let us know what you build! Send an email to `contact` at `vaporsoft.xyz` with your progress.
## Installation ## Installation
@ -77,57 +76,56 @@ Let's see if we have a draft to work with:
```ruby ```ruby
db.get_draft db.get_draft
# => #<Pigeon::Draft:0x000056160b2e64a0 @author="NONE", @body={"a"=>"\"bar\"", "b"=>"&CH...QG.sha256"}, @depth=-1, @kind="unit_test", @lipmaa="NONE", @prev="NONE", @signature="NONE"> RuntimeError: NO DRAFT. CREATE ONE FIRST. Call db.new_draft(kind:, body:)
from lib/pigeon/database.rb:104:in `get_draft'
``` ```
It appears that my database has a draft. I don't actually remember what this draft was, so I will just delete it before proceeding. We do not have a draft yet. We need to create one:
```ruby ```ruby
db.delete_current_draft db.new_draft(kind: "garden_diary", body: {"message_text"=>"Tomato plant looking healthy."})
# => nil => #<Pigeon::Draft:0x00005603ed399b48 @author="NONE",
# @body={"greeting"=>"\"Hello, world!\""}, @depth=-1, @kind="example123",
# @lipmaa="NONE", @prev="NONE", @signature="NONE">
``` ```
Now I can create a new draft. I am going to create a new `garden_diary` for a fictitious gardening app. In my gardening app, I expect every `garden_diary` message to have a `message_text` entry in its body. We can add that now. The command above creates a new draft entry of kind `garden_entry` with on key/value pair in the body. We can view the draft at any time via `#get_draft`:
```ruby ```ruby
db.new_draft(kind: "garden_diary", body: {"message_text" => "Tomato plant looking healthy."}) db.get_draft
# => #<Pigeon::Draft:0x000056160b63da68 @author="NONE", @body={"message_text"=>"\"Tomato plant looking healthy.\""}, @depth=-1, @kind="garden_diary", @lipmaa="NONE", @prev="NONE", @signature="NONE"> # => #<Pigeon::Draft:0x00005603ed81e830 @author="NONE",
# @body={"greeting"=>"\"Hello, world!\""}, @depth=-1,
# @kind="example123", @lipmaa="NONE", @prev="NONE",
# @signature="NONE">
``` ```
A few notes about this draft message: Since the draft has not been published to the feed, its contents are mutable. We can add a new key/value pair to the message body with the following command:
* `"garden_diary` is the message `kind`. This is definable by application developers and helps determine the type of message we are dealing with. A fictitious diary app might have other entries such as `"status_update"` or `"photo_entry"`. It depends on the application you are building.
* Notice that my hash used string keys for the `"message_text"` body entry. You can only use strings for key / value pairs (no `:symbols` or numbers). Later on we will learn how to attach files to messages.
* The `body:` part is optional. I could have called `db.new_draft(kind: "garden_diary")` and added key / value pairs to the body later.
Oops! Speaking of adding entries to a draft's body, it looks like I forgot something. In my fictitious gardening app, a `garden_diary` entry doesn't just have a `"message_text"`, it also has a `"current_mood"` entry. Luckily, it is easy to add keys to unpublished drafts. Let's add the key now:
```ruby ```ruby
db.update_draft("current_mood", "Feeling great") db.update_draft("current_mood", "Feeling great")
# => "\"Feeling great\"" # => "\"Feeling great\""
``` ```
OK, I think our draft message is looking better. Let's take a look: A few notes about this draft message:
```ruby * `"garden_diary` is the message `kind`. This is definable by application developers and helps determine the type of message we are dealing with. A fictitious diary app might have other entries such as `"status_update"` or `"photo_entry"`. It depends on the application you are building.
db.get_draft * I used string keys for the `"message_text"` body entry rather than symbols or numbers. This is because you can only use strings for key / value pairs (no `:symbols` or numbers). Later on we will learn how to attach files to messages.
# => => #<Pigeon::Draft:0x000056160b3e6be8 @author="NONE", @body={"message_text"=>"\"Tomato plant looking healthy.\"", "current_mood"=>"\"Feeling great\""}, @depth=-1, @kind="garden_diary", @lipmaa="NONE", @prev="NONE", @signature="NONE"> * The `body:` part is optional. I could have called `db.new_draft(kind: "garden_diary")` and added key / value pairs to the body later.
```
I can see the status of my current draft message using `db.get_draft`. It returns a `Pigeon::Draft` object, whish is not very human readable. To get a more human readable version, I can use the `render_as_draft` method on a `Draft` object: Let's take a final look at our draft message. To get a more human readable version, I can use the `render_as_draft` method on a `Draft` object:
```ruby ```ruby
human_readable_string = db.get_draft.render_as_draft human_readable_string = db.get_draft.render_as_draft
# => "author DRAFT\nkind garden_dia...." # => "author DRAFT\nkind garden_dia...."
puts human_readable_string puts human_readable_string
# => author DRAFT # => author DRAFT
# kind garden_diary # depth DRAFT
# prev DRAFT # kind example123
# depth DRAFT # lipmaa DRAFT
# lipmaa DRAFT # prev DRAFT
# #
# message_text:"Tomato plant looking healthy." # greeting:"Hello, world!"
# current_mood:"Feeling great" # current_mood:"Feeling great"
``` ```
Some interesting things about the draft we just rendered: Some interesting things about the draft we just rendered:
@ -135,6 +133,12 @@ Some interesting things about the draft we just rendered:
* Unlike a message, a draft has no signature (yet). * Unlike a message, a draft has no signature (yet).
* The `author`, `kind`, `prev`, `depth`, `lipmaa` properties are all set to `"DRAFT"`. Real values will be populated when we finally publish the draft. * The `author`, `kind`, `prev`, `depth`, `lipmaa` properties are all set to `"DRAFT"`. Real values will be populated when we finally publish the draft.
If we want to start over, we can delete a draft via `delete_current_draft`:
```ruby
db.delete_current_draft
# => nil
```
## Turning Drafts Into Messages ## Turning Drafts Into Messages
Now that I am happy with my draft, I can publish it. Once published, my message cannot be modified, so it is very important to visually inspect a draft with `db.get_draft` before proceeding. Now that I am happy with my draft, I can publish it. Once published, my message cannot be modified, so it is very important to visually inspect a draft with `db.get_draft` before proceeding.
@ -142,33 +146,33 @@ Since we did that in the last step, I will go ahead and publish the message:
``` ```
my_message = db.publish_draft my_message = db.publish_draft
# => #<Pigeon::Message:0x000056160b50dd00 => #<Pigeon::Message:0x000055a751032c28
# @author=#<Pigeon::RemoteIdentity:0x000056160b50dd78 @multihash="@753FT97S1FD3SRYPTVPQQ64F7HCEAZMWVBKG0C2MYMS5MJ3SBT6G.ed25519">, @author=#<Pigeon::RemoteIdentity:0x000055a751032cf0 @multihash="USER.6DQ4RRNBKJ2T4EY5E1GZYYX6X6SZXV1W0GNH1HA4KGKA5KZ2Y2DG">,
# @body={"message_text"=>"\"Tomato plant looking healthy.\"", "current_mood"=>"\"Feeling great\""}, @body={"greeting"=>"\"Hello, world!\"", "current_mood"=>"\"Feeling great\""},
# @depth=0, @depth=0,
# @kind="garden_diary", @kind="garden_diary",
# @lipmaa=0, @lipmaa="NONE",
# @prev="NONE", @prev="NONE",
# @signature="2ZHC8TX3P2SQVQTMFYXTAT4S02RN43JNZNECRJDA7QMSJNE5G7NV7GTRK3PGFHFY9MBE1Q95BCKBSJH4V0PTX6945A34Z1CARTGH230.sig.ed25519"> @signature="QNY...208">
``` ```
Let's look at our new message in a human-readable way: Let's look at our new message in a human-readable way:
```ruby ```ruby
puts my_message.render puts my_message.render
# => author @753...T6G.ed25519 # author USER.6DQ4RRNBKJ2T4EY5E1GZYYX6X6SZXV1W0GNH1HA4KGKA5KZ2Y2DG
# kind garden_diary # depth 0
# prev NONE # kind example123
# depth 0 # lipmaa NONE
# lipmaa 0 # prev NONE
# #
# message_text:"Tomato plant looking healthy." # greeting:"Hello, world!"
# current_mood:"Feeling great" # current_mood:"Feeling great"
# #
# signature 2ZH...230.sig.ed25519 # signature QNY...208
``` ```
We see that unlike our draft, the message has a signature. The header fields are also populated. Unlike our draft, the message has a signature. The header fields are also populated.
In the next section, we will learn more about messages. In the next section, we will learn more about messages.