diff --git a/README.md b/README.md index 9c034f4..3b6293f 100644 --- a/README.md +++ b/README.md @@ -46,9 +46,6 @@ See `kitchen_sink.sh` examples. # Current Status - - [X] Oops, `lipmaa` field needs to be a hash, not an integer! - - [ ] BUG: Keys that start with a carriage return (`\n`) freeze tokenizer. - - [ ] Convert literals to constants, remove unused locals, reduce duplication, run linter. - [ ] Change message templates to render headers in this order: `author`, `prev`, `lipmaa`, `depth`, `kind`. - [ ] Make location of blob folder configurable? - [ ] Change `@`, `%`, `&` to `feed.`, `mesg.`, `blob.`, respectively. Better readability, easier onboarding, URL friendly. diff --git a/lib/pigeon.rb b/lib/pigeon.rb index 0236a37..55cbda1 100644 --- a/lib/pigeon.rb +++ b/lib/pigeon.rb @@ -11,6 +11,7 @@ module Pigeon PIGEON_DB_PATH = File.join("pigeon.db") DEFAULT_BUNDLE_PATH = File.join(Dir.pwd, "bundle") PIGEON_BLOB_PATH = File.join(Dir.home, "pigeon_sha256") + MESSAGE_FILE = "messages.pgn" # MESSAGE TEMPLATE CONSTANTS: HEADER_TPL = "author <%= author %>\nkind <%= kind %>\nprev <%= prev %>\ndepth <%= depth %>\nlipmaa <%= lipmaa %>\n\n" BODY_TPL = "<% body.to_a.each do |k, v| %><%= k %>:<%= v %><%= \"\\n\" %><% end %>\n" @@ -49,6 +50,9 @@ module Pigeon NO_DRAFT_FOUND = "NO DRAFT FOUND" STRING_KEYS_ONLY = "String keys only" MISSING_BODY = "BODY CANT BE EMPTY" + STILL_HAVE_DRAFT = "RESET DRAFT (%s) FIRST (db.delete_current_draft)" + MISSING_DRAFT = "NO DRAFT. CREATE ONE FIRST. Call db.new_draft(kind, body)" + RUNAWAY_LOOP = "RUNAWAY LOOP DETECTED" # Constants for internal use only: FOOTERS_REGEX = Regexp.new("#{SIG_FOOTER}|#{IDENTITY_FOOTER}") diff --git a/lib/pigeon/database.rb b/lib/pigeon/database.rb index c071bf9..8fc9dbc 100644 --- a/lib/pigeon/database.rb +++ b/lib/pigeon/database.rb @@ -87,8 +87,9 @@ module Pigeon def new_draft(kind:, body: {}) old = _get_config(CURRENT_DRAFT) - raise "PUBLISH OR RESET CURRENT DRAFT (#{old.kind}) FIRST (call db.delete_current_draft)" if old - + if old + raise STILL_HAVE_DRAFT % old.kind + end _replace_draft(Draft.new(kind: kind, body: body)) end @@ -100,7 +101,7 @@ module Pigeon def get_draft draft = store._get_config(CURRENT_DRAFT) unless draft - raise "THERE IS NO DRAFT. CREATE ONE FIRST. Call db.new_draft(kind, body)" + raise MISSING_DRAFT end draft end @@ -145,11 +146,11 @@ module Pigeon .map(&:render) .join(BUNDLE_MESSAGE_SEPARATOR) - File.write(File.join(file_path, "messages.pgn"), content + CR) + File.write(File.join(file_path, MESSAGE_FILE), content + CR) end def import_bundle(file_path = DEFAULT_BUNDLE_PATH) - bundle = File.read(File.join(file_path, "messages.pgn")) + bundle = File.read(File.join(file_path, MESSAGE_FILE)) tokens = Pigeon::Lexer.tokenize(bundle) messages = Pigeon::Parser.parse(self, tokens) diff --git a/lib/pigeon/lexer.rb b/lib/pigeon/lexer.rb index 7753ff9..37859c1 100644 --- a/lib/pigeon/lexer.rb +++ b/lib/pigeon/lexer.rb @@ -47,7 +47,7 @@ module Pigeon def safety_check if @loops > 1000 - raise "RUNAWAY LOOP DETECTED" + raise RUNAWAY_LOOP else @loops += 1 end @@ -86,8 +86,11 @@ module Pigeon class LexError < StandardError; end + SYNTAX_ERROR = "Syntax error pos %s by %s field in %s" + FOOTER_ERROR = "Parse error at %s. Double carriage return not found." + def flunk!(where) - msg = "Syntax error pos #{scanner.pos} by #{@last_good} field in #{where}" + msg = SYNTAX_ERROR % [scanner.pos, @last_good, where] raise LexError, msg end @@ -182,7 +185,7 @@ module Pigeon return end - raise LexError, "Parse error at #{scanner.pos}. Double carriage return not found." + raise LexError, FOOTER_ERROR % scanner.pos end end end diff --git a/lib/pigeon/parser.rb b/lib/pigeon/parser.rb index 51ada7b..285958a 100644 --- a/lib/pigeon/parser.rb +++ b/lib/pigeon/parser.rb @@ -2,9 +2,9 @@ module Pigeon class Parser class DuplicateKeyError < StandardError; end - def self.parse(db, tokens) - raise "NO!" unless db.is_a?(Pigeon::Database) + DUPE_KEYS = "Found duplicate keys: %s" + def self.parse(db, tokens) new(db, tokens).parse end @@ -49,7 +49,7 @@ module Pigeon def set(key, value, hash = @scratchpad) if hash[key] - raise DuplicateKeyError, "Found duplicate keys: #{key}" + raise DuplicateKeyError, (DUPE_KEYS % key) else hash[key] = value end diff --git a/spec/pigeon/bundle_spec.rb b/spec/pigeon/bundle_spec.rb index 1f1851c..3919276 100644 --- a/spec/pigeon/bundle_spec.rb +++ b/spec/pigeon/bundle_spec.rb @@ -29,7 +29,7 @@ RSpec.describe Pigeon::Message do it "creates a bundle" do expected_bundle = create_fake_messages.map(&:render).join("\n\n") + "\n" db.export_bundle - actual_bundle = File.read(File.join(Pigeon::DEFAULT_BUNDLE_PATH, "messages.pgn")) + actual_bundle = File.read(File.join(Pigeon::DEFAULT_BUNDLE_PATH, Pigeon::MESSAGE_FILE)) expect(expected_bundle).to eq(actual_bundle) end diff --git a/spec/pigeon/lexer_spec.rb b/spec/pigeon/lexer_spec.rb index c6bdabf..1189e4d 100644 --- a/spec/pigeon/lexer_spec.rb +++ b/spec/pigeon/lexer_spec.rb @@ -129,7 +129,7 @@ RSpec.describe Pigeon::Lexer do end it "tokenizes a bundle" do - bundle = File.read("./spec/fixtures/normal/messages.pgn") + bundle = File.read("./spec/fixtures/normal/#{Pigeon::MESSAGE_FILE}") tokens = Pigeon::Lexer.tokenize(bundle) EXPECTED_TOKENS1.each_with_index do |_item, i| expect(tokens[i]).to eq(EXPECTED_TOKENS1[i]) diff --git a/spec/pigeon/message_spec.rb b/spec/pigeon/message_spec.rb index bf03cfd..7b1551f 100644 --- a/spec/pigeon/message_spec.rb +++ b/spec/pigeon/message_spec.rb @@ -32,8 +32,7 @@ RSpec.describe Pigeon::Message do it "discards a draft after signing" do db.publish_draft(draft) - err = "THERE IS NO DRAFT. CREATE ONE FIRST. Call db.new_draft(kind, body)" - expect { db.get_draft }.to raise_error(err) + expect { db.get_draft }.to raise_error(Pigeon::MISSING_DRAFT) end it "creates a single message" do diff --git a/spec/pigeon/parser_spec.rb b/spec/pigeon/parser_spec.rb index db885ae..0474f18 100644 --- a/spec/pigeon/parser_spec.rb +++ b/spec/pigeon/parser_spec.rb @@ -6,7 +6,7 @@ RSpec.describe Pigeon::Lexer do db.reset_database db end - let(:example_bundle) { File.read("./spec/fixtures/normal/messages.pgn") } + let(:example_bundle) { File.read("./spec/fixtures/normal/#{Pigeon::MESSAGE_FILE}") } let(:tokens) { Pigeon::Lexer.tokenize(example_bundle) } BAD_TOKENS = [