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.
Files changed (40) hide show
  1. checksums.yaml +7 -0
  2. data/lib/bluepine.rb +35 -0
  3. data/lib/bluepine/assertions.rb +80 -0
  4. data/lib/bluepine/attributes.rb +92 -0
  5. data/lib/bluepine/attributes/array_attribute.rb +11 -0
  6. data/lib/bluepine/attributes/attribute.rb +130 -0
  7. data/lib/bluepine/attributes/boolean_attribute.rb +22 -0
  8. data/lib/bluepine/attributes/currency_attribute.rb +19 -0
  9. data/lib/bluepine/attributes/date_attribute.rb +11 -0
  10. data/lib/bluepine/attributes/float_attribute.rb +13 -0
  11. data/lib/bluepine/attributes/integer_attribute.rb +14 -0
  12. data/lib/bluepine/attributes/ip_address_attribute.rb +15 -0
  13. data/lib/bluepine/attributes/number_attribute.rb +24 -0
  14. data/lib/bluepine/attributes/object_attribute.rb +71 -0
  15. data/lib/bluepine/attributes/schema_attribute.rb +23 -0
  16. data/lib/bluepine/attributes/string_attribute.rb +36 -0
  17. data/lib/bluepine/attributes/time_attribute.rb +19 -0
  18. data/lib/bluepine/attributes/uri_attribute.rb +19 -0
  19. data/lib/bluepine/attributes/visitor.rb +136 -0
  20. data/lib/bluepine/endpoint.rb +102 -0
  21. data/lib/bluepine/endpoints/method.rb +90 -0
  22. data/lib/bluepine/endpoints/params.rb +115 -0
  23. data/lib/bluepine/error.rb +17 -0
  24. data/lib/bluepine/functions.rb +49 -0
  25. data/lib/bluepine/generators.rb +3 -0
  26. data/lib/bluepine/generators/generator.rb +16 -0
  27. data/lib/bluepine/generators/grpc/generator.rb +10 -0
  28. data/lib/bluepine/generators/open_api/generator.rb +205 -0
  29. data/lib/bluepine/generators/open_api/property_generator.rb +111 -0
  30. data/lib/bluepine/registry.rb +75 -0
  31. data/lib/bluepine/resolvable.rb +11 -0
  32. data/lib/bluepine/resolver.rb +99 -0
  33. data/lib/bluepine/serializer.rb +125 -0
  34. data/lib/bluepine/serializers/serializable.rb +25 -0
  35. data/lib/bluepine/validator.rb +205 -0
  36. data/lib/bluepine/validators/normalizable.rb +25 -0
  37. data/lib/bluepine/validators/proxy.rb +77 -0
  38. data/lib/bluepine/validators/validatable.rb +48 -0
  39. data/lib/bluepine/version.rb +3 -0
  40. metadata +208 -0
@@ -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
@@ -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,11 @@
1
+ require "bluepine/attributes/attribute"
2
+
3
+ module Bluepine
4
+ module Attributes
5
+ class ArrayAttribute < Attribute
6
+ def native_type
7
+ "array"
8
+ end
9
+ end
10
+ end
11
+ 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,11 @@
1
+ require "bluepine/attributes/attribute"
2
+
3
+ module Bluepine
4
+ module Attributes
5
+ class DateAttribute < StringAttribute
6
+ def format
7
+ super || "date"
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,13 @@
1
+ require "bluepine/attributes/attribute"
2
+
3
+ module Bluepine
4
+ module Attributes
5
+ class FloatAttribute < NumberAttribute
6
+ self.serializer = ->(v) { v.to_f }
7
+
8
+ def format
9
+ super || "float"
10
+ end
11
+ end
12
+ end
13
+ 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