bluepine 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|