Factor Pigeon::Message into two smaller classes: Pigeon::Message and Pigeon::Draft

This commit is contained in:
Netscape Navigator 2020-03-05 07:59:11 -06:00
parent aaea1d3f6d
commit 17bd8e7927
11 changed files with 181 additions and 170 deletions

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
coverage/
.pigeon/
*.pigeon
doc/

5
dist/pigeon.rb vendored
View File

@ -21,6 +21,7 @@ module Pigeon
CURRENT_DRAFT = "HEAD.draft"
EMPTY_MESSAGE = "NONE"
OUTBOX_PATH = File.join(".pigeon", "user")
DRAFT_PLACEHOLDER = "DRAFT"
# /MESSAGE TEMPLATE CONSTANTS
# Internal namespaces for PStore keys:
@ -46,5 +47,7 @@ end
require_relative File.join("pigeon", "key_pair.rb")
require_relative File.join("pigeon", "storage.rb")
require_relative File.join("pigeon", "template.rb")
require_relative File.join("pigeon", "draft_template.rb")
require_relative File.join("pigeon", "message_template.rb")
require_relative File.join("pigeon", "message.rb")
require_relative File.join("pigeon", "draft.rb")

57
dist/pigeon/draft.rb vendored Normal file
View File

@ -0,0 +1,57 @@
require "digest"
module Pigeon
class Draft
attr_reader :kind, :body
def self.create(kind:, body: {})
self.new(kind: kind, body: body).save
end
def self.current
@current ||=
(Pigeon::Storage.current.get_config(CURRENT_DRAFT) || new.save)
end
def self.reset_current
Pigeon::Storage.current.set_config(CURRENT_DRAFT, nil)
@current = nil
end
def initialize(kind:, body: {})
@kind = kind
@body = body
end
def [](key)
self.body[key]
end
def []=(key, value)
case value[0]
when BLOB_SIGIL, SIGNATURE_SIGIL, IDENTITY_SIGIL, STRING_SIGIL
self.body[key] = value
else
self.body[key] = value.inspect
end
self.save
return self.body[key]
end
# NOT the same thing as #sign/0
def save
Pigeon::Storage.current.set_config(CURRENT_DRAFT, self)
self
end
# Convert a Pigeon::Draft into a Pigeon::Message
def sign
Draft.reset_current
raise "WIP"
end
def render
DraftTemplate.new(self).render
end
end
end

24
dist/pigeon/draft_template.rb vendored Normal file
View File

@ -0,0 +1,24 @@
require "erb"
module Pigeon
# Wrapper around a Pigeon::Draft to perform string templating.
# Renders a string that contains most (but not all) of a Pigeon message.
class DraftTemplate
attr_reader :message
def initialize(message)
@message = message
end
def render
body = message.body
kind = message.kind
author = DRAFT_PLACEHOLDER
depth = DRAFT_PLACEHOLDER
prev = DRAFT_PLACEHOLDER
signature = DRAFT_PLACEHOLDER
ERB.new([HEADER_TPL, BODY_TPL].join("")).result(binding)
end
end
end

View File

@ -4,42 +4,16 @@ module Pigeon
class Message
attr_reader :author, :kind, :body, :signature
class NotSaved < StandardError; end
def self.create(kind:, body: {})
self.new(author: KeyPair.current.public_key,
kind: kind,
body: body).save
def self.from_draft(draft, author: KeyPair.current)
self.new(author: KeyPair.current,
kind: draft.kind,
body: draft.body).save
end
def initialize(author:, kind:, body: {})
def initialize(author:, kind:, body: )
@author = author
@kind = kind
@body = body
@depth = nil # Set at save time
end
def append(key, value)
puts "TODO: Add #[] / #[]= methods"
puts "TODO: Add #readonly? method and disallow edits after save"
# TODO: Sanitize, validate inputs.
case value[0]
when BLOB_SIGIL, SIGNATURE_SIGIL, IDENTITY_SIGIL, STRING_SIGIL
self.body[key] = value
else
self.body[key] = value.inspect
end
self.save
return self.body[key]
end
def self.current
@current ||=
(Pigeon::Storage.current.get_config(CURRENT_DRAFT) || new.save)
end
def self.reset_current
@current = nil
end
def save
@ -73,10 +47,6 @@ module Pigeon
end
end
def saved?
@saved == true
end
private
def calculate_signature
@ -88,34 +58,6 @@ module Pigeon
def previous_message
raise "TODO - I need to create a `Pigeon::Index` class or something. " \
"need a way to index messages by: signature, depth"
# if @depth.nil?
# raise "Could not load @depth"
# end
# if @previous_message
# return @previous_message
# end
# if @depth == 1
# return @previous_message = nil
# end
# path = path_to_message_number(@depth - 1)
# @previous_message = Marshal.load(File.read(path))
end
def calculate_depth
if saved?
raise NotSaved, "Can't calculate depth of unsaved messages"
else
# raise "Hmm.... I need to create a message index."
@depth || Pigeon::Storage.current.message_count
end
end
def message_id # I need this to calculate `prev`.
# raise "NO!" unless @signature && !@signature.downcase.include?("draft")
# Digest::SHA256.digest(self.render)
end
end
end

View File

@ -74,21 +74,21 @@ module Pigeon
desc "create", "Begin a new Pigeon message"
def create(kind)
puts Pigeon::Message.create(kind: kind).render
puts Pigeon::Draft.create(kind: kind).render
end
desc "append", "Add a key/value pair to the current DRAFT"
def append(key, raw_value = "")
v = (raw_value != "") ? raw_value : STDIN.read
puts Pigeon::Message.current.append(key, v)
puts Pigeon::Draft.current[key] = v
end
desc "show", "Print a message to STDOUT. If message_id is missing, current draft will be displayed."
def show(message_id = "")
if message_id == ""
puts Pigeon::Message.current.render
puts Pigeon::Draft.current.render
else
raise "TODO: Find message by ID?"
end
@ -97,7 +97,7 @@ module Pigeon
desc "sign", "Commit current DRAFT to local feed."
def sign
puts Pigeon::Message.current.sign.render
puts Pigeon::Draft.current.sign.render
end
end
@ -108,9 +108,9 @@ module Pigeon
puts "
,.------------------------------------
| -`. Pigeon Protocol Ruby Client
| '( @ > Version: #{Config::VERSION}
| _) ( Peers: #{Dir[File.join(".pigeon", "peers", "*")].count}
| / ) Blocked: #{Dir[File.join(".pigeon", "blocked", "*")].count}
| '( @ > Version: #{Pigeon::VERSION}
| _) ( Peers: #{Pigeon::Storage.current.all_peers.count}
| / ) Blocked: #{Pigeon::Storage.current.all_blocks.count}
| /_,' /
| \ /
|===m" "m===

View File

@ -40,41 +40,41 @@ echo "listing all peers:"
echo "Making a new `scratch_pad` log entry"
./pigeon-cli message create scratch_pad
# echo "Appending values..."
echo "Appending values..."
# echo "...string via pipe"
echo "...string via pipe"
# echo "my_value" | ./pigeon-cli message append key1
echo "my_value" | ./pigeon-cli message append key1
# echo "...string with no quotes"
# ./pigeon-cli message append key2 my_value2
echo "...string with no quotes"
./pigeon-cli message append key2 my_value2
# echo "...string with quotes"
# ./pigeon-cli message append key3 "my_value3"
echo "...string with quotes"
./pigeon-cli message append key3 "my_value3"
# echo "...message ID"
# ./pigeon-cli message append key4 \%jvKh9yoiEJaePzoWCF1nnqpIlPgTk9FHEtqczQbvzGM=.sha256
echo "...message ID"
./pigeon-cli message append key4 \%jvKh9yoiEJaePzoWCF1nnqpIlPgTk9FHEtqczQbvzGM=.sha256
# echo "...blob"
# ./pigeon-cli message append key5 \&29f3933302c49c60841d7620886ce54afc68630242aee6ff683926d2465e6ca3.sha256
echo "...blob"
./pigeon-cli message append key5 \&29f3933302c49c60841d7620886ce54afc68630242aee6ff683926d2465e6ca3.sha256
# echo "...identity"
# ./pigeon-cli message append key6 \@galdahnB3L2DE2cTU0Me54IpIUKVEgKmBwvZVtWJccg=.ed25519
echo "...identity"
./pigeon-cli message append key6 \@galdahnB3L2DE2cTU0Me54IpIUKVEgKmBwvZVtWJccg=.ed25519
# echo "== show draft message"
# ./pigeon-cli message show
echo "== show draft message"
./pigeon-cli message show
# echo "== sign (publish, save, commit, etc) draft message"
# ./pigeon-cli message sign
echo "== sign (publish, save, commit, etc) draft message"
./pigeon-cli message sign
# echo "=== add a second message to the db"
# ./pigeon-cli message create second_test
echo "=== add a second message to the db"
./pigeon-cli message create second_test
# echo "=== append hello:'world' to message:"
# ./pigeon-cli message append hello "world"
echo "=== append hello:'world' to message:"
./pigeon-cli message append hello "world"
# echo "=== Sign message #2"
# ./pigeon-cli message sign
# echo "=== getting status:"
# ./pigeon-cli status
echo "=== getting status:"
./pigeon-cli status

55
spec/pigeon/draft_spec.rb Normal file
View File

@ -0,0 +1,55 @@
require "spec_helper"
RSpec.describe Pigeon::Draft do
let(:message) do
message = Pigeon::Draft.create(kind: "unit_test")
hash = Pigeon::Storage.current.set_blob(File.read("./logo.png"))
message["a"] = "bar"
message["b"] = hash
message
end
before(:each) do
Pigeon::Storage.reset
Pigeon::KeyPair.reset
end
MSG = [
"author DRAFT",
"kind unit_test",
"prev DRAFT",
"depth DRAFT",
"\na:\"bar\"",
"b:&6462a5f5174b53702fc25afe67a8f9a29f572610a65bafefff627531552f096f.sha256",
"\n"
].join("\n")
it "renders a message" do
pk = Pigeon::KeyPair.current.public_key
actual = message.render
expected = MSG.gsub("___", pk)
expect(actual).to start_with(expected)
end
it "creates a new message" do
message = Pigeon::Draft.create(kind: "unit_test")
hash = Pigeon::Storage.current.set_blob(File.read("./logo.png"))
expectations = {
kind: "unit_test",
body: {
"a" => "bar".to_json,
"b" => hash,
}
}
message["a"] = "bar"
message["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")
expect(message.body).to eq(expectations.fetch(:body))
expectations.map do |k, v|
expect(Pigeon::Draft.current.send(k)).to eq(v)
end
end
end

View File

@ -32,4 +32,9 @@ RSpec.describe Pigeon::KeyPair do
result = Pigeon::KeyPair.strip_headers(example)
expect(result).to eq(whatever)
end
it "caches KeyPair.current" do
first_kp = Pigeon::KeyPair.current
expect(first_kp).to be(first_kp) # Need strict equality here!
end
end

View File

@ -1,76 +0,0 @@
require "spec_helper"
RSpec.describe Pigeon::Message do
let(:message) do
message = Pigeon::Message.create(kind: "unit_test")
hash = Pigeon::Storage.current.set_blob(File.read("./logo.png"))
message.append("a", "bar")
message.append("b", hash)
message.sign
message
end
it "signs a message" do
Pigeon::Storage.reset
Pigeon::KeyPair.reset
m1 = message
message2 = Pigeon::Message.create(kind: "unit_test")
message2.append("expected_sequence", "1")
message2.sign
expect(message2.author).to eq(Pigeon::KeyPair.current.public_key)
expect(message2.kind).to eq("unit_test")
binding.pry
expect(message2.prev).to eq(m1)
expect(message2.body).to eq("expected_sequence" => "1")
expect(message2.depth).to eq(1)
end
MSG = [
"author ___",
"kind unit_test",
"prev NONE",
"depth 0\n",
"a:\"bar\"",
"b:&6462a5f5174b53702fc25afe67a8f9a29f572610a65bafefff627531552f096f.sha256\n",
"signature ",
].join("\n")
it "renders a first message" do
Pigeon::Storage.reset
Pigeon::KeyPair.reset
pk = Pigeon::KeyPair.current.public_key
actual = message.render
expected = MSG.gsub("___", pk)
expect(actual).to start_with(expected)
end
it "creates a new message" do
Pigeon::Storage.reset
Pigeon::KeyPair.reset
message = Pigeon::Message.create(kind: "unit_test")
hash = Pigeon::Storage.current.set_blob(File.read("./logo.png"))
expectations = {
author: Pigeon::KeyPair.current.public_key,
kind: "unit_test",
body: {
"a" => "bar".to_json,
"b" => hash,
},
depth: 0,
prev: Pigeon::EMPTY_MESSAGE,
}
message.append("a", "bar")
message.append("b", hash)
expect(message.author).to eq(Pigeon::KeyPair.current.public_key)
expect(message.kind).to eq("unit_test")
expect(message.body).to eq(expectations.fetch(:body))
expect(message.depth).to eq(0)
message.sign
binding.pry
expect(message.prev).to eq(Pigeon::EMPTY_MESSAGE)
expectations.map do |k, v|
expect(Pigeon::Message.current.send(k)).to eq(v)
end
end
end