sober_swag 0.1.0
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/.github/workflows/ruby.yml +33 -0
- data/.gitignore +11 -0
- data/.rspec +3 -0
- data/.rubocop.yml +7 -0
- data/.ruby-version +1 -0
- data/.travis.yml +7 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +92 -0
- data/LICENSE.txt +21 -0
- data/README.md +7 -0
- data/Rakefile +8 -0
- data/bin/console +38 -0
- data/bin/setup +8 -0
- data/example/.gitignore +24 -0
- data/example/.ruby-version +1 -0
- data/example/Gemfile +42 -0
- data/example/Gemfile.lock +212 -0
- data/example/README.md +24 -0
- data/example/Rakefile +6 -0
- data/example/app/controllers/application_controller.rb +2 -0
- data/example/app/controllers/concerns/.keep +0 -0
- data/example/app/controllers/people_controller.rb +74 -0
- data/example/app/jobs/application_job.rb +7 -0
- data/example/app/models/application_record.rb +3 -0
- data/example/app/models/concerns/.keep +0 -0
- data/example/app/models/person.rb +2 -0
- data/example/bin/bundle +114 -0
- data/example/bin/rails +9 -0
- data/example/bin/rake +9 -0
- data/example/bin/setup +33 -0
- data/example/bin/spring +17 -0
- data/example/config/application.rb +37 -0
- data/example/config/boot.rb +4 -0
- data/example/config/credentials.yml.enc +1 -0
- data/example/config/database.yml +25 -0
- data/example/config/environment.rb +5 -0
- data/example/config/environments/development.rb +44 -0
- data/example/config/environments/production.rb +91 -0
- data/example/config/environments/test.rb +38 -0
- data/example/config/initializers/application_controller_renderer.rb +8 -0
- data/example/config/initializers/backtrace_silencers.rb +7 -0
- data/example/config/initializers/cors.rb +16 -0
- data/example/config/initializers/filter_parameter_logging.rb +4 -0
- data/example/config/initializers/inflections.rb +16 -0
- data/example/config/initializers/mime_types.rb +4 -0
- data/example/config/initializers/wrap_parameters.rb +14 -0
- data/example/config/locales/en.yml +33 -0
- data/example/config/puma.rb +38 -0
- data/example/config/routes.rb +6 -0
- data/example/config/spring.rb +6 -0
- data/example/config.ru +5 -0
- data/example/db/migrate/20200311152021_create_people.rb +12 -0
- data/example/db/schema.rb +23 -0
- data/example/db/seeds.rb +7 -0
- data/example/lib/tasks/.keep +0 -0
- data/example/log/.keep +0 -0
- data/example/person.json +4 -0
- data/example/public/robots.txt +1 -0
- data/example/test/controllers/.keep +0 -0
- data/example/test/fixtures/.keep +0 -0
- data/example/test/fixtures/files/.keep +0 -0
- data/example/test/fixtures/people.yml +11 -0
- data/example/test/integration/.keep +0 -0
- data/example/test/models/.keep +0 -0
- data/example/test/models/person_test.rb +7 -0
- data/example/test/test_helper.rb +13 -0
- data/example/tmp/.keep +0 -0
- data/example/vendor/.keep +0 -0
- data/lib/sober_swag/blueprint/field.rb +36 -0
- data/lib/sober_swag/blueprint/field_syntax.rb +17 -0
- data/lib/sober_swag/blueprint/view.rb +44 -0
- data/lib/sober_swag/blueprint.rb +113 -0
- data/lib/sober_swag/compiler/error.rb +5 -0
- data/lib/sober_swag/compiler/path.rb +80 -0
- data/lib/sober_swag/compiler/paths.rb +54 -0
- data/lib/sober_swag/compiler/type.rb +235 -0
- data/lib/sober_swag/compiler.rb +107 -0
- data/lib/sober_swag/controller/route.rb +136 -0
- data/lib/sober_swag/controller/undefined_body_error.rb +6 -0
- data/lib/sober_swag/controller/undefined_path_error.rb +6 -0
- data/lib/sober_swag/controller/undefined_query_error.rb +6 -0
- data/lib/sober_swag/controller.rb +157 -0
- data/lib/sober_swag/nodes/array.rb +30 -0
- data/lib/sober_swag/nodes/attribute.rb +31 -0
- data/lib/sober_swag/nodes/base.rb +51 -0
- data/lib/sober_swag/nodes/binary.rb +44 -0
- data/lib/sober_swag/nodes/enum.rb +28 -0
- data/lib/sober_swag/nodes/list.rb +40 -0
- data/lib/sober_swag/nodes/nullable_primitive.rb +6 -0
- data/lib/sober_swag/nodes/object.rb +12 -0
- data/lib/sober_swag/nodes/one_of.rb +12 -0
- data/lib/sober_swag/nodes/primitive.rb +29 -0
- data/lib/sober_swag/nodes/sum.rb +6 -0
- data/lib/sober_swag/nodes.rb +20 -0
- data/lib/sober_swag/parser.rb +73 -0
- data/lib/sober_swag/path/integer.rb +21 -0
- data/lib/sober_swag/path/lit.rb +41 -0
- data/lib/sober_swag/path/literal.rb +29 -0
- data/lib/sober_swag/path/param.rb +33 -0
- data/lib/sober_swag/path.rb +8 -0
- data/lib/sober_swag/serializer/array.rb +21 -0
- data/lib/sober_swag/serializer/base.rb +38 -0
- data/lib/sober_swag/serializer/conditional.rb +49 -0
- data/lib/sober_swag/serializer/field_list.rb +44 -0
- data/lib/sober_swag/serializer/mapped.rb +29 -0
- data/lib/sober_swag/serializer/optional.rb +29 -0
- data/lib/sober_swag/serializer/primitive.rb +15 -0
- data/lib/sober_swag/serializer.rb +23 -0
- data/lib/sober_swag/types.rb +5 -0
- data/lib/sober_swag/version.rb +5 -0
- data/lib/sober_swag.rb +29 -0
- data/sober_swag.gemspec +40 -0
- metadata +269 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module SoberSwag
|
|
2
|
+
module Nodes
|
|
3
|
+
##
|
|
4
|
+
# Root node of the tree
|
|
5
|
+
class Primitive < Base
|
|
6
|
+
def initialize(value)
|
|
7
|
+
@value = value
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
attr_reader :value
|
|
11
|
+
|
|
12
|
+
def map(&block)
|
|
13
|
+
self.class.new(block.call(value))
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def deconstruct
|
|
17
|
+
[value]
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def deconstruct_keys(_)
|
|
21
|
+
{ value: value }
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def cata(&block)
|
|
25
|
+
block.call(self.class.new(value))
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module SoberSwag
|
|
2
|
+
##
|
|
3
|
+
# Base namespace for all nodes.
|
|
4
|
+
# These nodes are compiled into an actual swagger definition
|
|
5
|
+
# via a catamorphism, which I promise is not nearly as scary as it sounds.
|
|
6
|
+
# Sort of.
|
|
7
|
+
module Nodes
|
|
8
|
+
autoload :Base, 'sober_swag/nodes/base'
|
|
9
|
+
autoload :Enum, 'sober_swag/nodes/enum'
|
|
10
|
+
autoload :Binary, 'sober_swag/nodes/binary'
|
|
11
|
+
autoload :Primitive, 'sober_swag/nodes/primitive'
|
|
12
|
+
autoload :NullablePrimitive, 'sober_swag/nodes/nullable_primitive'
|
|
13
|
+
autoload :Sum, 'sober_swag/nodes/sum'
|
|
14
|
+
autoload :Array, 'sober_swag/nodes/array'
|
|
15
|
+
autoload :Object, 'sober_swag/nodes/object'
|
|
16
|
+
autoload :Attribute, 'sober_swag/nodes/attribute'
|
|
17
|
+
autoload :OneOf, 'sober_swag/nodes/one_of'
|
|
18
|
+
autoload :List, 'sober_swag/nodes/list'
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
module SoberSwag
|
|
2
|
+
class Parser
|
|
3
|
+
def initialize(node)
|
|
4
|
+
@node = node
|
|
5
|
+
@found = Set.new
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def to_syntax
|
|
9
|
+
case @node
|
|
10
|
+
when Dry::Types::Array::Member
|
|
11
|
+
Nodes::List.new(bind(Parser.new(@node.member)))
|
|
12
|
+
when Dry::Types::Enum
|
|
13
|
+
Nodes::Enum.new(@node.values)
|
|
14
|
+
when Dry::Types::Schema
|
|
15
|
+
Nodes::Object.new(
|
|
16
|
+
@node.map { |attr| bind(Parser.new(attr)) }
|
|
17
|
+
)
|
|
18
|
+
when Dry::Types::Schema::Key
|
|
19
|
+
Nodes::Attribute.new(
|
|
20
|
+
@node.name,
|
|
21
|
+
@node.required?,
|
|
22
|
+
bind(Parser.new(@node.type))
|
|
23
|
+
)
|
|
24
|
+
when Dry::Types::Sum
|
|
25
|
+
left = bind(Parser.new(@node.left))
|
|
26
|
+
right = bind(Parser.new(@node.right))
|
|
27
|
+
# special case booleans to just return the left value
|
|
28
|
+
# this is because modeling a boolean as a sum type of
|
|
29
|
+
# TrueClass and FalseClass is kinda weird, because they're
|
|
30
|
+
# considered different types instead of different constructors,
|
|
31
|
+
# which we don't want to do
|
|
32
|
+
is_bool = [left, right].all? do |e|
|
|
33
|
+
e.respond_to?(:value) && [TrueClass, FalseClass].include?(e.value)
|
|
34
|
+
end
|
|
35
|
+
if is_bool
|
|
36
|
+
left
|
|
37
|
+
else
|
|
38
|
+
Nodes::Sum.new(left, right)
|
|
39
|
+
end
|
|
40
|
+
when Dry::Types::Constrained
|
|
41
|
+
bind(Parser.new(@node.type))
|
|
42
|
+
when Dry::Types::Nominal
|
|
43
|
+
# start off with the moral equivalent of NodeTree[String]
|
|
44
|
+
Nodes::Primitive.new(@node.primitive)
|
|
45
|
+
else
|
|
46
|
+
# Inside of this case we have a class that is some user-defined type
|
|
47
|
+
# We put it in our array of found types, and consider it a primitive
|
|
48
|
+
@found.add(@node)
|
|
49
|
+
Nodes::Primitive.new(@node)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def run_parser
|
|
54
|
+
[to_syntax, found]
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
##
|
|
58
|
+
# What other types did we find while parsing this type?
|
|
59
|
+
attr_reader :found
|
|
60
|
+
|
|
61
|
+
##
|
|
62
|
+
# Call `.to_syntax` on another node, putting any new classes it finds
|
|
63
|
+
# in the list of classes we found in the process.
|
|
64
|
+
#
|
|
65
|
+
# If you're a big Haskell nerd, then this is >>=.
|
|
66
|
+
def bind(other)
|
|
67
|
+
result = other.to_syntax
|
|
68
|
+
@found += other.found
|
|
69
|
+
result
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
module SoberSwag
|
|
2
|
+
class Path
|
|
3
|
+
class Lit
|
|
4
|
+
##
|
|
5
|
+
# Parse a literal path fragment
|
|
6
|
+
def initialize(lit)
|
|
7
|
+
@lit = lit
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
attr_reader :lit
|
|
11
|
+
|
|
12
|
+
def param?
|
|
13
|
+
false
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def param_type
|
|
17
|
+
nil
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def param_key
|
|
21
|
+
nil
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
##
|
|
25
|
+
# Constant to avoid a bunch of array allocation
|
|
26
|
+
MATCH_SUCC = [:match, nil].freeze
|
|
27
|
+
##
|
|
28
|
+
# Constant to avoid a bunch of array allocation
|
|
29
|
+
MATHC_FAIL = [:fail].freeze
|
|
30
|
+
|
|
31
|
+
def match(param)
|
|
32
|
+
if param == lit
|
|
33
|
+
MATCH_SUCC
|
|
34
|
+
else
|
|
35
|
+
MATCH_FAIL
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module SoberSwag
|
|
2
|
+
module Path
|
|
3
|
+
##
|
|
4
|
+
# One literal text fragment, basically
|
|
5
|
+
class Literal < Node
|
|
6
|
+
##
|
|
7
|
+
# Make a new text node
|
|
8
|
+
# @param text [String]
|
|
9
|
+
def initialize(text)
|
|
10
|
+
@text = text
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
attr_reader :text
|
|
14
|
+
|
|
15
|
+
##
|
|
16
|
+
# We can make a jump table out of this node!
|
|
17
|
+
def jumpable?
|
|
18
|
+
true
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
##
|
|
22
|
+
# This doesn't read a parameter type.
|
|
23
|
+
def param?
|
|
24
|
+
false
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
module SoberSwag
|
|
2
|
+
class Path
|
|
3
|
+
##
|
|
4
|
+
# Parse a parameter
|
|
5
|
+
class Param
|
|
6
|
+
def initialize(name, type)
|
|
7
|
+
@name = name
|
|
8
|
+
@type = type
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def param?
|
|
12
|
+
true
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def param_key
|
|
16
|
+
@name
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def param_type
|
|
20
|
+
@type
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def match(param)
|
|
24
|
+
if (m = @type.try(param)).success?
|
|
25
|
+
[:match, m]
|
|
26
|
+
else
|
|
27
|
+
[:fail]
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module SoberSwag
|
|
2
|
+
module Serializer
|
|
3
|
+
##
|
|
4
|
+
# Make a serialize of arrays out of a serializer of the elements
|
|
5
|
+
class Array < Base
|
|
6
|
+
def initialize(element_serializer)
|
|
7
|
+
@element_serializer = element_serializer
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
attr_reader :element_serializer
|
|
11
|
+
|
|
12
|
+
def serialize(object, options = {})
|
|
13
|
+
object.map { |a| element_serializer.serialize(a, options) }
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def type
|
|
17
|
+
SoberSwag::Types::Array.of(element_serializer.type)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
module SoberSwag
|
|
2
|
+
module Serializer
|
|
3
|
+
class Base
|
|
4
|
+
|
|
5
|
+
##
|
|
6
|
+
# Return a new serializer that is an *array* of elements of this serializer.
|
|
7
|
+
def array
|
|
8
|
+
SoberSwag::Serializer::Array.new(self)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
##
|
|
12
|
+
# Returns a serializer that will pass `nil` values on unscathed
|
|
13
|
+
def optional
|
|
14
|
+
SoberSwag::Serializer::Optional.new(self)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
##
|
|
18
|
+
# If I am a serializer for type 'a', and you give me a way to turn 'a's into 'b's,
|
|
19
|
+
# I can give you a serializer for type 'b' by running the funciton you gave.
|
|
20
|
+
# For example, if I am a serializer for {String}, and you know how to turn
|
|
21
|
+
# an {Int} into a {String}, I can now serialize {Int}s (by turning them into a string).
|
|
22
|
+
#
|
|
23
|
+
# Note that the *declared* type of this is *not* changed: from a user's perspective,
|
|
24
|
+
# they see a "string"
|
|
25
|
+
def via_map(&block)
|
|
26
|
+
SoberSwag::Serializer::Mapped.new(self, block)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
##
|
|
30
|
+
# Serializer lets you get a serializer from things that might be classes
|
|
31
|
+
# because of the blueprint naming hack.
|
|
32
|
+
def serializer
|
|
33
|
+
self
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
module SoberSwag
|
|
2
|
+
module Serializer
|
|
3
|
+
##
|
|
4
|
+
# Conditionally serialize one thing *or* the other thing via deciding on a condition.
|
|
5
|
+
# This works by taking three elements: a "decision" proc, a "left" serializer, and a "right" serializer.
|
|
6
|
+
# The decision proc takes in both the object to be serialized *and* the options hash, and returns a
|
|
7
|
+
# `[:left, val]` object, or a `[:right, val]` object, which
|
|
8
|
+
# then get passed on to the appropriate serializer.
|
|
9
|
+
#
|
|
10
|
+
# This is a very weird, not-very-Ruby-like abstraction, *upon which* we can build abstractions that are actually use for users.
|
|
11
|
+
# It lets you build abstractions like "Use this serializer if a type has this class, otherwise use this other one."
|
|
12
|
+
# When composed together, you can make arbitrary decision trees.
|
|
13
|
+
class Conditional < Base
|
|
14
|
+
##
|
|
15
|
+
# Error thrown when a chooser proc returns a non left-or-right value.
|
|
16
|
+
class BadChoiceError < Error; end
|
|
17
|
+
|
|
18
|
+
def initialize(chooser, left, right)
|
|
19
|
+
@chooser = chooser
|
|
20
|
+
@left = left
|
|
21
|
+
@right = right
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
attr_reader :chooser, :left, :right
|
|
25
|
+
|
|
26
|
+
def serialize(object, options = {})
|
|
27
|
+
tag, val = chooser.call(object, options)
|
|
28
|
+
if tag == :left
|
|
29
|
+
left.serialize(val, options)
|
|
30
|
+
elsif tag == :right
|
|
31
|
+
right.serialize(val, options)
|
|
32
|
+
else
|
|
33
|
+
raise BadChoiceError, "result of chooser proc was not a left or right, but a #{val.class}"
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
##
|
|
38
|
+
# Since this could potentially serialize one of two alternatives,
|
|
39
|
+
# the "type" we serialize two is *either* one alternative or the other.
|
|
40
|
+
def type
|
|
41
|
+
if left.type == right.type
|
|
42
|
+
left.type
|
|
43
|
+
else
|
|
44
|
+
left.type | right.type
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
module SoberSwag
|
|
2
|
+
module Serializer
|
|
3
|
+
##
|
|
4
|
+
# Extract out a hash from a list of
|
|
5
|
+
# name/serializer pairs.
|
|
6
|
+
class FieldList < Base
|
|
7
|
+
|
|
8
|
+
def initialize(field_list)
|
|
9
|
+
@field_list = field_list
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
attr_reader :field_list
|
|
13
|
+
|
|
14
|
+
##
|
|
15
|
+
# Alias to make writing primitive stuff much easier
|
|
16
|
+
def primitive(symbol)
|
|
17
|
+
SoberSwag::Serializer.Primitive(SoberSwag::Types.const_get(symbol))
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def serialize(object, options)
|
|
22
|
+
field_list.map { |field|
|
|
23
|
+
[field.name, field.serializer.serialize(object, options)]
|
|
24
|
+
}.to_h
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def type
|
|
28
|
+
@type ||= make_struct_type!
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
def make_struct_type!
|
|
34
|
+
f = field_list
|
|
35
|
+
Class.new(Dry::Struct) do
|
|
36
|
+
f.each do |field|
|
|
37
|
+
attribute field.name, field.serializer.type
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module SoberSwag
|
|
2
|
+
module Serializer
|
|
3
|
+
##
|
|
4
|
+
# A new serializer by mapping over the serialization function
|
|
5
|
+
class Mapped < Base
|
|
6
|
+
|
|
7
|
+
def initialize(base, map_f)
|
|
8
|
+
@base = base
|
|
9
|
+
@map_f = map_f
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def serialize(object, options)
|
|
13
|
+
@base.serialize(@map_f.call(object), options)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def type
|
|
17
|
+
@base.type
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
##
|
|
21
|
+
# I have no freaking clue if ruby optimizes proc composition,
|
|
22
|
+
# but we at least save some node traversals here
|
|
23
|
+
def via_map(&block)
|
|
24
|
+
SoberSwag::Serializer::Mapped.new(@base, @map_f >> block)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module SoberSwag
|
|
2
|
+
module Serializer
|
|
3
|
+
class Optional < Base
|
|
4
|
+
|
|
5
|
+
def initialize(inner)
|
|
6
|
+
@inner = inner
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
attr_reader :inner
|
|
10
|
+
|
|
11
|
+
def serialize(object, options = {})
|
|
12
|
+
if object.nil?
|
|
13
|
+
object
|
|
14
|
+
else
|
|
15
|
+
inner.serialize(object, options)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def type
|
|
20
|
+
inner.type.optional
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def optional(*)
|
|
24
|
+
raise ArgumentError, 'no nesting optionals please'
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module SoberSwag
|
|
2
|
+
module Serializer
|
|
3
|
+
autoload(:Base, 'sober_swag/serializer/base')
|
|
4
|
+
autoload(:Primitive, 'sober_swag/serializer/primitive')
|
|
5
|
+
autoload(:Conditional, 'sober_swag/serializer/conditional')
|
|
6
|
+
autoload(:Array, 'sober_swag/serializer/array')
|
|
7
|
+
autoload(:Mapped, 'sober_swag/serializer/mapped')
|
|
8
|
+
autoload(:Optional, 'sober_swag/serializer/optional')
|
|
9
|
+
autoload(:FieldList, 'sober_swag/serializer/field_list')
|
|
10
|
+
|
|
11
|
+
class << self
|
|
12
|
+
##
|
|
13
|
+
# Use a "Primitive" serializer, which *does not* actually do any type-changing, and instead passes
|
|
14
|
+
# in values raw.
|
|
15
|
+
#
|
|
16
|
+
# @param contained {Class} Dry::Type to use
|
|
17
|
+
def Primitive(contained)
|
|
18
|
+
SoberSwag::Serializer::Primitive.new(contained)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
end
|
|
23
|
+
end
|
data/lib/sober_swag.rb
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'bundler'
|
|
4
|
+
Bundler.setup
|
|
5
|
+
require 'dry-struct'
|
|
6
|
+
require 'dry-types'
|
|
7
|
+
require 'sober_swag/types'
|
|
8
|
+
require 'sober_swag/version'
|
|
9
|
+
require 'active_support/inflector'
|
|
10
|
+
|
|
11
|
+
##
|
|
12
|
+
# Root namespace
|
|
13
|
+
module SoberSwag
|
|
14
|
+
class Error < StandardError; end
|
|
15
|
+
|
|
16
|
+
autoload :Parser, 'sober_swag/parser'
|
|
17
|
+
autoload :Serializer, 'sober_swag/serializer'
|
|
18
|
+
autoload :Blueprint, 'sober_swag/blueprint'
|
|
19
|
+
autoload :Nodes, 'sober_swag/nodes'
|
|
20
|
+
autoload :Compiler, 'sober_swag/compiler'
|
|
21
|
+
autoload :Controller, 'sober_swag/controller'
|
|
22
|
+
|
|
23
|
+
##
|
|
24
|
+
# Define a struct of something.
|
|
25
|
+
# Useful to prevent weirdness from autoloading.
|
|
26
|
+
def self.struct(parent = Dry::Struct, &block)
|
|
27
|
+
Class.new(parent, &block)
|
|
28
|
+
end
|
|
29
|
+
end
|
data/sober_swag.gemspec
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
lib = File.expand_path('lib', __dir__)
|
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
5
|
+
require 'sober_swag/version'
|
|
6
|
+
|
|
7
|
+
Gem::Specification.new do |spec|
|
|
8
|
+
spec.name = 'sober_swag'
|
|
9
|
+
spec.version = SoberSwag::VERSION
|
|
10
|
+
spec.authors = ['Anthony Super']
|
|
11
|
+
spec.email = ['asuper@sondermind.com']
|
|
12
|
+
|
|
13
|
+
spec.summary = 'Generate swagger types from dry-types'
|
|
14
|
+
spec.description = 'Parse data, don\'t write docs'
|
|
15
|
+
spec.homepage = 'https://github.com/SonderMindOrg/sober_swag'
|
|
16
|
+
spec.license = 'MIT'
|
|
17
|
+
|
|
18
|
+
spec.metadata['allowed_push_host'] = "https://rubygems.org"
|
|
19
|
+
|
|
20
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
|
21
|
+
|
|
22
|
+
# Specify which files should be added to the gem when it is released.
|
|
23
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
|
24
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
|
25
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
|
26
|
+
end
|
|
27
|
+
spec.bindir = 'exe'
|
|
28
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
29
|
+
spec.require_paths = ['lib']
|
|
30
|
+
|
|
31
|
+
spec.add_dependency 'dry-struct', '~> 1.0'
|
|
32
|
+
spec.add_dependency 'dry-types', '~> 1.2'
|
|
33
|
+
spec.add_dependency 'activesupport'
|
|
34
|
+
|
|
35
|
+
spec.add_development_dependency 'pry'
|
|
36
|
+
spec.add_development_dependency 'simplecov'
|
|
37
|
+
spec.add_development_dependency 'bundler', '~> 2.0'
|
|
38
|
+
spec.add_development_dependency 'rake', '~> 13.0'
|
|
39
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
|
40
|
+
end
|