bluepine 0.1.1

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