From fe1047932db2b72f7f9c61a362462b888905f18d Mon Sep 17 00:00:00 2001 From: Kirill Usanov Date: Sun, 22 Jun 2025 23:29:27 +0300 Subject: [PATCH] MVP --- example/example.rb | 132 ++++++++++++++++++++++++++++++++++ lib/apicraft.rb | 8 ++- lib/apicraft/array_node.rb | 17 +++++ lib/apicraft/info.rb | 35 +++++++++ lib/apicraft/info/contact.rb | 19 +++++ lib/apicraft/info/license.rb | 19 +++++ lib/apicraft/node.rb | 13 ++++ lib/apicraft/server.rb | 7 ++ lib/apicraft/servers.rb | 7 ++ lib/apicraft/specification.rb | 23 ++++++ 10 files changed, 279 insertions(+), 1 deletion(-) create mode 100644 example/example.rb create mode 100644 lib/apicraft/array_node.rb create mode 100644 lib/apicraft/info.rb create mode 100644 lib/apicraft/info/contact.rb create mode 100644 lib/apicraft/info/license.rb create mode 100644 lib/apicraft/node.rb create mode 100644 lib/apicraft/server.rb create mode 100644 lib/apicraft/servers.rb create mode 100644 lib/apicraft/specification.rb diff --git a/example/example.rb b/example/example.rb new file mode 100644 index 0000000..841eeef --- /dev/null +++ b/example/example.rb @@ -0,0 +1,132 @@ +APICraft.specification do + openapi_version "3.1.0" + + info do + title "Example API" + summary "Summary" + description do + <<~TEXT + This schema is used for testing purposes. + TEXT + end + terms_of_service "Terms of Service" + contact do + name "LassoID" + url "https://lassoid.ru/" + email "kirill@lassoid.ru" + end + license do + name "License" + identifier "License ID" + url "url" + end + version "1.0.0" + end + + json_schema_dialect "dialect" + + servers do + element do + url { "url" } + description { "description" } + variables do + var_name_1 do + enum { ["value11", "value12"] } + default { "value11" } + description { "description1" } + end + var_name_2 do + enum { ["value21", "value22"] } + default { "value21" } + description { "description2" } + end + end + end + end + + paths do + route "/users" do + get do + reference { "endpoints/users_list" } + end + post do + reference { "endpoints/user_create" } + end + options do + # NO + end + head do + # NO + end + trace do + # NO + end + end + route "/users/{id}" do + parameters do + element do + name { "id" } + in { "path" } + required { true } + deprecated { false } + description { "User ID" } + schema do + type { "string" } + end + end + end + + servers do + element do + url { "url2" } + end + end + + get do + reference { "endpoints/user_get" } + end + put do + reference { "endpoints/user_update" } + end + patch do + reference { "endpoints/user_update" } + end + delete do + reference { "endpoints/user_delete" } + end + options do + # NO + end + head do + # NO + end + trace do + # NO + end + end + end + + # components do + # schemas do + # ref { "./components/schemas.yml" } + # end + # parameters do + # + # end + # securitySchemes do + + # end + # end + + # security do + # [ + # jwt_token do + + # end, + # ] + # end + + # tags do + # "users" + # end +end diff --git a/lib/apicraft.rb b/lib/apicraft.rb index 6509cfe..66005a5 100644 --- a/lib/apicraft.rb +++ b/lib/apicraft.rb @@ -4,5 +4,11 @@ module APICraft class Error < StandardError; end - # Your code goes here... + + class << self + def specification(&block) + @specification = Specification.new + @specification.instance_exec(&block) + end + end end diff --git a/lib/apicraft/array_node.rb b/lib/apicraft/array_node.rb new file mode 100644 index 0000000..73b9d0a --- /dev/null +++ b/lib/apicraft/array_node.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +module APICraft + class ArrayNode < Node + def element_node + raise NotImplementedError + end + + def element(&block) + new_element = element_node.new + new_element.instance_exec(&block) + @elements << new_element + + new_element + end + end +end diff --git a/lib/apicraft/info.rb b/lib/apicraft/info.rb new file mode 100644 index 0000000..852a4ee --- /dev/null +++ b/lib/apicraft/info.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +module APICraft + class Info < Node + def title(value, &block) + @title = block ? instance_exec(&block).to_s : value.to_s + end + + def summary(value, &block) + @summary = block ? instance_exec(&block).to_s : value.to_s + end + + def description(value, &block) + @description = block ? instance_exec(&block).to_s : value.to_s + end + + def terms_of_service(value, &block) + @terms_of_service = block ? instance_exec(&block).to_s : value.to_s + end + + def contact(value, &block) + @contact = Contact.new + @contact.instance_exec(&block) + end + + def license(value, &block) + @license = License.new + @license.instance_exec(&block) + end + + def version(value, &block) + @version = block ? instance_exec(&block).to_s : value.to_s + end + end +end diff --git a/lib/apicraft/info/contact.rb b/lib/apicraft/info/contact.rb new file mode 100644 index 0000000..4b010ed --- /dev/null +++ b/lib/apicraft/info/contact.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module APICraft + class Info + class Contact < Node + def name(value, &block) + @name = block ? instance_exec(&block).to_s : value.to_s + end + + def url(value, &block) + @url = block ? instance_exec(&block).to_s : value.to_s + end + + def email(value, &block) + @email = block ? instance_exec(&block).to_s : value.to_s + end + end + end +end diff --git a/lib/apicraft/info/license.rb b/lib/apicraft/info/license.rb new file mode 100644 index 0000000..5230644 --- /dev/null +++ b/lib/apicraft/info/license.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module APICraft + class Info + class License < Node + def name(value, &block) + @name = block ? instance_exec(&block).to_s : value.to_s + end + + def identifier(value, &block) + @identifier = block ? instance_exec(&block).to_s : value.to_s + end + + def url(value, &block) + @url = block ? instance_exec(&block).to_s : value.to_s + end + end + end +end diff --git a/lib/apicraft/node.rb b/lib/apicraft/node.rb new file mode 100644 index 0000000..e11872f --- /dev/null +++ b/lib/apicraft/node.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module APICraft + class Node < BasicObject + class << self + def define_plain_field(method, *args, &block) + define_method(method) do |value, &block| + instance_variable_set("@#{method}", block ? instance_exec(&block) : value) + end + end + end + end +end diff --git a/lib/apicraft/server.rb b/lib/apicraft/server.rb new file mode 100644 index 0000000..9c750d5 --- /dev/null +++ b/lib/apicraft/server.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +module APICraft + class Servers < Node + + end +end diff --git a/lib/apicraft/servers.rb b/lib/apicraft/servers.rb new file mode 100644 index 0000000..11962fd --- /dev/null +++ b/lib/apicraft/servers.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +module APICraft + class Servers < ArrayNode + def element_node = Server + end +end diff --git a/lib/apicraft/specification.rb b/lib/apicraft/specification.rb new file mode 100644 index 0000000..b554c74 --- /dev/null +++ b/lib/apicraft/specification.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module APICraft + class Specification < Node + def openapi_version(value, &block) + @openapi_version = block ? instance_exec(&block).to_s : value.to_s + end + + def info(&block) + @info = Info.new + @info.instance_exec(&block) + end + + def json_schema_dialect(value, &block) + @json_schema_dialect = block ? instance_exec(&block).to_s : value.to_s + end + + def servers(&block) + @servers = Server.new + @servers.instance_exec(&block) + end + end +end