tomyum 0.1.0.a
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/README.md +1106 -0
- data/lib/tomyum/assertions.rb +80 -0
- data/lib/tomyum/attributes/array.rb +11 -0
- data/lib/tomyum/attributes/attribute.rb +130 -0
- data/lib/tomyum/attributes/boolean.rb +22 -0
- data/lib/tomyum/attributes/currency.rb +19 -0
- data/lib/tomyum/attributes/date.rb +11 -0
- data/lib/tomyum/attributes/float.rb +13 -0
- data/lib/tomyum/attributes/integer.rb +14 -0
- data/lib/tomyum/attributes/ip_address.rb +15 -0
- data/lib/tomyum/attributes/number.rb +24 -0
- data/lib/tomyum/attributes/object.rb +71 -0
- data/lib/tomyum/attributes/schema.rb +23 -0
- data/lib/tomyum/attributes/string.rb +36 -0
- data/lib/tomyum/attributes/time.rb +19 -0
- data/lib/tomyum/attributes/uri.rb +19 -0
- data/lib/tomyum/attributes/visitor.rb +136 -0
- data/lib/tomyum/attributes.rb +92 -0
- data/lib/tomyum/endpoint.rb +102 -0
- data/lib/tomyum/endpoints/method.rb +90 -0
- data/lib/tomyum/endpoints/params.rb +115 -0
- data/lib/tomyum/error.rb +17 -0
- data/lib/tomyum/functions.rb +49 -0
- data/lib/tomyum/generators/generator.rb +16 -0
- data/lib/tomyum/generators/grpc/generator.rb +10 -0
- data/lib/tomyum/generators/open_api/generator.rb +205 -0
- data/lib/tomyum/generators/open_api/property_generator.rb +111 -0
- data/lib/tomyum/generators.rb +3 -0
- data/lib/tomyum/registry.rb +75 -0
- data/lib/tomyum/resolvable.rb +11 -0
- data/lib/tomyum/resolver.rb +99 -0
- data/lib/tomyum/serializer.rb +125 -0
- data/lib/tomyum/serializers/serializable.rb +23 -0
- data/lib/tomyum/server/app.rb +33 -0
- data/lib/tomyum/server/document.rb +20 -0
- data/lib/tomyum/server/documents/redoc.rb +36 -0
- data/lib/tomyum/server/documents/swagger.rb +47 -0
- data/lib/tomyum/server/routes.rb +0 -0
- data/lib/tomyum/support.rb +13 -0
- data/lib/tomyum/validator.rb +205 -0
- data/lib/tomyum/validators/normalizable.rb +24 -0
- data/lib/tomyum/validators/proxy.rb +77 -0
- data/lib/tomyum/validators/validatable.rb +48 -0
- data/lib/tomyum/version.rb +3 -0
- data/lib/tomyum.rb +28 -0
- metadata +202 -0
| @@ -0,0 +1,111 @@ | |
| 1 | 
            +
            module Tomyum
         | 
| 2 | 
            +
              module Generators
         | 
| 3 | 
            +
                module OpenAPI
         | 
| 4 | 
            +
                  # Generate property based on Open API Spec
         | 
| 5 | 
            +
                  class PropertyGenerator < Tomyum::Attributes::Visitor
         | 
| 6 | 
            +
                    include Tomyum::Assertions
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                    class << self
         | 
| 9 | 
            +
                      def visit(attr, options = {})
         | 
| 10 | 
            +
                        new.visit(attr, options)
         | 
| 11 | 
            +
                      end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                      alias_method :generate, :visit
         | 
| 14 | 
            +
                    end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                    def visit(attr, options = {})
         | 
| 17 | 
            +
                      attr = normalize_attribute(attr, options)
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                      # handle case when attr is a Symbol (reference)
         | 
| 20 | 
            +
                      return attr unless attr.respond_to?(:native_type)
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                      super
         | 
| 23 | 
            +
                    end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                    # catch-all
         | 
| 26 | 
            +
                    def visit_attribute(attr, options = {})
         | 
| 27 | 
            +
                      build(attr, options)
         | 
| 28 | 
            +
                    end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                    def visit_array(attr, options = {})
         | 
| 31 | 
            +
                      build(attr, options).tap do |property|
         | 
| 32 | 
            +
                        property[:items] = attr.of ? visit(attr.of, options) : {}
         | 
| 33 | 
            +
                      end
         | 
| 34 | 
            +
                    end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                    def visit_object(attr, options = {})
         | 
| 37 | 
            +
                      build(attr, options).tap do |property|
         | 
| 38 | 
            +
                        required = []
         | 
| 39 | 
            +
                        attr.attributes.values.each_with_object(property) do |attribute, object|
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                          # Adds to required list
         | 
| 42 | 
            +
                          required << attribute.name if attribute.required
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                          object[:properties] ||= {}
         | 
| 45 | 
            +
                          object[:properties][attribute.name] = visit(attribute, options) if attribute.serializable?
         | 
| 46 | 
            +
                        end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                        # additional options
         | 
| 49 | 
            +
                        property[:required] = required unless required.empty?
         | 
| 50 | 
            +
                      end
         | 
| 51 | 
            +
                    end
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                    # Handle Schema
         | 
| 54 | 
            +
                    def visit_schema(attr, options)
         | 
| 55 | 
            +
                      return build_ref(attr.of) unless attr.expandable
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                      # Schema#of may contains array of references
         | 
| 58 | 
            +
                      # e.g. of = [:user, :customer]
         | 
| 59 | 
            +
                      refs = Array(attr.of).map { |of| build_ref(of) }
         | 
| 60 | 
            +
                      refs << visit("string")
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                      {
         | 
| 63 | 
            +
                        "oneOf": refs,
         | 
| 64 | 
            +
                      }
         | 
| 65 | 
            +
                    end
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                    def normalize_attribute(object, options = {})
         | 
| 68 | 
            +
                      return build_ref(object, options)    if object.kind_of?(Symbol)
         | 
| 69 | 
            +
                      return object                        if object.respond_to?(:native_type)
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                      # object is string (native types e.g. "integer", "boolean" etc)
         | 
| 72 | 
            +
                      Tomyum::Attributes.create(object.to_sym, object)
         | 
| 73 | 
            +
                    end
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                    private
         | 
| 76 | 
            +
             | 
| 77 | 
            +
                    def build(attr, options = {})
         | 
| 78 | 
            +
                      assert_kind_of Tomyum::Attributes::Attribute, attr
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                      # build base property
         | 
| 81 | 
            +
                      {
         | 
| 82 | 
            +
                        type: attr.native_type,
         | 
| 83 | 
            +
                      }.tap do |property|
         | 
| 84 | 
            +
                        property[:description]     = attr.description if attr.description.present?
         | 
| 85 | 
            +
                        property[:default]         = attr.default if attr.default
         | 
| 86 | 
            +
                        property[:enum]            = attr.in if attr.in
         | 
| 87 | 
            +
                        property[:nullable]        = attr.null if attr.null
         | 
| 88 | 
            +
                        property[:format]          = attr.format if attr.format
         | 
| 89 | 
            +
                        property[:pattern]         = build_pattern(attr.match) if attr.match
         | 
| 90 | 
            +
                        property["x-custom-schema"] = options[:schema] if options[:schema].present?
         | 
| 91 | 
            +
                      end
         | 
| 92 | 
            +
                    end
         | 
| 93 | 
            +
             | 
| 94 | 
            +
                    # create $ref
         | 
| 95 | 
            +
                    def build_ref(attr, options = {})
         | 
| 96 | 
            +
                      ref = options[:as] || attr
         | 
| 97 | 
            +
             | 
| 98 | 
            +
                      {
         | 
| 99 | 
            +
                        "$ref": "#/components/schemas/#{ref}",
         | 
| 100 | 
            +
                      }
         | 
| 101 | 
            +
                    end
         | 
| 102 | 
            +
             | 
| 103 | 
            +
                    def build_pattern(value)
         | 
| 104 | 
            +
                      return value.source if value.respond_to?(:source)
         | 
| 105 | 
            +
             | 
| 106 | 
            +
                      value
         | 
| 107 | 
            +
                    end
         | 
| 108 | 
            +
                  end
         | 
| 109 | 
            +
                end
         | 
| 110 | 
            +
              end
         | 
| 111 | 
            +
            end
         | 
| @@ -0,0 +1,75 @@ | |
| 1 | 
            +
            module Tomyum
         | 
| 2 | 
            +
              # A generic registry
         | 
| 3 | 
            +
              #
         | 
| 4 | 
            +
              # @example Create key/value pairs registry (Hash)
         | 
| 5 | 
            +
              #   registry = Registry.new do |key, value|
         | 
| 6 | 
            +
              #     { name: key, age: value }
         | 
| 7 | 
            +
              #   end
         | 
| 8 | 
            +
              #
         | 
| 9 | 
            +
              #   # Register object
         | 
| 10 | 
            +
              #   registry.register(:john, name: :john, age: 10)
         | 
| 11 | 
            +
              #
         | 
| 12 | 
            +
              #   # Retrieve object
         | 
| 13 | 
            +
              #   registry.get(:john) # => { name: :john, age: 10 }
         | 
| 14 | 
            +
              #
         | 
| 15 | 
            +
              #   # Create new object
         | 
| 16 | 
            +
              #   registry.create(:joe, 10) # => { name: joe, age: 10 }
         | 
| 17 | 
            +
              class Registry
         | 
| 18 | 
            +
                include Tomyum::Assertions
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                KeyError = Tomyum::Error.create("Object %s already exists")
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                # @param [Object] A collection of objects which has #name property
         | 
| 23 | 
            +
                # @param &block A {Proc} that'll create new object
         | 
| 24 | 
            +
                def initialize(objects = [], error: KeyError, &block)
         | 
| 25 | 
            +
                  assert_kind_of Proc, block
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                  @objects = normalize objects
         | 
| 28 | 
            +
                  @factory = block
         | 
| 29 | 
            +
                  @error   = error
         | 
| 30 | 
            +
                end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                # Registers new object by id
         | 
| 33 | 
            +
                #
         | 
| 34 | 
            +
                # @param id [String] Unique name
         | 
| 35 | 
            +
                # @param object [Object] Object to register
         | 
| 36 | 
            +
                # @param override [Boolean] Overrides existing key if exists
         | 
| 37 | 
            +
                def register(id, object, override: false)
         | 
| 38 | 
            +
                  if key?(id) && !override
         | 
| 39 | 
            +
                    raise @error, id
         | 
| 40 | 
            +
                  end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                  @objects[id.to_sym] = object
         | 
| 43 | 
            +
                end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                # Creates new object by using a {Proc} from #new
         | 
| 46 | 
            +
                #
         | 
| 47 | 
            +
                # @return [Object]
         | 
| 48 | 
            +
                # @example
         | 
| 49 | 
            +
                #   registry.create(:user, "john")
         | 
| 50 | 
            +
                def create(id, *args, &block)
         | 
| 51 | 
            +
                  instance_exec(id, *args, block, &@factory)
         | 
| 52 | 
            +
                end
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                # Retrieves registered Object by key
         | 
| 55 | 
            +
                def get(id)
         | 
| 56 | 
            +
                  raise @error, id unless key?(id)
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                  @objects[id.to_sym]
         | 
| 59 | 
            +
                end
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                def key?(id)
         | 
| 62 | 
            +
                  @objects.key? id
         | 
| 63 | 
            +
                end
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                def keys
         | 
| 66 | 
            +
                  @objects.keys
         | 
| 67 | 
            +
                end
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                private
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                def normalize(objects = [])
         | 
| 72 | 
            +
                  (objects || []).each_with_object({}) { |object, target| target[object.name] = object }
         | 
| 73 | 
            +
                end
         | 
| 74 | 
            +
              end
         | 
| 75 | 
            +
            end
         | 
| @@ -0,0 +1,99 @@ | |
| 1 | 
            +
            module Tomyum
         | 
| 2 | 
            +
              # Responsible for registering and looking up the schemas and endpoints
         | 
| 3 | 
            +
              #
         | 
| 4 | 
            +
              # @example Register via +:schemas+ and +:endpoints+ options
         | 
| 5 | 
            +
              #   resolver = Resolver.new(schemas: [], endpoints: [])
         | 
| 6 | 
            +
              #
         | 
| 7 | 
            +
              # @example Register via +block+
         | 
| 8 | 
            +
              #   resolver = Resolver.new do
         | 
| 9 | 
            +
              #     schema :user do
         | 
| 10 | 
            +
              #       string :username
         | 
| 11 | 
            +
              #     end
         | 
| 12 | 
            +
              #
         | 
| 13 | 
            +
              #     schema :team do
         | 
| 14 | 
            +
              #       string :name
         | 
| 15 | 
            +
              #     end
         | 
| 16 | 
            +
              #
         | 
| 17 | 
            +
              #     endpoint "/users" do
         | 
| 18 | 
            +
              #       string :username
         | 
| 19 | 
            +
              #     end
         | 
| 20 | 
            +
              #   end
         | 
| 21 | 
            +
              #
         | 
| 22 | 
            +
              # @example Manually register new schema/endpoint
         | 
| 23 | 
            +
              #   resolver.schema(:user) do
         | 
| 24 | 
            +
              #     string :username
         | 
| 25 | 
            +
              #   end
         | 
| 26 | 
            +
              #
         | 
| 27 | 
            +
              #   resolver.endpoint("/teams") do
         | 
| 28 | 
            +
              #     post :create
         | 
| 29 | 
            +
              #   end
         | 
| 30 | 
            +
              #
         | 
| 31 | 
            +
              # @example Register an existing schema/endpoint
         | 
| 32 | 
            +
              #   resolver.schemas.register(:user, user_schema)
         | 
| 33 | 
            +
              #
         | 
| 34 | 
            +
              class Resolver
         | 
| 35 | 
            +
                Endpoint   = Tomyum::Endpoint
         | 
| 36 | 
            +
                Attributes = Tomyum::Attributes
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                SchemaNotFound   = Tomyum::Error.create("Endpoint %s cannot be found")
         | 
| 39 | 
            +
                EndpointNotFound = Tomyum::Error.create("Schema %s cannot be found")
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                def initialize(schemas: [], endpoints: [], schema_registry: nil, endpoint_registry: nil, &block)
         | 
| 42 | 
            +
                  @registries = {
         | 
| 43 | 
            +
                    schemas:   create_schema_registry(schemas, schema_registry),
         | 
| 44 | 
            +
                    endpoints: create_endpoint_registry(endpoints, endpoint_registry)
         | 
| 45 | 
            +
                  }
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                  instance_exec(&block) if block_given?
         | 
| 48 | 
            +
                end
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                def resolve(type, name)
         | 
| 51 | 
            +
                  @registries[type].get(name)
         | 
| 52 | 
            +
                end
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                def register(type, name, args, &block)
         | 
| 55 | 
            +
                  @registries[type].create(name, **args, &block)
         | 
| 56 | 
            +
                end
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                # Exposes schema registry
         | 
| 59 | 
            +
                def schemas
         | 
| 60 | 
            +
                  @registries[:schemas]
         | 
| 61 | 
            +
                end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                # Exposes endpoint registry
         | 
| 64 | 
            +
                def endpoints
         | 
| 65 | 
            +
                  @registries[:endpoints]
         | 
| 66 | 
            +
                end
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                def schema(name, options = {}, &block)
         | 
| 69 | 
            +
                  return resolve(:schemas, name) unless block_given?
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                  register(:schemas, name, options, &block)
         | 
| 72 | 
            +
                end
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                def endpoint(path, options = {}, &block)
         | 
| 75 | 
            +
                  return resolve(:endpoints, Endpoint.normalize_name(path)) unless block_given?
         | 
| 76 | 
            +
             | 
| 77 | 
            +
                  register(:endpoints, path, options, &block)
         | 
| 78 | 
            +
                end
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                private
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                def create_schema_registry(schemas, registry = nil)
         | 
| 83 | 
            +
                  return registry.(schemas) if registry
         | 
| 84 | 
            +
             | 
| 85 | 
            +
                  Registry.new(schemas, error: SchemaNotFound) do |name, options = {}, block|
         | 
| 86 | 
            +
                    @objects[name] = Attributes.create(:object, name, options, &block)
         | 
| 87 | 
            +
                  end
         | 
| 88 | 
            +
                end
         | 
| 89 | 
            +
             | 
| 90 | 
            +
                def create_endpoint_registry(endpoints, registry = nil)
         | 
| 91 | 
            +
                  return registry.(endpoints) if registry
         | 
| 92 | 
            +
             | 
| 93 | 
            +
                  Registry.new(endpoints, error: EndpointNotFound) do |path, options = {}, block|
         | 
| 94 | 
            +
                    endpoint = Endpoint.new(path, options, &block)
         | 
| 95 | 
            +
                    @objects[endpoint.name] = endpoint
         | 
| 96 | 
            +
                  end
         | 
| 97 | 
            +
                end
         | 
| 98 | 
            +
              end
         | 
| 99 | 
            +
            end
         | 
| @@ -0,0 +1,125 @@ | |
| 1 | 
            +
            module Tomyum
         | 
| 2 | 
            +
              # user_schema = Attributes.create(:object) do
         | 
| 3 | 
            +
              #   string :username
         | 
| 4 | 
            +
              #   string :email
         | 
| 5 | 
            +
              # end
         | 
| 6 | 
            +
              #
         | 
| 7 | 
            +
              # user = <any object>
         | 
| 8 | 
            +
              #
         | 
| 9 | 
            +
              # resolver   = Resolver.new(schemas: [user_schema, ...])
         | 
| 10 | 
            +
              # serializer.serialize(user_schema, user)
         | 
| 11 | 
            +
              class Serializer < Tomyum::Attributes::Visitor
         | 
| 12 | 
            +
                include Tomyum::Resolvable
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                InvalidPredicate = Tomyum::Error.create("Invalid predicate value (must be either Symbol or Proc)")
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                def initialize(resolver = nil)
         | 
| 17 | 
            +
                  @resolver = resolver
         | 
| 18 | 
            +
                end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                # Override to make it accepts 3 arguments
         | 
| 21 | 
            +
                def normalize_attribute(attribute, object, options = {})
         | 
| 22 | 
            +
                  super(attribute, options)
         | 
| 23 | 
            +
                end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                # catch all
         | 
| 26 | 
            +
                def visit_attribute(attribute, object, options = {})
         | 
| 27 | 
            +
                  attribute.serialize(attribute.value(object))
         | 
| 28 | 
            +
                end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                # Primitive attribute
         | 
| 31 | 
            +
                # -------------------
         | 
| 32 | 
            +
                # serialize(string, "user")
         | 
| 33 | 
            +
                # serialize(object, { name: "john" })
         | 
| 34 | 
            +
                #
         | 
| 35 | 
            +
                # Schema Attribute
         | 
| 36 | 
            +
                # ----------------
         | 
| 37 | 
            +
                # schema = Attributes.create(:object, :user) do
         | 
| 38 | 
            +
                #   string :username
         | 
| 39 | 
            +
                # end
         | 
| 40 | 
            +
                #
         | 
| 41 | 
            +
                # class User
         | 
| 42 | 
            +
                #   def initialize(data)
         | 
| 43 | 
            +
                #     @data = data
         | 
| 44 | 
            +
                #   end
         | 
| 45 | 
            +
                #
         | 
| 46 | 
            +
                #   def username
         | 
| 47 | 
            +
                #     @data[:name]
         | 
| 48 | 
            +
                #   end
         | 
| 49 | 
            +
                # end
         | 
| 50 | 
            +
                # user = User.new(name: "john")
         | 
| 51 | 
            +
                #
         | 
| 52 | 
            +
                # serialize(schema, user)
         | 
| 53 | 
            +
                alias :serialize :visit
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                # Defines visitors for primitive types e.g. `visit_string` etc
         | 
| 56 | 
            +
                Tomyum::Attributes::SCALAR_TYPES.each do |type|
         | 
| 57 | 
            +
                  alias_method "visit_#{type}", :visit_attribute
         | 
| 58 | 
            +
                end
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                def visit_object(attribute, object, options = {})
         | 
| 61 | 
            +
                  visit_object_handler(attribute, object) do |attr, value, attrs|
         | 
| 62 | 
            +
                    attrs[attr.name] = visit(attr, value, options)
         | 
| 63 | 
            +
                  end
         | 
| 64 | 
            +
                end
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                def visit_array(attribute, object, options = {})
         | 
| 67 | 
            +
                  as = attribute.of
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                  Array(object).map do |item|
         | 
| 70 | 
            +
                    # item#serialize_as will be used when of: option is not specified.
         | 
| 71 | 
            +
                    # e.g. ListSerializer.schema has `array :data`
         | 
| 72 | 
            +
                    # as = attribute.of || (item.serialize_as if item.respond_to?(:serialize_as))
         | 
| 73 | 
            +
                    unless as.kind_of?(Symbol)
         | 
| 74 | 
            +
                      item
         | 
| 75 | 
            +
                    else
         | 
| 76 | 
            +
                      visit(as, item, options)
         | 
| 77 | 
            +
                    end
         | 
| 78 | 
            +
                  end
         | 
| 79 | 
            +
                end
         | 
| 80 | 
            +
             | 
| 81 | 
            +
                def visit_schema(attribute, object, options = {})
         | 
| 82 | 
            +
                  attribute = resolver.schema(attribute.of)
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                  visit_object(attribute, object, options)
         | 
| 85 | 
            +
                end
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                private
         | 
| 88 | 
            +
             | 
| 89 | 
            +
                def get(object, name)
         | 
| 90 | 
            +
                  object.respond_to?(name) ? object.send(name) : object&.fetch(name, nil)
         | 
| 91 | 
            +
                end
         | 
| 92 | 
            +
             | 
| 93 | 
            +
                def visit_object_handler(attribute, object)
         | 
| 94 | 
            +
                  attribute.attributes.values.each_with_object({}) do |attr, attrs|
         | 
| 95 | 
            +
                    next unless serializable?(attr, object)
         | 
| 96 | 
            +
             | 
| 97 | 
            +
                    # get value for each field
         | 
| 98 | 
            +
                    value = get(object, attr.method)
         | 
| 99 | 
            +
             | 
| 100 | 
            +
                    yield(attr, value, attrs)
         | 
| 101 | 
            +
                  end
         | 
| 102 | 
            +
                end
         | 
| 103 | 
            +
             | 
| 104 | 
            +
                def serializable?(attr, object)
         | 
| 105 | 
            +
                  return unless attr.serializable?
         | 
| 106 | 
            +
             | 
| 107 | 
            +
                  # check predicate :if and :unless
         | 
| 108 | 
            +
                  return execute_predicate(attr.if, object)      if attr.if
         | 
| 109 | 
            +
                  return !execute_predicate(attr.unless, object) if attr.unless
         | 
| 110 | 
            +
             | 
| 111 | 
            +
                  true
         | 
| 112 | 
            +
                end
         | 
| 113 | 
            +
             | 
| 114 | 
            +
                def execute_predicate(predicate, object)
         | 
| 115 | 
            +
                  case predicate
         | 
| 116 | 
            +
                  when Symbol
         | 
| 117 | 
            +
                    get(object, predicate)
         | 
| 118 | 
            +
                  when Proc
         | 
| 119 | 
            +
                    predicate.call(object)
         | 
| 120 | 
            +
                  else
         | 
| 121 | 
            +
                    raise InvalidPredicate
         | 
| 122 | 
            +
                  end
         | 
| 123 | 
            +
                end
         | 
| 124 | 
            +
              end
         | 
| 125 | 
            +
            end
         | 
| @@ -0,0 +1,23 @@ | |
| 1 | 
            +
            module Tomyum
         | 
| 2 | 
            +
              module Serializers
         | 
| 3 | 
            +
                module Serializable
         | 
| 4 | 
            +
                  extend Tomyum::Support
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                  included do
         | 
| 7 | 
            +
                    class << self
         | 
| 8 | 
            +
                      attr_accessor :serializer
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                      def serializer
         | 
| 11 | 
            +
                        @serializer || superclass.serializer
         | 
| 12 | 
            +
                      end
         | 
| 13 | 
            +
                    end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                    self.serializer = ->(v) { v }
         | 
| 16 | 
            +
                  end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                  def serialize(value)
         | 
| 19 | 
            +
                    self.class.serializer.(value)
         | 
| 20 | 
            +
                  end
         | 
| 21 | 
            +
                end
         | 
| 22 | 
            +
              end
         | 
| 23 | 
            +
            end
         | 
| @@ -0,0 +1,33 @@ | |
| 1 | 
            +
            module Tomyum
         | 
| 2 | 
            +
              module Server
         | 
| 3 | 
            +
                class App
         | 
| 4 | 
            +
                  def initialize(resolver = nil)
         | 
| 5 | 
            +
                    @resolver = resolver
         | 
| 6 | 
            +
                  end
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                  def call(env)
         | 
| 9 | 
            +
                    resolver = Tomyum::Resolver.new do
         | 
| 10 | 
            +
                      endpoint "/users" do
         | 
| 11 | 
            +
                        get :create, path: "/"
         | 
| 12 | 
            +
                      end
         | 
| 13 | 
            +
                    end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                    body = ['<h1>Hello</h1>']
         | 
| 16 | 
            +
                    resolver.endpoints.keys.each do |key|
         | 
| 17 | 
            +
                      endpoint = resolver.endpoints.get(key)
         | 
| 18 | 
            +
                      methods  = endpoint.methods
         | 
| 19 | 
            +
                      methods.map do |name, method|
         | 
| 20 | 
            +
                        path = [endpoint.path, method.path].map { |r| r.chomp '/' }.reject(&:empty?).join("/")
         | 
| 21 | 
            +
                        if env["REQUEST_PATH"] == path && env["REQUEST_METHOD"].downcase == method.verb.to_s
         | 
| 22 | 
            +
                          body = ["Path #{path}"]
         | 
| 23 | 
            +
                        end
         | 
| 24 | 
            +
                      end
         | 
| 25 | 
            +
                    end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                    status  = 200
         | 
| 28 | 
            +
                    headers = { "Content-Type" => "text/html" }
         | 
| 29 | 
            +
                    [status, headers, body]
         | 
| 30 | 
            +
                  end
         | 
| 31 | 
            +
                end
         | 
| 32 | 
            +
              end
         | 
| 33 | 
            +
            end
         | 
| @@ -0,0 +1,20 @@ | |
| 1 | 
            +
            require_relative "documents/swagger"
         | 
| 2 | 
            +
            require_relative "documents/redoc"
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            module Tomyum
         | 
| 5 | 
            +
              module Server
         | 
| 6 | 
            +
                class Document
         | 
| 7 | 
            +
                  def initialize(app, path: "/doc")
         | 
| 8 | 
            +
                    @app = app
         | 
| 9 | 
            +
                    @path = path
         | 
| 10 | 
            +
                  end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                  def call(env)
         | 
| 13 | 
            +
                    request = Rack::Request.new(env)
         | 
| 14 | 
            +
                    return @app.call(env) if request.path != @path
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                    [200, {}, [Documents::Redoc.new.call]]
         | 
| 17 | 
            +
                  end
         | 
| 18 | 
            +
                end
         | 
| 19 | 
            +
              end
         | 
| 20 | 
            +
            end
         | 
| @@ -0,0 +1,36 @@ | |
| 1 | 
            +
            module Tomyum
         | 
| 2 | 
            +
              module Server
         | 
| 3 | 
            +
                module Documents
         | 
| 4 | 
            +
                  class Redoc
         | 
| 5 | 
            +
                    def call
         | 
| 6 | 
            +
            <<~HTML
         | 
| 7 | 
            +
            <!DOCTYPE html>
         | 
| 8 | 
            +
            <html>
         | 
| 9 | 
            +
              <head>
         | 
| 10 | 
            +
                <title>Redoc</title>
         | 
| 11 | 
            +
                <!-- needed for adaptive design -->
         | 
| 12 | 
            +
                <meta charset="utf-8"/>
         | 
| 13 | 
            +
                <meta name="viewport" content="width=device-width, initial-scale=1">
         | 
| 14 | 
            +
                <link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700" rel="stylesheet">
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                <!--
         | 
| 17 | 
            +
                Redoc doesn't change outer page styles
         | 
| 18 | 
            +
                -->
         | 
| 19 | 
            +
                <style>
         | 
| 20 | 
            +
                  body {
         | 
| 21 | 
            +
                    margin: 0;
         | 
| 22 | 
            +
                    padding: 0;
         | 
| 23 | 
            +
                  }
         | 
| 24 | 
            +
                </style>
         | 
| 25 | 
            +
              </head>
         | 
| 26 | 
            +
              <body>
         | 
| 27 | 
            +
                <redoc spec-url='http://petstore.swagger.io/v2/swagger.json'></redoc>
         | 
| 28 | 
            +
                <script src="https://cdn.jsdelivr.net/npm/redoc@latest/bundles/redoc.standalone.js"> </script>
         | 
| 29 | 
            +
              </body>
         | 
| 30 | 
            +
            </html>
         | 
| 31 | 
            +
            HTML
         | 
| 32 | 
            +
                    end
         | 
| 33 | 
            +
                  end
         | 
| 34 | 
            +
                end
         | 
| 35 | 
            +
              end
         | 
| 36 | 
            +
            end
         | 
| @@ -0,0 +1,47 @@ | |
| 1 | 
            +
            module Tomyum
         | 
| 2 | 
            +
              module Server
         | 
| 3 | 
            +
                module Documents
         | 
| 4 | 
            +
                  class Swagger
         | 
| 5 | 
            +
                    def call
         | 
| 6 | 
            +
             <<~HTML
         | 
| 7 | 
            +
              <!DOCTYPE html>
         | 
| 8 | 
            +
              <html xmlns="http://www.w3.org/1999/xhtml">
         | 
| 9 | 
            +
              <head>
         | 
| 10 | 
            +
                  <meta charset="UTF-8">
         | 
| 11 | 
            +
                  <link rel="stylesheet" type="text/css" href="https://unpkg.com/swagger-ui-dist@3.12.1/swagger-ui.css">
         | 
| 12 | 
            +
              </head>
         | 
| 13 | 
            +
              <body>
         | 
| 14 | 
            +
             | 
| 15 | 
            +
              <div id="swagger-ui"></div>
         | 
| 16 | 
            +
             | 
| 17 | 
            +
              <script src="https://unpkg.com/swagger-ui-dist@3.12.1/swagger-ui-standalone-preset.js"></script>
         | 
| 18 | 
            +
              <script src="https://unpkg.com/swagger-ui-dist@3.12.1/swagger-ui-bundle.js"></script>
         | 
| 19 | 
            +
             | 
| 20 | 
            +
              <script>
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                  window.onload = function() {
         | 
| 23 | 
            +
                      // Build a system
         | 
| 24 | 
            +
                      const ui = SwaggerUIBundle({
         | 
| 25 | 
            +
                          url: "https://petstore.swagger.io/v2/swagger.json",
         | 
| 26 | 
            +
                          dom_id: '#swagger-ui',
         | 
| 27 | 
            +
                          deepLinking: true,
         | 
| 28 | 
            +
                          presets: [
         | 
| 29 | 
            +
                              SwaggerUIBundle.presets.apis,
         | 
| 30 | 
            +
                              SwaggerUIStandalonePreset
         | 
| 31 | 
            +
                          ],
         | 
| 32 | 
            +
                          // plugins: [
         | 
| 33 | 
            +
                          //    SwaggerUIBundle.plugins.DownloadUrl
         | 
| 34 | 
            +
                          // ],
         | 
| 35 | 
            +
                          layout: "StandaloneLayout",
         | 
| 36 | 
            +
                      })
         | 
| 37 | 
            +
                      // window.ui = ui
         | 
| 38 | 
            +
                  }
         | 
| 39 | 
            +
              </script>
         | 
| 40 | 
            +
              </body>
         | 
| 41 | 
            +
              </html>
         | 
| 42 | 
            +
            HTML
         | 
| 43 | 
            +
                    end
         | 
| 44 | 
            +
                  end
         | 
| 45 | 
            +
                end
         | 
| 46 | 
            +
              end
         | 
| 47 | 
            +
            end
         | 
| 
            File without changes
         | 
| @@ -0,0 +1,13 @@ | |
| 1 | 
            +
            module Tomyum
         | 
| 2 | 
            +
              module Support
         | 
| 3 | 
            +
                # Mimic Active Support's .included behaviours
         | 
| 4 | 
            +
                def included(base = nil, &block)
         | 
| 5 | 
            +
                  if base.nil?
         | 
| 6 | 
            +
                    @_included_block = block
         | 
| 7 | 
            +
                  else
         | 
| 8 | 
            +
                    base.extend const_get(:ClassMethods) if const_defined?(:ClassMethods)
         | 
| 9 | 
            +
                    base.class_eval(&@_included_block) if instance_variable_defined?(:@_included_block)
         | 
| 10 | 
            +
                  end
         | 
| 11 | 
            +
                end
         | 
| 12 | 
            +
              end
         | 
| 13 | 
            +
            end
         |