diff --git a/app/actions/books/index.rb b/app/actions/books/index.rb new file mode 100644 index 0000000..3e0db55 --- /dev/null +++ b/app/actions/books/index.rb @@ -0,0 +1,13 @@ +module Adamantium + module Actions + module Books + class Index < Action + include Deps["views.books.index"] + + def handle(req, res) + res.render index + end + end + end + end +end diff --git a/app/commands/posts/create_book_post.rb b/app/commands/posts/create_book_post.rb new file mode 100644 index 0000000..f84889c --- /dev/null +++ b/app/commands/posts/create_book_post.rb @@ -0,0 +1,19 @@ +require "dry/monads" + +module Adamantium + module Commands + module Posts + class CreateBookPost < Command + include Deps["repos.post_repo"] + + include Dry::Monads[:result] + + def call(post) + created_post = post_repo.create(post) + + Success(created_post) + end + end + end + end +end diff --git a/app/commands/posts/creation_resolver.rb b/app/commands/posts/creation_resolver.rb index b236d0d..9728d51 100644 --- a/app/commands/posts/creation_resolver.rb +++ b/app/commands/posts/creation_resolver.rb @@ -6,9 +6,11 @@ module Adamantium "validation.posts.post_contract", "validation.posts.bookmark_contract", "validation.posts.checkin_contract", + "validation.posts.book_contract", "commands.posts.create_entry", "commands.posts.create_bookmark", - "commands.posts.create_checkin" + "commands.posts.create_checkin", + "commands.posts.create_book_post" ] def call(entry_type:) @@ -17,6 +19,8 @@ module Adamantium {command: create_bookmark, validation: bookmark_contract} in Entities::CheckinRequest {command: create_checkin, validation: checkin_contract} + in Entities::BookRequest + {command: create_book_post, validation: book_contract} else {command: create_entry, validation: post_contract} end diff --git a/app/decorators/books/decorator.rb b/app/decorators/books/decorator.rb new file mode 100644 index 0000000..f296fa9 --- /dev/null +++ b/app/decorators/books/decorator.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: false + +# auto_register: false + +module Adamantium + module Decorators + module Books + class Decorator < SimpleDelegator + def display_published_at + published_at.strftime("%e %B, %Y") + end + + def machine_published_at + published_at.rfc2822 + end + + def syndicated? + !syndication_sources.empty? + end + + def template_type + :book + end + end + end + end +end diff --git a/app/entities/book_request.rb b/app/entities/book_request.rb new file mode 100644 index 0000000..6da55a7 --- /dev/null +++ b/app/entities/book_request.rb @@ -0,0 +1,15 @@ +module Adamantium + module Entities + class BookRequest < Dry::Struct + attribute :h, Types::Coercible::String + attribute :content, Types::Coercible::String + attribute :book_status, Types::Coercible::String + attribute :name, Types::Coercible::String + attribute :book_author, Types::Coercible::String + attribute :slug, Types::Coercible::String + attribute :category, Types::Array.of(Types::Coercible::String) + attribute :published_at, Types::Nominal::DateTime + attribute :post_type, Types::Coercible::String + end + end +end diff --git a/app/repos/post_repo.rb b/app/repos/post_repo.rb index e095347..4597470 100644 --- a/app/repos/post_repo.rb +++ b/app/repos/post_repo.rb @@ -127,6 +127,15 @@ module Adamantium .to_a end + def books_listing(limit: nil) + posts + .where(post_type: "book") + .published + .order(Sequel.desc(:published_at)) + .limit(limit) + .to_a + end + def bookmark_listing(query: nil) base = posts .where(post_type: "bookmark") diff --git a/app/templates/books/index.html.slim b/app/templates/books/index.html.slim new file mode 100644 index 0000000..13fff0b --- /dev/null +++ b/app/templates/books/index.html.slim @@ -0,0 +1,23 @@ +- context.content_for(:title, "Books | ") + +div class="mb-12 prose dark:prose-invert max-w-prose mx-auto text-gray-800 dark:text-gray-200" + h1 Books + + div class="mb-12 max-w-prose mx-auto" + table class="prose dark:prose-invert table-auto" + thead + tr + td Title + td Author + td Status + - books.each do |book| + tr + td + / a href="/books/#{book.slug}" + = book.name + td + = book.book_author + td + = book.book_status + +div class="max-w-screen-md mx-auto border-t-4 border-solid border-gray-400 dark:border-gray-600" diff --git a/app/validation/posts/book_contract.rb b/app/validation/posts/book_contract.rb new file mode 100644 index 0000000..c4aada7 --- /dev/null +++ b/app/validation/posts/book_contract.rb @@ -0,0 +1,18 @@ +module Adamantium + module Validation + module Posts + class BookContract < Dry::Validation::Contract + params do + required(:name).maybe(:string) + required(:content).filled(:string) + required(:category).array(:string) + required(:published_at).maybe(:time) + required(:slug).filled(:string) + required(:post_type).value(included_in?: %w[book]) + required(:book_author).maybe(:string) + required(:book_status).value(included_in?: %w[to-read reading read]) + end + end + end + end +end diff --git a/app/views/books/index.rb b/app/views/books/index.rb new file mode 100644 index 0000000..7a6f490 --- /dev/null +++ b/app/views/books/index.rb @@ -0,0 +1,15 @@ +module Adamantium + module Views + module Books + class Index < Adamantium::View + include Deps["repos.post_repo"] + + expose :books do + post_repo.books_listing.map do |book| + Decorators::Books::Decorator.new book + end + end + end + end + end +end diff --git a/config/routes.rb b/config/routes.rb index 5c7291e..772a543 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -59,6 +59,8 @@ module Adamantium get "/podcasts", to: "podcasts.index" + get "/books", to: "books.index" + redirect "deploying-a-hanami-app-to-fly-io", to: "/post/deploying-a-hanami-20-app-to-flyio" redirect "deploying-a-hanami-app-to-fly-io/", to: "/post/deploying-a-hanami-20-app-to-flyio" diff --git a/db/migrate/20230626090009_add_book_posts.rb b/db/migrate/20230626090009_add_book_posts.rb new file mode 100644 index 0000000..b54e3fd --- /dev/null +++ b/db/migrate/20230626090009_add_book_posts.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +ROM::SQL.migration do + change do + alter_table :posts do + add_column :book_status, :text, default: nil + add_column :book_author, :text, default: nil + end + end +end diff --git a/lib/adamantium/micropub_request_parser.rb b/lib/adamantium/micropub_request_parser.rb index 2781dbf..ddb9bcd 100644 --- a/lib/adamantium/micropub_request_parser.rb +++ b/lib/adamantium/micropub_request_parser.rb @@ -13,6 +13,9 @@ module Adamantium when :checkin checkin_params = parse_checkin_params(params) Entities::CheckinRequest.new(checkin_params) + when :book + book_params = parse_book_params(params) + Entities::BookRequest.new(book_params) else req_params = parse_post_params(req_type, cont_type, params) Entities::PostRequest.new(req_params) @@ -23,6 +26,7 @@ module Adamantium def content_type(params) return :bookmark if params[:"bookmark-of"] + return :book if params.dig(:properties, :"read-of") return :checkin if params.dig(:properties, :checkin) :post end @@ -110,5 +114,23 @@ module Adamantium new_params[:location] = "geo:#{location.dig(:latitude).first},#{location.dig(:longitude).first};u=0" new_params end + + def parse_book_params(params) + new_params = {} + new_params[:post_type] = "book" + + entry = params[:properties] + new_params[:h] = "entry" + new_params[:content] = entry[:summary].first + new_params[:book_status] = entry[:"read-status"].first + + book = params.dig(:properties, :"read-of").first[:properties] + new_params[:name] = book[:name].first + new_params[:book_author] = book[:author].first + new_params[:slug] = book[:uid].first + new_params[:category] = [] + new_params[:published_at] = Time.now + new_params + end end end diff --git a/slices/admin/templates/posts/show.html.slim b/slices/admin/templates/posts/show.html.slim index 98312bc..f32cd88 100644 --- a/slices/admin/templates/posts/show.html.slim +++ b/slices/admin/templates/posts/show.html.slim @@ -9,5 +9,5 @@ div class="mb-12 prose dark:prose-invert max-w-prose mx-auto text-gray-800 dark: button hx-post="/admin/posts/#{post.id}/syndicate/day_one" Send to Day One article class="mb-12 prose dark:prose-invert max-w-prose mx-auto text-gray-800 dark:text-gray-200 prose-a:text-blue-600 prose-a:no-underline hover:prose-a:underline prose-img:rounded" h1= post.name - textarea class="w-full border-pink-200 border-2 rounded p-2" x-data="{ resize: () => { $el.style.height = '5px'; $el.style.height = $el.scrollHeight + 'px' } }" x-init="resize()" @input="resize()" + textarea class="text-gray-800 w-full border-blue-200 border-2 rounded p-2" x-data="{ resize: () => { $el.style.height = '5px'; $el.style.height = $el.scrollHeight + 'px' } }" x-init="resize()" @input="resize()" == markdown_body \ No newline at end of file