bluepine 0.1.1
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/lib/bluepine.rb +35 -0
- data/lib/bluepine/assertions.rb +80 -0
- data/lib/bluepine/attributes.rb +92 -0
- data/lib/bluepine/attributes/array_attribute.rb +11 -0
- data/lib/bluepine/attributes/attribute.rb +130 -0
- data/lib/bluepine/attributes/boolean_attribute.rb +22 -0
- data/lib/bluepine/attributes/currency_attribute.rb +19 -0
- data/lib/bluepine/attributes/date_attribute.rb +11 -0
- data/lib/bluepine/attributes/float_attribute.rb +13 -0
- data/lib/bluepine/attributes/integer_attribute.rb +14 -0
- data/lib/bluepine/attributes/ip_address_attribute.rb +15 -0
- data/lib/bluepine/attributes/number_attribute.rb +24 -0
- data/lib/bluepine/attributes/object_attribute.rb +71 -0
- data/lib/bluepine/attributes/schema_attribute.rb +23 -0
- data/lib/bluepine/attributes/string_attribute.rb +36 -0
- data/lib/bluepine/attributes/time_attribute.rb +19 -0
- data/lib/bluepine/attributes/uri_attribute.rb +19 -0
- data/lib/bluepine/attributes/visitor.rb +136 -0
- data/lib/bluepine/endpoint.rb +102 -0
- data/lib/bluepine/endpoints/method.rb +90 -0
- data/lib/bluepine/endpoints/params.rb +115 -0
- data/lib/bluepine/error.rb +17 -0
- data/lib/bluepine/functions.rb +49 -0
- data/lib/bluepine/generators.rb +3 -0
- data/lib/bluepine/generators/generator.rb +16 -0
- data/lib/bluepine/generators/grpc/generator.rb +10 -0
- data/lib/bluepine/generators/open_api/generator.rb +205 -0
- data/lib/bluepine/generators/open_api/property_generator.rb +111 -0
- data/lib/bluepine/registry.rb +75 -0
- data/lib/bluepine/resolvable.rb +11 -0
- data/lib/bluepine/resolver.rb +99 -0
- data/lib/bluepine/serializer.rb +125 -0
- data/lib/bluepine/serializers/serializable.rb +25 -0
- data/lib/bluepine/validator.rb +205 -0
- data/lib/bluepine/validators/normalizable.rb +25 -0
- data/lib/bluepine/validators/proxy.rb +77 -0
- data/lib/bluepine/validators/validatable.rb +48 -0
- data/lib/bluepine/version.rb +3 -0
- metadata +208 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 9decc5e24159b2da22bb444768643364dc66c402856eed434d1f44cbb1fa807f
|
4
|
+
data.tar.gz: 6309d91631be3713316a21b5aea15b36738ac2afef45658bfa8a2853265bd72a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 2738f638bef59a74fb2e3f917997fb3a99a08210e718fe96d1355be228f323af51a11553575986318aadb97c927a9296f487a7a967a739b8ac0800204b364d3a
|
7
|
+
data.tar.gz: 66fa03e1670e1cd513bae15fb6f3d1b2463f151616dfc0d238ef266ddc0b77ec361a7c3644c5d91a6d8b39c2835f82dafdde2b604550d2bc366c5e5239635729
|
data/lib/bluepine.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'active_support/concern'
|
2
|
+
require 'active_model'
|
3
|
+
|
4
|
+
require "bluepine/version"
|
5
|
+
require "bluepine/functions"
|
6
|
+
require "bluepine/assertions"
|
7
|
+
require "bluepine/error"
|
8
|
+
|
9
|
+
require "bluepine/registry"
|
10
|
+
require "bluepine/resolvable"
|
11
|
+
|
12
|
+
# attributes
|
13
|
+
require "bluepine/attributes"
|
14
|
+
|
15
|
+
# endpoint
|
16
|
+
require "bluepine/endpoint"
|
17
|
+
|
18
|
+
# serializer
|
19
|
+
require "bluepine/serializer"
|
20
|
+
|
21
|
+
# generators
|
22
|
+
require "bluepine/generators"
|
23
|
+
|
24
|
+
# validators
|
25
|
+
require "bluepine/validator"
|
26
|
+
|
27
|
+
# resolver
|
28
|
+
require "bluepine/resolver"
|
29
|
+
|
30
|
+
module Bluepine
|
31
|
+
module API
|
32
|
+
module Spec
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module Bluepine
|
2
|
+
# Declarative way to deal with errors
|
3
|
+
module Assertions
|
4
|
+
Error = Class.new(StandardError)
|
5
|
+
KeyError = Class.new(Error)
|
6
|
+
SubsetError = Class.new(Error)
|
7
|
+
|
8
|
+
extend self
|
9
|
+
def self.included(object)
|
10
|
+
object.extend(self)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Usage
|
14
|
+
#
|
15
|
+
# Use default error message
|
16
|
+
#
|
17
|
+
# assert valid?
|
18
|
+
#
|
19
|
+
# Use custom error message
|
20
|
+
#
|
21
|
+
# assert valid?, "Invalid value"
|
22
|
+
#
|
23
|
+
# Use custom Error class
|
24
|
+
#
|
25
|
+
# assert valid?, ValidationError, "Invalid value"
|
26
|
+
def assert(object, *msgs)
|
27
|
+
raises Error, msgs << "#{object.class} is not a truthy" unless object
|
28
|
+
end
|
29
|
+
|
30
|
+
def assert_not(object, *msgs)
|
31
|
+
raises Error, msgs << "#{object.class} is not a falsey" if object
|
32
|
+
end
|
33
|
+
|
34
|
+
def assert_kind_of(classes, object, *msgs)
|
35
|
+
classes = normalize_array(classes)
|
36
|
+
found = classes.find { |klass| object.kind_of?(klass) }
|
37
|
+
|
38
|
+
raises Error, msgs << "#{object.class} must be an instance of #{classes.map(&:name).join(', ')}" unless found
|
39
|
+
end
|
40
|
+
|
41
|
+
alias_method :assert_kind_of_either, :assert_kind_of
|
42
|
+
|
43
|
+
# Usage
|
44
|
+
#
|
45
|
+
# assert_in ["john", "joe"], "joe"
|
46
|
+
# assert_in { amount: 1 }, :amount
|
47
|
+
def assert_in(list, value, *msgs)
|
48
|
+
raises KeyError, msgs << "#{value.class} - #{value} is not in the #{list.keys}" if list.respond_to?(:key?) && !list.key?(value)
|
49
|
+
raises KeyError, msgs << "#{value.class} - #{value} is not in the #{list}" if list.kind_of?(Array) && !list.include?(value)
|
50
|
+
end
|
51
|
+
|
52
|
+
def assert_subset_of(parent, subset, *msgs)
|
53
|
+
rest = subset - parent
|
54
|
+
raises SubsetError, msgs << "#{rest} are not subset of #{parent}" if rest.present?
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def normalize_array(values)
|
60
|
+
values.respond_to?(:each) ? values : [values]
|
61
|
+
end
|
62
|
+
|
63
|
+
# allow caller to pass custom Error
|
64
|
+
def raises(error, msgs = [])
|
65
|
+
error, msg = msgs unless msgs.first.kind_of?(String)
|
66
|
+
|
67
|
+
# Error class has its own error message and caller
|
68
|
+
# doesn't specify custom message
|
69
|
+
if msgs.length == 2
|
70
|
+
if error.respond_to?(:message)
|
71
|
+
msg = error.message
|
72
|
+
elsif error.respond_to?(:new)
|
73
|
+
msg = error.new&.message
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
raise error, msg || msgs.last
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require_relative "registry"
|
2
|
+
require_relative "attributes/visitor"
|
3
|
+
require_relative "serializers/serializable"
|
4
|
+
require_relative "validators/normalizable"
|
5
|
+
require_relative "validators/validatable"
|
6
|
+
|
7
|
+
# Attributes
|
8
|
+
require_relative "attributes/boolean_attribute"
|
9
|
+
require_relative "attributes/string_attribute"
|
10
|
+
require_relative "attributes/number_attribute"
|
11
|
+
require_relative "attributes/integer_attribute"
|
12
|
+
require_relative "attributes/float_attribute"
|
13
|
+
require_relative "attributes/array_attribute"
|
14
|
+
require_relative "attributes/object_attribute"
|
15
|
+
require_relative "attributes/schema_attribute"
|
16
|
+
require_relative "attributes/date_attribute"
|
17
|
+
require_relative "attributes/time_attribute"
|
18
|
+
require_relative "attributes/currency_attribute"
|
19
|
+
require_relative "attributes/uri_attribute"
|
20
|
+
require_relative "attributes/ip_address_attribute"
|
21
|
+
|
22
|
+
module Bluepine
|
23
|
+
# Attributes registry holds the references to all attributes
|
24
|
+
#
|
25
|
+
# @see .create
|
26
|
+
module Attributes
|
27
|
+
include Bluepine::Assertions
|
28
|
+
KeyError = Bluepine::Error.create "Attribute %s already exists"
|
29
|
+
|
30
|
+
@registry = Registry.new({}, error: KeyError) do |id, name, options, block|
|
31
|
+
attribute = get(id)
|
32
|
+
attribute.new(name, options, &block)
|
33
|
+
end
|
34
|
+
|
35
|
+
class << self
|
36
|
+
# Holds reference to all attribute objects
|
37
|
+
#
|
38
|
+
# @return [Registry]
|
39
|
+
attr_accessor :registry
|
40
|
+
|
41
|
+
# Creates new attribute (Delegates to Registry#create).
|
42
|
+
#
|
43
|
+
# @return [Attribute]
|
44
|
+
#
|
45
|
+
# @example Creates primitive attribute
|
46
|
+
# Attributes.create(:string, :username, required: true)
|
47
|
+
#
|
48
|
+
# @example Creates compound attribute
|
49
|
+
# Attributes.create(:object, :user) do
|
50
|
+
# string :username
|
51
|
+
# end
|
52
|
+
def create(type, name, options = {}, &block)
|
53
|
+
registry.create(type, name, options, &block)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Registers new Attribute (alias for Registry#register)
|
57
|
+
#
|
58
|
+
# @example
|
59
|
+
# register(:custom, CustomAttribute)
|
60
|
+
def register(type, klass, override: false)
|
61
|
+
registry.register(type, klass, override: override)
|
62
|
+
end
|
63
|
+
|
64
|
+
def key?(key)
|
65
|
+
registry.key?(key)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
ALL = {
|
70
|
+
string: StringAttribute,
|
71
|
+
number: NumberAttribute,
|
72
|
+
integer: IntegerAttribute,
|
73
|
+
float: FloatAttribute,
|
74
|
+
boolean: BooleanAttribute,
|
75
|
+
object: ObjectAttribute,
|
76
|
+
array: ArrayAttribute,
|
77
|
+
schema: SchemaAttribute,
|
78
|
+
time: TimeAttribute,
|
79
|
+
date: DateAttribute,
|
80
|
+
uri: URIAttribute,
|
81
|
+
currency: CurrencyAttribute,
|
82
|
+
ip_address: IPAddressAttribute,
|
83
|
+
}.freeze
|
84
|
+
|
85
|
+
SCALAR_TYPES = %i[string number integer float boolean].freeze
|
86
|
+
NATIVE_TYPES = SCALAR_TYPES + %i[array object].freeze
|
87
|
+
NON_SCALAR_TYPES = ALL.keys - SCALAR_TYPES
|
88
|
+
|
89
|
+
# register pre-defined attributes
|
90
|
+
ALL.each { |name, attr| register(name, attr) }
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
module Bluepine
|
2
|
+
module Attributes
|
3
|
+
# An abstract Attribute based class.
|
4
|
+
#
|
5
|
+
# @abstract
|
6
|
+
class Attribute
|
7
|
+
include Bluepine::Assertions
|
8
|
+
include Bluepine::Serializers::Serializable
|
9
|
+
include Bluepine::Validators::Normalizable
|
10
|
+
include Bluepine::Validators::Validatable
|
11
|
+
|
12
|
+
class_attribute :options
|
13
|
+
attr_reader :name
|
14
|
+
|
15
|
+
# Assigns default class attribute values
|
16
|
+
self.options = {}.freeze
|
17
|
+
|
18
|
+
def initialize(name, options = {})
|
19
|
+
@name = name
|
20
|
+
@options = self.class.options.merge(options)
|
21
|
+
end
|
22
|
+
|
23
|
+
def options
|
24
|
+
@options.merge({
|
25
|
+
name: @name,
|
26
|
+
match: match,
|
27
|
+
method: method,
|
28
|
+
type: type,
|
29
|
+
native_type: native_type,
|
30
|
+
of: of,
|
31
|
+
in: send(:in),
|
32
|
+
if: @options[:if],
|
33
|
+
unless: @options[:unless],
|
34
|
+
null: null,
|
35
|
+
spec: spec,
|
36
|
+
spec_uri: spec_uri,
|
37
|
+
format: format,
|
38
|
+
private: private,
|
39
|
+
deprecated: deprecated,
|
40
|
+
required: required,
|
41
|
+
default: default,
|
42
|
+
description: description,
|
43
|
+
attributes: attributes.values&.map(&:options),
|
44
|
+
})
|
45
|
+
end
|
46
|
+
|
47
|
+
def type
|
48
|
+
self.class.name.demodulize.chomp("Attribute").underscore
|
49
|
+
end
|
50
|
+
|
51
|
+
def match
|
52
|
+
@options[:match]
|
53
|
+
end
|
54
|
+
|
55
|
+
def method
|
56
|
+
@options[:method] || @name
|
57
|
+
end
|
58
|
+
|
59
|
+
def of
|
60
|
+
@options[:of]
|
61
|
+
end
|
62
|
+
|
63
|
+
def in
|
64
|
+
@options[:in]
|
65
|
+
end
|
66
|
+
|
67
|
+
def if
|
68
|
+
@options[:if]
|
69
|
+
end
|
70
|
+
|
71
|
+
def unless
|
72
|
+
@options[:unless]
|
73
|
+
end
|
74
|
+
|
75
|
+
def null
|
76
|
+
@options.fetch(:null, false)
|
77
|
+
end
|
78
|
+
|
79
|
+
def native_type
|
80
|
+
type
|
81
|
+
end
|
82
|
+
|
83
|
+
def format
|
84
|
+
@options[:format]
|
85
|
+
end
|
86
|
+
|
87
|
+
def spec
|
88
|
+
nil
|
89
|
+
end
|
90
|
+
|
91
|
+
def spec_uri
|
92
|
+
nil
|
93
|
+
end
|
94
|
+
|
95
|
+
def attributes
|
96
|
+
{}
|
97
|
+
end
|
98
|
+
|
99
|
+
# deprecated attribute should be listed in schema
|
100
|
+
def deprecated
|
101
|
+
@options.fetch(:deprecated, false)
|
102
|
+
end
|
103
|
+
|
104
|
+
def private
|
105
|
+
@options.fetch(:private, false)
|
106
|
+
end
|
107
|
+
|
108
|
+
def required
|
109
|
+
@options.fetch(:required, false)
|
110
|
+
end
|
111
|
+
|
112
|
+
def default
|
113
|
+
@options[:default]
|
114
|
+
end
|
115
|
+
|
116
|
+
def description
|
117
|
+
@options[:description]
|
118
|
+
end
|
119
|
+
|
120
|
+
# Should not be listed in schema or serialize this attribute
|
121
|
+
def serializable?
|
122
|
+
!private
|
123
|
+
end
|
124
|
+
|
125
|
+
def value(value)
|
126
|
+
value.nil? ? default : value
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require "bluepine/attributes/attribute"
|
2
|
+
|
3
|
+
module Bluepine
|
4
|
+
module Attributes
|
5
|
+
# @example Registers custom normalizer that accepts "on" as truth value
|
6
|
+
# BooleanAttribute.normalizer = ->(x) { ["on", true].include?(x) ? true : false }
|
7
|
+
#
|
8
|
+
# @example Registers custom serializer
|
9
|
+
# BooleanAttribute.serialize = ->(x) { x == "on" ? true : false }
|
10
|
+
class BooleanAttribute < Attribute
|
11
|
+
self.serializer = ->(v) { ActiveModel::Type::Boolean.new.cast(v) }
|
12
|
+
|
13
|
+
def native_type
|
14
|
+
"boolean"
|
15
|
+
end
|
16
|
+
|
17
|
+
def in
|
18
|
+
@options.fetch(:in, [true, false])
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require "bluepine/attributes/attribute"
|
2
|
+
|
3
|
+
module Bluepine
|
4
|
+
module Attributes
|
5
|
+
class CurrencyAttribute < StringAttribute
|
6
|
+
def format
|
7
|
+
super || type
|
8
|
+
end
|
9
|
+
|
10
|
+
def spec
|
11
|
+
"ISO 4217"
|
12
|
+
end
|
13
|
+
|
14
|
+
def spec_uri
|
15
|
+
"https://en.wikipedia.org/wiki/ISO_4217"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require "bluepine/attributes/attribute"
|
2
|
+
|
3
|
+
module Bluepine
|
4
|
+
module Attributes
|
5
|
+
class IntegerAttribute < NumberAttribute
|
6
|
+
self.serializer = ->(v) { v.to_i }
|
7
|
+
|
8
|
+
# JSON schema supports `integer` type
|
9
|
+
def native_type
|
10
|
+
"integer"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|