tomyum 0.1.0.a
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/README.md +1106 -0
- data/lib/tomyum/assertions.rb +80 -0
- data/lib/tomyum/attributes/array.rb +11 -0
- data/lib/tomyum/attributes/attribute.rb +130 -0
- data/lib/tomyum/attributes/boolean.rb +22 -0
- data/lib/tomyum/attributes/currency.rb +19 -0
- data/lib/tomyum/attributes/date.rb +11 -0
- data/lib/tomyum/attributes/float.rb +13 -0
- data/lib/tomyum/attributes/integer.rb +14 -0
- data/lib/tomyum/attributes/ip_address.rb +15 -0
- data/lib/tomyum/attributes/number.rb +24 -0
- data/lib/tomyum/attributes/object.rb +71 -0
- data/lib/tomyum/attributes/schema.rb +23 -0
- data/lib/tomyum/attributes/string.rb +36 -0
- data/lib/tomyum/attributes/time.rb +19 -0
- data/lib/tomyum/attributes/uri.rb +19 -0
- data/lib/tomyum/attributes/visitor.rb +136 -0
- data/lib/tomyum/attributes.rb +92 -0
- data/lib/tomyum/endpoint.rb +102 -0
- data/lib/tomyum/endpoints/method.rb +90 -0
- data/lib/tomyum/endpoints/params.rb +115 -0
- data/lib/tomyum/error.rb +17 -0
- data/lib/tomyum/functions.rb +49 -0
- data/lib/tomyum/generators/generator.rb +16 -0
- data/lib/tomyum/generators/grpc/generator.rb +10 -0
- data/lib/tomyum/generators/open_api/generator.rb +205 -0
- data/lib/tomyum/generators/open_api/property_generator.rb +111 -0
- data/lib/tomyum/generators.rb +3 -0
- data/lib/tomyum/registry.rb +75 -0
- data/lib/tomyum/resolvable.rb +11 -0
- data/lib/tomyum/resolver.rb +99 -0
- data/lib/tomyum/serializer.rb +125 -0
- data/lib/tomyum/serializers/serializable.rb +23 -0
- data/lib/tomyum/server/app.rb +33 -0
- data/lib/tomyum/server/document.rb +20 -0
- data/lib/tomyum/server/documents/redoc.rb +36 -0
- data/lib/tomyum/server/documents/swagger.rb +47 -0
- data/lib/tomyum/server/routes.rb +0 -0
- data/lib/tomyum/support.rb +13 -0
- data/lib/tomyum/validator.rb +205 -0
- data/lib/tomyum/validators/normalizable.rb +24 -0
- data/lib/tomyum/validators/proxy.rb +77 -0
- data/lib/tomyum/validators/validatable.rb +48 -0
- data/lib/tomyum/version.rb +3 -0
- data/lib/tomyum.rb +28 -0
- metadata +202 -0
@@ -0,0 +1,80 @@
|
|
1
|
+
module Tomyum
|
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,130 @@
|
|
1
|
+
module Tomyum
|
2
|
+
module Attributes
|
3
|
+
# An abstract Attribute based class.
|
4
|
+
#
|
5
|
+
# @abstract
|
6
|
+
class Attribute
|
7
|
+
include Tomyum::Assertions
|
8
|
+
include Tomyum::Serializers::Serializable
|
9
|
+
include Tomyum::Validators::Normalizable
|
10
|
+
include Tomyum::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_relative "attribute"
|
2
|
+
|
3
|
+
module Tomyum
|
4
|
+
module Attributes
|
5
|
+
# @example Registers custom normalizer that accepts "on" as truth value
|
6
|
+
# Boolean.normalizer = ->(x) { ["on", true].include?(x) ? true : false }
|
7
|
+
#
|
8
|
+
# @example Registers custom serializer
|
9
|
+
# Boolean.serialize = ->(x) { x == "on" ? true : false }
|
10
|
+
class Boolean < 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_relative "attribute"
|
2
|
+
|
3
|
+
module Tomyum
|
4
|
+
module Attributes
|
5
|
+
class Currency < String
|
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,24 @@
|
|
1
|
+
require_relative "attribute"
|
2
|
+
|
3
|
+
module Tomyum
|
4
|
+
module Attributes
|
5
|
+
class Number < Attribute
|
6
|
+
self.serializer = ->(v) { v.to_s.include?(".") ? v.to_f : v.to_i }
|
7
|
+
|
8
|
+
RULES = {
|
9
|
+
max: {
|
10
|
+
group: :numericality,
|
11
|
+
name: :less_than_or_equal,
|
12
|
+
},
|
13
|
+
min: {
|
14
|
+
group: :numericality,
|
15
|
+
name: :greater_than_or_equal,
|
16
|
+
},
|
17
|
+
}.freeze
|
18
|
+
|
19
|
+
def native_type
|
20
|
+
"number"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require_relative "attribute"
|
2
|
+
|
3
|
+
module Tomyum
|
4
|
+
module Attributes
|
5
|
+
class Object < Attribute
|
6
|
+
class_attribute :stacks
|
7
|
+
attr_reader :attributes
|
8
|
+
|
9
|
+
self.stacks = []
|
10
|
+
|
11
|
+
def initialize(name, options = {}, &block)
|
12
|
+
super
|
13
|
+
|
14
|
+
@attributes = {}
|
15
|
+
instance_exec(&block) if block_given?
|
16
|
+
end
|
17
|
+
|
18
|
+
def native_type
|
19
|
+
"object"
|
20
|
+
end
|
21
|
+
|
22
|
+
# Apply default options to all attributes
|
23
|
+
#
|
24
|
+
# group if: :deleted? { ... }
|
25
|
+
# group unless: :deleted? { ... }
|
26
|
+
# group if: ->{ @user.deleted? } { ... }
|
27
|
+
def group(options, &block)
|
28
|
+
return unless block_given?
|
29
|
+
|
30
|
+
# Use stacks to allow nested conditions
|
31
|
+
self.class.stacks << Attribute.options
|
32
|
+
Attribute.options = options
|
33
|
+
|
34
|
+
instance_exec(&block)
|
35
|
+
|
36
|
+
# restore options
|
37
|
+
Attribute.options = self.class.stacks.pop
|
38
|
+
end
|
39
|
+
|
40
|
+
# Shortcut for creating attribute (delegate call to Registry.create)
|
41
|
+
# This allows us to access newly registered attributes
|
42
|
+
#
|
43
|
+
# string :username (or array, number etc)
|
44
|
+
def method_missing(type, name = nil, options = {}, &block)
|
45
|
+
if Attributes.registry.key?(type)
|
46
|
+
@attributes[name] = Attributes.create(type, name, options, &block)
|
47
|
+
else
|
48
|
+
super
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def respond_to_missing?(method, *)
|
53
|
+
super
|
54
|
+
end
|
55
|
+
|
56
|
+
def [](name)
|
57
|
+
@attributes[name.to_sym]
|
58
|
+
end
|
59
|
+
|
60
|
+
def []=(name, attribute)
|
61
|
+
assert_kind_of Attribute, attribute
|
62
|
+
|
63
|
+
@attributes[name.to_sym] = attribute
|
64
|
+
end
|
65
|
+
|
66
|
+
def keys
|
67
|
+
@attributes.keys
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require_relative "attribute"
|
2
|
+
|
3
|
+
module Tomyum
|
4
|
+
module Attributes
|
5
|
+
# Reference to other schema and doesn't accept &block.
|
6
|
+
#
|
7
|
+
# Schema supports extra option named `expandable`
|
8
|
+
# which will either return `id` or `serialized object`
|
9
|
+
# as the result.
|
10
|
+
class Schema < Object
|
11
|
+
DEFAULT_EXPANDABLE = false
|
12
|
+
def initialize(name, options = {})
|
13
|
+
# automatically add name to :of if it's not given
|
14
|
+
options[:of] = name unless options.key?(:of)
|
15
|
+
@expandable = options.fetch(:expandable, DEFAULT_EXPANDABLE)
|
16
|
+
|
17
|
+
super
|
18
|
+
end
|
19
|
+
|
20
|
+
attr_reader :expandable
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require_relative "attribute"
|
2
|
+
|
3
|
+
module Tomyum
|
4
|
+
module Attributes
|
5
|
+
class String < Attribute
|
6
|
+
self.serializer = ->(v) { v.to_s }
|
7
|
+
|
8
|
+
RULES = {
|
9
|
+
match: {
|
10
|
+
group: :format,
|
11
|
+
name: :with,
|
12
|
+
},
|
13
|
+
min: {
|
14
|
+
group: :length,
|
15
|
+
name: :minimum,
|
16
|
+
},
|
17
|
+
max: {
|
18
|
+
group: :length,
|
19
|
+
name: :maximum,
|
20
|
+
},
|
21
|
+
range: {
|
22
|
+
group: :length,
|
23
|
+
name: :in,
|
24
|
+
},
|
25
|
+
}.freeze
|
26
|
+
|
27
|
+
def native_type
|
28
|
+
"string"
|
29
|
+
end
|
30
|
+
|
31
|
+
def in
|
32
|
+
super&.map(&:to_s)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require_relative "attribute"
|
2
|
+
|
3
|
+
module Tomyum
|
4
|
+
module Attributes
|
5
|
+
class Time < String
|
6
|
+
def format
|
7
|
+
super || "date-time"
|
8
|
+
end
|
9
|
+
|
10
|
+
def spec
|
11
|
+
"ISO 8601"
|
12
|
+
end
|
13
|
+
|
14
|
+
def spec_uri
|
15
|
+
"https://en.wikipedia.org/wiki/ISO_8601"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require_relative "attribute"
|
2
|
+
|
3
|
+
module Tomyum
|
4
|
+
module Attributes
|
5
|
+
class URI < String
|
6
|
+
def format
|
7
|
+
super || type
|
8
|
+
end
|
9
|
+
|
10
|
+
def spec
|
11
|
+
"RFC 3986"
|
12
|
+
end
|
13
|
+
|
14
|
+
def spec_uri
|
15
|
+
"https://tools.ietf.org/html/rfc3986"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
module Tomyum
|
2
|
+
module Attributes
|
3
|
+
# An abstract Visitor for traversing {Attribute}.
|
4
|
+
#
|
5
|
+
# Sub-classes must implement the methods correspond to {Attribute} classes.
|
6
|
+
#
|
7
|
+
# @example Implements +string+ visitor for {String}
|
8
|
+
# class SimpleVisitor < Tomyum::Attributes::Visitor
|
9
|
+
# def visit_string(attribute, *args)
|
10
|
+
# "Hello #{attribute.name}"
|
11
|
+
# end
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# # Usage
|
15
|
+
# username = Attributes.create(:string, :username)
|
16
|
+
# visitor = StringVisitor.new
|
17
|
+
# visitor.visit(username) # => "Hello username"
|
18
|
+
#
|
19
|
+
# @abstract
|
20
|
+
class Visitor
|
21
|
+
include Functions
|
22
|
+
|
23
|
+
MethodNotFound = Tomyum::Error.create("Cannot find method Visitor#%s")
|
24
|
+
|
25
|
+
# Traveres a visitable object and calls corresponding method based-on
|
26
|
+
# sub-classes' impementations.
|
27
|
+
#
|
28
|
+
# @example When +attribute+ is an instance of {Attribute}.
|
29
|
+
# object = Attributes.create(:object, :user) { }
|
30
|
+
# visit(object) # => visit_object
|
31
|
+
#
|
32
|
+
# @example When +attribute+ is a {Symbol}.
|
33
|
+
# visit(:user) # => visit_user or visit_schema(attr, of: :user)
|
34
|
+
#
|
35
|
+
# @param [Attribute|Symbol] The +Attribute+ object or +Symbol+
|
36
|
+
def visit(attribute, *args)
|
37
|
+
method, attribute = find_method!(attribute, *args)
|
38
|
+
|
39
|
+
send(method, attribute, *args)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Performs visitor logic when no corresponding method can be found (Catch-all).
|
43
|
+
def visit_attribute(attribute, options = {})
|
44
|
+
raise NotImplementedError
|
45
|
+
end
|
46
|
+
|
47
|
+
def visit_schema(attribute, options = {})
|
48
|
+
raise NotImplementedError
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
# Finds a vistor method.
|
54
|
+
#
|
55
|
+
# @return Array<String, Attribute> Pair of method name and +Attribute+ object
|
56
|
+
#
|
57
|
+
# It'll return a first callable method in the chains or return +nil+ when no methods can be found.
|
58
|
+
# If +attribute+ is a Symbol. It'll stop looking any further.
|
59
|
+
#
|
60
|
+
# find_method(:integer) # => `visit_integer`
|
61
|
+
#
|
62
|
+
# If it's an instance of <tt>Attribute</tt>. It'll look up in ancestors
|
63
|
+
# chain and return the first callable method.
|
64
|
+
#
|
65
|
+
# object = Attributes.create(:object, :name) { ... }
|
66
|
+
# find_method(object) => `visit_object`
|
67
|
+
def find_method(attribute, *args)
|
68
|
+
compose(
|
69
|
+
curry(:resolve_methods),
|
70
|
+
curry(:respond_to_visitor?),
|
71
|
+
curry(:normalize_symbol, attribute, args)
|
72
|
+
).(attribute)
|
73
|
+
end
|
74
|
+
|
75
|
+
def find_method!(attribute, *args)
|
76
|
+
method, attribute = find_method(attribute, *args)
|
77
|
+
|
78
|
+
raise MethodNotFound, normalize_method(attribute) unless method
|
79
|
+
|
80
|
+
[method, attribute]
|
81
|
+
end
|
82
|
+
|
83
|
+
# Returns list of method Symbols from given +Attribute+
|
84
|
+
#
|
85
|
+
# @return [Symbol] Method symbols e.g. [:string, String, Attribute]
|
86
|
+
def resolve_methods(attribute)
|
87
|
+
return [attribute] if attribute.kind_of?(Symbol)
|
88
|
+
|
89
|
+
# Finds all ancestors in hierarchy up to `Attribute`
|
90
|
+
parents = attribute.class.ancestors.map(&:to_s)
|
91
|
+
parents.slice(0, parents.index(Attribute.name).to_i + 1)
|
92
|
+
end
|
93
|
+
|
94
|
+
def normalize_method(method)
|
95
|
+
return unless method
|
96
|
+
|
97
|
+
# Cannot use `chomp("Attribute")` here, because the top most class is `Attribute`
|
98
|
+
"visit_" + method.to_s.demodulize.gsub(/(\w+)Attribute/, "\\1").underscore
|
99
|
+
end
|
100
|
+
|
101
|
+
# Creates Attribute from symbol
|
102
|
+
# e.g. :integer to Integer
|
103
|
+
def normalize_attribute(attribute, options = {})
|
104
|
+
return attribute unless Attributes.key?(attribute)
|
105
|
+
|
106
|
+
Attributes.create(attribute, attribute.to_s, options)
|
107
|
+
end
|
108
|
+
|
109
|
+
# Finds method from method list that respond_to `visit_{attribute}` call
|
110
|
+
def respond_to_visitor?(methods)
|
111
|
+
methods.find { |m| respond_to?(normalize_method(m)) }
|
112
|
+
end
|
113
|
+
|
114
|
+
def normalize_symbol(attribute, args, method)
|
115
|
+
# When we can't find method e.g. `visit_{method}`
|
116
|
+
# and `attribute` is a symbol (e.g. :user),
|
117
|
+
# we'll enforce the same logic for all visitor sub-classes
|
118
|
+
# by calling `visit_schema` with of: attribute
|
119
|
+
if !method && attribute.kind_of?(Symbol)
|
120
|
+
method, attribute = normalize_schema_symbol(attribute, *args)
|
121
|
+
end
|
122
|
+
|
123
|
+
[
|
124
|
+
normalize_method(method),
|
125
|
+
normalize_attribute(attribute, *args),
|
126
|
+
]
|
127
|
+
end
|
128
|
+
|
129
|
+
def normalize_schema_symbol(schema, *args)
|
130
|
+
args.last[:of] = schema if args.last.is_a?(Hash)
|
131
|
+
|
132
|
+
[:schema, :schema]
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|