Refactor micropub specific things out to a slice

This commit is contained in:
2023-11-15 18:55:57 +11:00
parent 730ecb9ea4
commit 5b133363b3
63 changed files with 468 additions and 174 deletions

View File

@@ -1,29 +0,0 @@
module Adamantium
module Actions
module Media
class Create < Action
include Deps["commands.media.upload"]
before :authenticate!
def handle(req, res)
data = req.params[:file]
halt 401 unless verify_scope(req: req, scope: :create) || verify_scope(req: req, scope: :media)
upload_result = upload.call(file: data)
res.status = 422 if upload_result.failure?
if upload_result.success?
res.status = 201
res.headers["Location"] = upload_result.value!
res.headers["HX-Refresh"] = true
res.body = {
url: upload_result.value!
}.to_json
end
end
end
end
end
end

View File

@@ -1,29 +0,0 @@
module Adamantium
module Actions
module Media
class Show < Action
include Deps["settings"]
def handle(req, res)
res.body = if req.params[:q] == "source"
{
items: media_url(req.params[:file])
}.to_json
else
"Micropub media endpoint"
end
res.status = 200
end
private
def media_url(filename)
pathname = Time.now.strftime("%m-%Y")
File.join(settings.micropub_site_url, "/media/", "/#{pathname}/", filename).to_s
end
end
end
end
end

View File

@@ -1,79 +0,0 @@
module Adamantium
module Actions
module Posts
class Handle < Action
before :authenticate!
include Deps[
"settings",
"post_utilities.slugify",
"repos.post_repo",
post_param_parser: "param_parser.micropub_post",
create_resolver: "commands.posts.creation_resolver",
delete_post: "commands.posts.delete",
undelete_post: "commands.posts.undelete",
update_post: "commands.posts.update",
add_post_syndication_source: "commands.posts.add_syndication_source"
]
def handle(req, res)
req_entity = post_param_parser.call(params: req.params.to_h)
action = req.params[:action]
# delete, undelete, update
if action
perform_action(req: req, res: res, action: action)
elsif req_entity # create
create_entry(req: req, res: res, req_entity: req_entity)
end
end
private
def create_entry(req:, res:, req_entity:)
halt 401 unless verify_scope(req: req, scope: :create)
command, contract = create_resolver.call(entry_type: req_entity).values_at(:command, :validation)
post_params = prepare_params(req_entity.to_h)
validation = contract.call(post_params)
if validation.success?
command.call(validation.to_h).bind do |post|
res.status = 201
res.headers["Location"] = "#{settings.micropub_site_url}/#{post.post_type}/#{post.slug}"
end
else
res.body = {error: validation.errors.to_h}.to_json
res.status = 422
end
end
def perform_action(req:, res:, action:)
operation, permission_check = resolve_operation(action)
halt 401 unless permission_check.call(req)
operation.call(params: req.params.to_h)
res.status = 200
end
def prepare_params(post_params)
post = post_params.to_h
post[:slug] = post[:slug].empty? ? slugify.call(text: post[:name], checker: post_repo.method(:slug_exists?)) : post[:slug]
post
end
def resolve_operation(action)
case action
when "delete"
[delete_post, ->(req) { verify_scope(req: req, scope: :delete) }]
when "undelete"
[undelete_post, ->(req) { verify_scope(req: req, scope: :undelete) }]
when "update"
[update_post, ->(req) { verify_scope(req: req, scope: :update) }]
end
end
end
end
end
end

View File

@@ -1,69 +0,0 @@
module Adamantium
module Actions
module Site
class Config < Action
include Deps["settings", "views.site.home", "queries.posts.microformat_post"]
before :authenticate!
def handle(req, res)
if req.params[:q] == "config"
res.status = 200
res.content_type = "application/json"
res.body = {
"media-endpoint" => settings.micropub_media_endpoint,
"destination" => [
{uid: settings.micropub_site_id, name: settings.micropub_site_name}
],
"post-types" => [
{type: "note", name: "Note", properties: %w[content photo category]},
{type: "article", name: "Article", properties: %w[name content category]},
{type: "photo", name: "Photo", properties: %w[name photo content category]},
{type: "video", name: "Video", properties: %w[name video content category]},
{type: "bookmark", name: "Bookmark", properties: %w[name content category]}
],
"syndicate-to" => [
{
uid: "https://social.dnitza.com",
name: "Mastodon"
},
{
uid: "https://pinboard.in",
name: "Pinboard"
},
{
uid: "https://bsky.app",
name: "Blue Sky"
}
]
}.to_json
elsif req.params[:q] == "syndicate-to"
res.status = 200
res.content_type = "Application/JSON"
res.body = {
"syndicate-to" => [
{
uid: "https://social.dnitza.com",
name: "Mastodon"
},
{
uid: "https://pinboard.in",
name: "Pinboard"
},
{
uid: "https://bsky.app",
name: "Blue Sky"
}
]
}.to_json
elsif req.params[:q] == "source"
res.status = 200
res.content_type = "Application/JSON"
res.body = microformat_post.call(url: req.params[:url], properties: req.params[:properties]).to_json
else
res.render home
end
end
end
end
end
end

View File

@@ -1,32 +0,0 @@
# frozen_string_literal: true
module Adamantium
module Actions
module Webmentions
class Create < Adamantium::Action
include Deps["repos.webmentions_repo",
"repos.post_repo",
webmention_parser: "param_parser.webmention"
]
def handle(req, res)
webmention = webmention_parser.call(params: req.params)
case webmention
in Success[:reply, reply]
slug = req.params[:"in-reply-to"].split("/").last
post = post_repo.fetch!(slug)
reply[:post_id] = post.id
webmentions_repo.create(reply)
res.status = 201
in Failure(:invalid_request)
res.status = 429
in Failure(:not_implemented)
res.status = 429
end
end
end
end
end
end

0
app/commands/.keep Normal file
View File

View File

@@ -1,31 +0,0 @@
module Adamantium
module Commands
module AutoTagging
class Tag
include Dry::Monads[:result]
include Deps["repos.post_repo", "repos.auto_tagging_repo"]
def call(auto_tag_id: nil)
auto_taggings = if auto_tag_id
auto_tagging_repo.find(auto_tag_id)
else
auto_tagging_repo.all
end
auto_taggings.each do |auto_tagging|
posts = auto_tagging.title_only? ?
post_repo.by_title(title_contains: auto_tagging.title_contains) :
post_repo.by_content(body_contains: auto_tagging.body_contains)
posts.each do |post|
post_repo.auto_tag_post(post_id: post.id,
tag_id: auto_tagging.tag_id)
end
end
Success()
end
end
end
end
end

View File

@@ -1,103 +0,0 @@
# frozen_string_literal: true
require "securerandom"
require "dry/monads"
require "filemagic"
require "image_processing/vips"
require "open3"
module Adamantium
module Commands
module Media
class Upload < Command
include Deps["settings"]
include Dry::Monads[:result]
IMAGE_TYPES = %i[jpeg jpg png].freeze
VIDEO_TYPES = %i[gif iso].freeze
VALID_UPLOAD_TYPES = IMAGE_TYPES + VIDEO_TYPES
def call(file:)
mime = FileMagic.new
type = mime.file(file[:tempfile].path, true).to_sym
return Failure(:invalid_file_type) unless VALID_UPLOAD_TYPES.include? type
result = save_image(file: file) if IMAGE_TYPES.include? type
result = save_video(file: file, type: type) if VIDEO_TYPES.include? type
if result.success?
Success(result.value!)
else
Failure()
end
end
private
def pathname
Time.now.strftime("%m-%Y")
end
def uuid
SecureRandom.uuid
end
def save_video(file:, type:)
fullsize_filename = "#{uuid}.mp4"
dirname = File.join("public", "media", pathname)
unless File.directory?(dirname)
FileUtils.mkdir_p(dirname)
end
begin
case type
when :gif
Open3.popen3("ffmpeg -i #{file[:tempfile].path} -movflags faststart -pix_fmt yuv420p -vf 'scale=trunc(iw/2)*2:trunc(ih/2)*2' #{File.join(dirname, fullsize_filename)}")
when :iso
Open3.popen3("ffmpeg -i #{file[:tempfile].path} -vcodec libx264 -crf 28 #{File.join(dirname, fullsize_filename)}")
end
rescue Errno::ENOENT, NoMethodError => e
return Failure(e.message)
end
upload_path = File.join(settings.micropub_site_url, "/media/", "/#{pathname}/", fullsize_filename).to_s
Success(upload_path)
end
def save_image(file:)
fullsize_filename = "#{uuid}#{File.extname(file[:filename])}"
thumbnail_filename = "#{uuid}-small#{File.extname(file[:filename])}"
dirname = File.join("public", "media", pathname)
fullsize_pipeline = ImageProcessing::Vips.source(file[:tempfile])
.resize_to_limit(1024, nil)
.saver(quality: 100)
.call(save: false)
thumbnail_pipeline = ImageProcessing::Vips.source(file[:tempfile])
.resize_to_limit(300, 300, crop: :attention)
.saver(quality: 100)
.call(save: false)
unless File.directory?(dirname)
FileUtils.mkdir_p(dirname)
end
begin
fullsize_pipeline.write_to_file(File.join(dirname, fullsize_filename))
thumbnail_pipeline.write_to_file(File.join(dirname, thumbnail_filename))
rescue Errno::ENOENT, NoMethodError => e
return Failure(e.message)
end
upload_path = File.join(settings.micropub_site_url, "/media/", "/#{pathname}/", fullsize_filename).to_s
Success(upload_path)
end
end
end
end
end

View File

@@ -1,18 +0,0 @@
module Adamantium
module Commands
module Posts
class AddSyndicationSource
include Deps["repos.post_repo"]
def call(post_id, source, url)
post = post_repo.find!(post_id).to_h
syndication_sources = post[:syndication_sources] || {}
syndication_sources[source] = url
post[:syndication_sources] = syndication_sources
post_repo.update(post_id, post)
end
end
end
end
end

View File

@@ -1,19 +0,0 @@
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

View File

@@ -1,32 +0,0 @@
require "dry/monads"
module Adamantium
module Commands
module Posts
class CreateBookmark < Command
include Deps["repos.post_repo",
"post_utilities.page_cacher",
syndicate: "commands.posts.syndicate",
raindrop: "syndication.raindrop",
]
include Dry::Monads[:result]
def call(bookmark)
created_bookmark = post_repo.create(bookmark)
syndicate.call(created_bookmark.id, bookmark)
raindrop.call(post: created_bookmark)
if bookmark[:cache]
page_cacher.call(url: created_bookmark.url) do |content|
post_repo.update(created_bookmark.id, cached_content: content)
end
end
Success(created_bookmark)
end
end
end
end
end

View File

@@ -1,42 +0,0 @@
require "dry/monads"
module Adamantium
module Commands
module Posts
class CreateCheckin < Command
include Deps["repos.post_repo",
"post_utilities.slugify",
"logger",
renderer: "renderers.markdown",
add_post_syndication_source: "commands.posts.add_syndication_source"
]
include Dry::Monads[:result]
def call(post)
syndication_sources = post.delete(:syndication_sources)
post_params = prepare_params(params: post)
created_post = post_repo.create(post_params)
syndication_sources.each do |url|
add_post_syndication_source.call(created_post.id, :swarm, url)
end
# decorated_post = Decorators::Posts::Decorator.new(created_post)
# send_webmentions.call(post_content: attrs[:content], post_url: decorated_post.permalink)
Success(created_post)
end
private
def prepare_params(params:)
attrs = params.to_h
attrs[:content] = renderer.call(content: attrs[:content]) if attrs[:content]
attrs
end
end
end
end
end

View File

@@ -1,43 +0,0 @@
require "dry/monads"
module Adamantium
module Commands
module Posts
class CreateEntry < Command
include Deps["repos.post_repo",
"post_utilities.slugify",
renderer: "renderers.markdown",
syndicate: "commands.posts.syndicate",
send_to_dayone: "syndication.dayone",
send_webmentions: "commands.posts.send_webmentions",
auto_tag: "commands.auto_tagging.tag",
]
include Dry::Monads[:result]
def call(post)
post_params = prepare_params(params: post)
created_post = post_repo.create(post_params)
auto_tag.call
syndicate.call(created_post.id, post)
decorated_post = Decorators::Posts::Decorator.new(created_post)
send_webmentions.call(post_content: created_post.content, post_url: decorated_post.permalink)
Success(created_post)
end
private
def prepare_params(params:)
attrs = params.to_h
attrs[:content] = renderer.call(content: attrs[:content])
attrs
end
end
end
end
end

View File

@@ -1,31 +0,0 @@
module Adamantium
module Commands
module Posts
class CreationResolver
include Deps[
"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_book_post"
]
def call(entry_type:)
case entry_type
in Entities::BookmarkRequest
{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
end
end
end
end
end

View File

@@ -1,14 +0,0 @@
module Adamantium
module Commands
module Posts
class Delete < Command
include Deps["repos.post_repo"]
def call(params:)
slug = URI(params[:url]).path.split("/").last
post_repo.delete!(slug)
end
end
end
end
end

View File

@@ -1,18 +0,0 @@
require "httparty"
require "que"
module Adamantium
module Commands
module Posts
class SendWebmentions
include Deps["settings", "post_utilities.link_finder"]
def call(post_content:, post_url:)
Que.connection = Adamantium::Container["persistence.db"]
Adamantium::Jobs::SendWebMentions.enqueue(post_content: post_content, post_url: post_url)
end
end
end
end
end

View File

@@ -1,51 +0,0 @@
require "dry/monads"
require "dry/monads/do"
module Adamantium
module Commands
module Posts
class Syndicate
include Dry::Monads[:result]
include Dry::Monads::Do.for(:call)
include Deps["settings",
"syndication.mastodon",
"syndication.blue_sky",
add_post_syndication_source: "commands.posts.add_syndication_source",
send_to_dayone: "syndication.dayone",
]
def call(post_id, post)
syndicate_to = syndication_targets(post[:syndicate_to])
if syndicate_to.include? :mastodon
res = mastodon.call(post: post)
add_post_syndication_source.call(post_id, :mastodon, res.value!) if res.success?
end
if syndicate_to.include? :blue_sky
res = blue_sky.call(post: post)
add_post_syndication_source.call(post_id, :blue_sky, res.value!) if res.success?
end
if post[:category].include? "weekly"
send_to_dayone.call(name: post[:name], content: post[:content])
end
Success()
end
private
def syndication_targets(syndicate_to)
targets = []
targets << :mastodon if syndicate_to.any? { |url| settings.mastodon_server.match(/#{url}/) }
targets << :blue_sky if syndicate_to.any? { |url| settings.blue_sky_url.match(/#{url}/) }
targets
end
end
end
end
end

View File

@@ -1,14 +0,0 @@
module Adamantium
module Commands
module Posts
class Undelete < Command
include Deps["repos.post_repo"]
def call(params:)
slug = URI(params[:url]).path.split("/").last
post_repo.restore!(slug)
end
end
end
end
end

View File

@@ -1,72 +0,0 @@
module Adamantium
module Commands
module Posts
class Update < Command
include Deps[
"repos.post_repo",
"renderers.markdown",
"commands.posts.add_syndication_source"
]
def call(params:)
slug = URI(params[:url]).path.split("/").last
post = post_repo.fetch!(slug)
if params.key? :replace
content = params[:replace].delete(:content).first
name = params[:replace].delete(:name)
attrs_to_replace = {}
attrs_to_replace[:name] = name if name
attrs_to_replace[:content] = markdown.call(content: content) if content
post_repo.update(post.id, attrs_to_replace)
end
if params.key? :add
attrs_to_add = {}
syndication = params[:add].delete(:syndication)&.first
tags = params[:add].delete(:category)
content = params[:add].delete(:content)&.first
name = params[:add].delete(:name)
attrs_to_add[:name] = name if post.name.empty?
attrs_to_add[:content] = markdown.call(content: content) if post.content.empty?
params[:add].keys.each_with_object(attrs_to_add) do |attr, memo|
memo[attr] = params[:add][attr].first if post.fetch(attr, nil).nil?
end
post_repo.update(post.id, attrs_to_add) unless attrs_to_add.empty?
post_repo.tag_post(post_id: post.id, tags: tags) if tags && !tags.empty?
add_syndication_source.call(post.id, "", syndication) if syndication && !syndication.empty?
end
if params.key? :delete
if params[:delete].is_a? Hash
tags = params[:delete][:category]
tags&.each do |tag|
post_repo.remove_tag(post_id: post.id, tag: tag)
end
elsif params[:delete].is_a? Array
if params[:delete].delete("category")
post.tags.each do |tag|
post_repo.remove_tag(post_id: post.id, tag: tag.label)
end
end
attrs = {}
params[:delete].each do |attr|
attrs[attr.to_sym] = nil
end
post_repo.update(post.id, attrs) unless attrs.empty?
end
end
end
end
end
end
end

View File

@@ -1,18 +0,0 @@
require "securerandom"
require "dry/monads"
require "filemagic"
module Adamantium
module Commands
module Workouts
class Create < Command
include Deps["repos.workout_repo"]
include Dry::Monads[:result]
def call(svg:, distance:, duration:)
workout_repo.create(path: svg, distance: distance, duration: duration, published_at: Time.now)
end
end
end
end
end

0
app/entities/.keep Normal file
View File

View File

@@ -1,13 +0,0 @@
module Adamantium
module Entities
class AutoTagging < Dry::Struct
attribute? :title_contains, Types::Optional::String
attribute? :body_contains, Types::Optional::String
attribute :tag_id, Types::Coercible::Integer
def title_only?
!title_contains.empty?
end
end
end
end

View File

@@ -1,15 +0,0 @@
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

View File

@@ -1,19 +0,0 @@
module Adamantium
module Entities
class BookmarkRequest < Dry::Struct
attribute :h, Types::Coercible::String
attribute :action, Types::Coercible::String.optional
attribute :name, Types::Coercible::String
attribute :cache, Types::Params::Bool.optional
attribute :content, Types::Coercible::String.optional
attribute :url, Types::Coercible::String
attribute :slug, Types::Coercible::String
attribute :category, Types::Array.of(Types::Coercible::String)
attribute :published_at, Types::Nominal::DateTime.optional
attribute :post_type, Types::Coercible::String
attribute :syndicate_to, Types::Array.of(Types::Coercible::String)
attribute :photos, Types::Array.of(Types::Hash)
attribute :location, Types::Coercible::String.optional
end
end
end

View File

@@ -1,17 +0,0 @@
module Adamantium
module Entities
class CheckinRequest < Dry::Struct
attribute :h, Types::Coercible::String
attribute :name, Types::Coercible::String.optional
attribute :content, Types::Coercible::String.optional
attribute :slug, Types::Coercible::String
attribute :url, Types::Coercible::String
attribute :category, Types::Array.of(Types::Coercible::String)
attribute :published_at, Types::Nominal::DateTime.optional
attribute :post_type, Types::Coercible::String
attribute :syndication_sources, Types::Array.of(Types::Coercible::String)
attribute :photos, Types::Array.of(Types::Hash)
attribute :location, Types::Coercible::String
end
end
end

View File

@@ -1,17 +0,0 @@
module Adamantium
module Entities
class PostRequest < Dry::Struct
attribute :h, Types::Coercible::String
attribute :action, Types::Coercible::String.optional
attribute :name, Types::Coercible::String.optional
attribute :content, Types::Coercible::String
attribute :slug, Types::Coercible::String
attribute :category, Types::Array.of(Types::Coercible::String)
attribute :published_at, Types::Nominal::DateTime.optional
attribute :post_type, Types::Coercible::String
attribute :syndicate_to, Types::Array.of(Types::Coercible::String)
attribute :photos, Types::Array.of(Types::Hash)
attribute :location, Types::Coercible::String.optional
end
end
end

View File

@@ -1,37 +0,0 @@
require "reverse_markdown"
module Adamantium
module Queries
module Posts
class MicroformatPost
include Deps["repos.post_repo"]
def call(url:, properties:)
slug = URI(url).path.split("/").last
post = post_repo.fetch_unpublished!(slug)
markdown_content = ReverseMarkdown.convert(post.content, unknown_tags: :pass_through, github_flavored: true).to_s
if properties.nil? || properties.empty?
return {
type: ["h-entry"],
properties: {
published: [post.published_at],
content: [markdown_content],
photo: post.photos,
category: post.tags.map { |t| t.label.to_s }
}
}
end
result = {properties: {}}
result[:properties][:published] = [post.published_at] if properties.include? "published"
result[:properties][:content] = [markdown_content] if properties.include? "content"
result[:properties][:category] = post.tags.map { |t| t.label.to_s } if properties.include? "category"
result[:properties][:photos] = post.photos if properties.include? "photos"
result
end
end
end
end
end

View File

@@ -1,18 +0,0 @@
module Adamantium
module Repos
class AutoTaggingRepo < Adamantium::Repo[:auto_taggings]
def find(id)
auto_taggings
.where(id: id)
.map_to(Adamantium::Entities::AutoTagging)
.to_a
end
def all
auto_taggings
.map_to(Adamantium::Entities::AutoTagging)
.to_a
end
end
end
end

View File

@@ -3,86 +3,6 @@ module Adamantium
class PostRepo < Adamantium::Repo[:posts]
Sequel.extension :pg_json
Sequel.extension :pg_json_ops
commands update: :by_pk
def create(post_attrs)
posts.transaction do
new_post = posts.changeset(:create, post_attrs).commit
post_attrs[:category].each do |tag_name|
next if tag_name == ""
tag = posts.tags.where(label: tag_name).one ||
posts
.tags
.changeset(:create, {label: tag_name, slug: tag_name.downcase.strip.tr(" ", "-").gsub(/[^\w-]/, "")})
.commit
posts.post_tags.changeset(:create, {
post_id: new_post.id,
tag_id: tag[:id]
})
.commit
end
new_post
end
end
def tag_post(post_id:, tags:)
tags.each do |tag_name|
next if tag_name == ""
tag = posts.tags.where(label: tag_name).one ||
posts
.tags
.changeset(:create, {label: tag_name, slug: tag_name.downcase.strip.tr(" ", "-").gsub(/[^\w-]/, "")})
.commit
posts.post_tags.changeset(:create, {
post_id: post_id,
tag_id: tag[:id]
})
.commit
end
end
def auto_tag_post(post_id:, tag_id:)
return if posts
.post_tags
.where(
post_id: post_id,
tag_id: tag_id
).count > 0
posts
.post_tags
.changeset(:create, {
post_id: post_id,
tag_id: tag_id
})
.commit
end
def by_title(title_contains:)
posts
.where(post_type: "post")
.published
.where(Sequel.ilike(:name, "%#{title_contains}%")).to_a
end
def by_content(body_contains:)
posts
.where(post_type: "post")
.published
.where(Sequel.ilike(:content, "%#{body_contains}%")).to_a
end
def remove_tag(post_id:, tag:)
tag = posts.tags.where(label: tag).one
posts.post_tags.where(post_id: post_id, tag_id: tag[:id]).changeset(:delete).commit if tag
end
def by_year(year:)
posts
@@ -250,35 +170,12 @@ module Adamantium
.one!
end
def fetch_unpublished!(slug)
posts
.combine(:tags)
.where(slug: slug)
.one!
end
def find!(id)
posts
.by_pk(id)
.one!
end
def slug_exists?(slug)
!!posts
.where(slug: slug)
.one
end
def delete!(slug)
delete_post = posts.where(slug: slug).command(:update)
delete_post.call(published_at: nil)
end
def restore!(slug)
delete_post = posts.where(slug: slug).command(:update)
delete_post.call(published_at: Time.now)
end
def post_years
posts
.where(post_type: "post", location: nil)

View File

@@ -1,18 +0,0 @@
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 finished])
end
end
end
end
end

View File

@@ -1,20 +0,0 @@
module Adamantium
module Validation
module Posts
class BookmarkContract < Dry::Validation::Contract
params do
required(:name).filled(:string)
required(:content).maybe(:string)
required(:category).array(:string)
required(:published_at).maybe(:time)
required(:url).filled(:string)
required(:slug).filled(:string)
required(:post_type).value(included_in?: %w[bookmark])
required(:syndicate_to).array(:string)
required(:photos).array(:hash)
required(:cache).filled(:bool)
end
end
end
end
end

View File

@@ -1,19 +0,0 @@
module Adamantium
module Validation
module Posts
class CheckinContract < Dry::Validation::Contract
params do
required(:name).maybe(:string)
required(:content).maybe(:string)
required(:category).array(:string)
required(:published_at).maybe(:time)
required(:slug).filled(:string)
required(:post_type).value(included_in?: %w[checkin])
required(:syndication_sources).array(:string)
required(:photos).array(:hash)
required(:location).maybe(:string)
end
end
end
end
end

View File

@@ -1,19 +0,0 @@
module Adamantium
module Validation
module Posts
class PostContract < 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[post])
required(:syndicate_to).array(:string)
required(:photos).array(:hash)
required(:location).maybe(:string)
end
end
end
end
end