Factor Pigeon::Message into two smaller classes: Pigeon::Message and Pigeon::Draft
This commit is contained in:
parent
aaea1d3f6d
commit
17bd8e7927
|
@ -1,3 +1,4 @@
|
|||
coverage/
|
||||
.pigeon/
|
||||
*.pigeon
|
||||
doc/
|
|
@ -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")
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
14
pigeon-cli
14
pigeon-cli
|
@ -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===
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
Loading…
Reference in New Issue