Skip to content

Instantly share code, notes, and snippets.

@jfeaver
Created July 31, 2019 20:10
Show Gist options
  • Save jfeaver/ce77a64998984868ac1a1f6281c008d8 to your computer and use it in GitHub Desktop.
Save jfeaver/ce77a64998984868ac1a1f6281c008d8 to your computer and use it in GitHub Desktop.
defmodule Isbn do
require Record
Record.defrecord(:isbn, raw: "", gs1: "", group: "", publisher: "", title: "", check_digit: "")
end
defmodule Isbn.Parser do
import Isbn
def parse(isbn) do
record =
isbn
|> String.graphemes
|> Enum.map(&lexeme/1)
|> join
|> parse_structure
end
defp lexeme(l) do
if String.match?(l, ~r/[\d -X]/) do
l
else
raise ArgumentError, "ISBN contains an invalid character: " <> l <> ". Valid characters are: 0-9, X, (space), -."
end
end
defp join(lexemes) do
Enum.reduce(lexemes, "", fn(my_lx, isbn) -> isbn <> my_lx end)
end
defp parse_structure(isbn) do
isbn
|> String.split([" ", "-"], trim: true)
|> record_from(isbn)
end
defp record_from(parts, raw) when length(parts) == 4 do
attrs = [:group, :publisher, :title, :check_digit]
|> Enum.zip(parts)
|> Kernel.++([raw: raw])
|> isbn
end
defp record_from([gs1 | parts], raw) when length(parts) == 4 do
record_from(parts, raw)
|> isbn(gs1: gs1)
end
end
defmodule IsbnParserTest do
use ExUnit.Case
require Record
import Isbn
test "parsing stores various parts of the isbn separately" do
record = Isbn.Parser.parse("3-598-21508-8")
assert(isbn(record, :group)) == "3"
assert(isbn(record, :publisher)) == "598"
assert(isbn(record, :title)) == "21508"
assert(isbn(record, :check_digit)) == "8"
assert(isbn(record, :raw)) == "3-598-21508-8"
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment