Remove wonky draft API

This commit is contained in:
Netscape Navigator 2020-04-19 16:27:09 -05:00
parent 6c897c5361
commit eb1b9a9a97
10 changed files with 77 additions and 58 deletions

View File

@ -4,6 +4,12 @@
This is a WIP [Pigeon Protocol] client written in Ruby.
# Caveats
* Not published to RubyGems yet (see installation instructions below)
* Not thread safe. This particular implementation probably never will be. Single user only.
* Bundle mechanism works for basic usage, but is extremely ineficient and **does not do anything with blocked identities**.
# Installation
We are not yet on Rubygems. The gem will be released after we are fully compliant with the spec and have high test coverage stats.

View File

@ -96,7 +96,7 @@ module Pigeon
desc "create", "Begin a new Pigeon message"
def create(kind)
puts db.create_draft(kind: kind).render_as_draft
puts db.new_draft(kind: kind).render_as_draft
end
desc "append", "Add a key/value pair to the current DRAFT"
@ -105,7 +105,7 @@ module Pigeon
v = (raw_value != "") ? raw_value : STDIN.read
draft = db.current_draft
if draft
puts draft.put(db, key, v)
puts db.update_draft(key, v)
else
bail("You must create a draft first")
end

View File

@ -169,23 +169,13 @@ module Pigeon
draft.signature = author.sign(unsigned)
tokens = Lexer.tokenize_unsigned(unsigned, draft.signature)
message = Parser.parse(db, tokens)[0]
db.discard_draft
db.reset_draft
message
end
# TODO: This is a wonky API
def self.update_draft(db, draft, key, value)
raise STRING_KEYS_ONLY unless key.is_a?(String)
case value[0]
when BLOB_SIGIL, MESSAGE_SIGIL, IDENTITY_SIGIL, STRING_SIGIL
draft.body[key] = value
else
# If users passes a string and forgets to append
# the string sigil (\"), we add it for them.
# This might be a bad or good idea. Not sure yet.
draft.body[key] = value.inspect
end
def self.update_draft(db, key, value)
draft = db.current_draft
draft[key] = value
db.save_draft(draft)
return draft.body[key]
end

View File

@ -33,9 +33,7 @@ module Pigeon
end
def create_message(kind, params)
draft = Pigeon::Draft.new(kind: kind, db: self)
params.map { |(k, v)| draft.put(self, k, v) }
publish_draft(draft)
publish_draft(new_draft(kind: kind, body: params))
end
# Store a message that someone (not the LocalIdentity)
@ -60,9 +58,12 @@ module Pigeon
# === DRAFTS
def reset_current_draft; set_config(CURRENT_DRAFT, nil); end
def create_draft(kind:, body: {})
draft = Draft.new(kind: kind, body: body, db: self)
save_draft(draft)
def new_draft(kind:, body: {})
old = get_config(CURRENT_DRAFT)
if old
raise "PUBLISH OR RESET CURRENT DRAFT (#{old.kind}) FIRST"
end
save_draft(Draft.new(kind: kind, body: body))
end
def save_draft(draft)
@ -71,15 +72,22 @@ module Pigeon
end
def current_draft
store.get_config(CURRENT_DRAFT)
draft = store.get_config(CURRENT_DRAFT)
if draft
return draft
else
raise "THERE IS NO DRAFT. CREATE ONE FIRST."
end
end
def discard_draft
def update_draft(k, v); Helpers.update_draft(self, k, v); end
def reset_draft
set_config(CURRENT_DRAFT, nil)
end
# Author a new message.
def publish_draft(draft)
def publish_draft(draft = self.current_draft)
Helpers.publish_draft(self, draft)
end

View File

@ -5,20 +5,31 @@ module Pigeon
attr_accessor :signature, :prev, :lipmaa, :kind, :depth,
:body, :author
def initialize(kind:, body: {}, db:)
def initialize(kind:, body: {})
@signature = Pigeon::NOTHING
@prev = Pigeon::NOTHING
@kind = kind
@depth = -1
@body = body
@body = {}
@author = Pigeon::NOTHING
@lipmaa = Pigeon::NOTHING
body.to_a.map { |(k, v)| self[k] = v }
end
def [](key)
self.body[key]
end
def []=(key, value)
raise STRING_KEYS_ONLY unless key.is_a?(String)
case value[0]
when BLOB_SIGIL, MESSAGE_SIGIL, IDENTITY_SIGIL, STRING_SIGIL
body[key] = value
else
body[key] = value.inspect
end
end
def render_as_draft
DraftSerializer.new(self).render
end

View File

@ -79,6 +79,7 @@ module Pigeon
all = []
depth = -1
last = ""
# TODO: This loop may become unresponsive.
until (last == nil) || (depth > 99999)
last = self.get_message_by_depth(author, depth += 1)
all.push(last) if last

View File

@ -8,11 +8,12 @@ RSpec.describe Pigeon::Draft do
end
let(:message) do
message = db.create_draft(kind: "unit_test")
hash = db.put_blob(File.read("./logo.png"))
message.put(db, "a", "bar")
message.put(db, "b", hash)
message
db.reset_draft
db.new_draft(kind: "unit_test")
logo = File.read("./logo.png")
db.update_draft("a", "bar")
db.update_draft("b", db.put_blob(logo))
db.current_draft
end
MSG = [
@ -34,7 +35,8 @@ RSpec.describe Pigeon::Draft do
end
it "creates a new message" do
message = db.create_draft(kind: "unit_test")
db.reset_draft
db.new_draft(kind: "unit_test")
hash = db.put_blob(File.read("./logo.png"))
expectations = {
kind: "unit_test",
@ -43,8 +45,8 @@ RSpec.describe Pigeon::Draft do
"b" => hash,
},
}
message.put(db, "a", "bar")
message.put(db, "b", hash)
db.update_draft("a", "bar")
db.update_draft("b", hash)
expect(message["a"]).to eq(expectations.dig(:body, "a"))
expect(message["b"]).to eq(expectations.dig(:body, "b"))
expect(message.kind).to eq("unit_test")

View File

@ -122,9 +122,10 @@ RSpec.describe Pigeon::Lexer do
end
let(:message) do
draft = db.create_draft(kind: "unit_test")
draft.put(db, "foo", "bar")
db.publish_draft(draft)
db.reset_draft
db.new_draft(kind: "unit_test")
db.update_draft("foo", "bar")
db.publish_draft
end
it "tokenizes a bundle" do

View File

@ -1,14 +1,14 @@
require "spec_helper"
RSpec.describe Pigeon::Message do
def create_draft(params)
draft = db.create_draft(kind: "unit_test")
params.each { |(k, v)| draft.put(db, k, v) }
draft
def reset_draft(params)
db.reset_draft
db.new_draft(kind: "unit_test", body: params)
db.current_draft
end
def create_message(params)
draft = create_draft(params)
draft = reset_draft(params)
db.publish_draft(draft)
end
@ -20,7 +20,7 @@ RSpec.describe Pigeon::Message do
let(:draft) do
hash = db.put_blob(File.read("./logo.png"))
create_draft({ "a" => "bar", "b" => hash })
reset_draft({ "a" => "bar", "b" => hash })
end
let(:templated_message) { create_message({ "a" => "b" }) }
@ -31,7 +31,7 @@ RSpec.describe Pigeon::Message do
it "discards a draft after signing" do
db.publish_draft(draft)
expect(db.current_draft).to be(nil)
expect { db.current_draft }.to raise_error("THERE IS NO DRAFT. CREATE ONE FIRST.")
end
it "creates a single message" do
@ -64,9 +64,10 @@ RSpec.describe Pigeon::Message do
it "creates a chain of messages" do
all = []
0.upto(4) do |expected_depth|
draft1 = db.create_draft(kind: "unit_test")
draft1.put(db, "description", "Message number #{expected_depth}")
message = db.publish_draft(draft1)
db.reset_draft
db.new_draft(kind: "unit_test")
db.update_draft("description", "Message number #{expected_depth}")
message = db.publish_draft
all.push(message)
expect(message.depth).to eq(expected_depth)
if expected_depth == 0
@ -78,7 +79,6 @@ RSpec.describe Pigeon::Message do
end
it "verifies accuracy of hash chain" do
print "?SLOW?"
m1 = create_message({ "a" => "b" })
m2 = create_message({ "c" => "d" })
m3 = create_message({ "e" => "f" })
@ -105,7 +105,6 @@ RSpec.describe Pigeon::Message do
end
it "verifies accuracy of signatures" do
print "?SLOW?"
# === Initial setup
secret = db.get_config(Pigeon::SEED_CONFIG_KEY)
expect(secret).to be_kind_of(String)
@ -156,20 +155,21 @@ RSpec.describe Pigeon::Message do
WHITESPACE.map do |n|
kind = SecureRandom.alphanumeric(8)
kind[rand(0...8)] = n
draft = db.create_draft(kind: kind)
draft.put(db, "body", "empty")
boom = ->() { Pigeon::Lexer.tokenize(db.publish_draft(draft).render) }
db.reset_draft
db.new_draft(kind: kind)
boom = ->() { db.publish_draft.render }
expect(boom).to raise_error(Pigeon::Lexer::LexError)
end
end
it "does not allow whitespace in key names" do
WHITESPACE.map do |n|
draft = db.create_draft(kind: "unit_test")
db.reset_draft
db.new_draft(kind: "unit_test")
key = SecureRandom.alphanumeric(8)
key[rand(0...8)] = n
draft.put(db, key, "should crash")
boom = ->() { Pigeon::Lexer.tokenize(db.publish_draft(draft).render) }
db.update_draft(key, "should crash")
boom = ->() { Pigeon::Lexer.tokenize(db.publish_draft.render) }
expect(boom).to raise_error(Pigeon::Lexer::LexError)
end
end

View File

@ -3,9 +3,9 @@ require "pry"
db = Pigeon::Database.new(path: "my.db")
db.create_draft
db.reset_draft
db.current_draft
db.discard_draft
db.reset_draft
db.publish_draft
db.save_draft
db.save_message