kanji-web 0.1.0
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/LICENSE +21 -0
- data/Rakefile +1 -0
- data/bin/kanji +6 -0
- data/lib/kanji.rb +5 -0
- data/lib/kanji/application.rb +37 -0
- data/lib/kanji/cli.rb +36 -0
- data/lib/kanji/cli/generate.rb +13 -0
- data/lib/kanji/container.rb +12 -0
- data/lib/kanji/errors.rb +10 -0
- data/lib/kanji/generate.rb +48 -0
- data/lib/kanji/generators/abstract_generator.rb +50 -0
- data/lib/kanji/generators/project.rb +155 -0
- data/lib/kanji/generators/type.rb +110 -0
- data/lib/kanji/graph.rb +1 -0
- data/lib/kanji/graph/coerce_type.rb +66 -0
- data/lib/kanji/graph/container.rb +18 -0
- data/lib/kanji/graph/helpers.rb +27 -0
- data/lib/kanji/graph/import.rb +8 -0
- data/lib/kanji/graph/query.rb +16 -0
- data/lib/kanji/graph/register_mutation.rb +43 -0
- data/lib/kanji/graph/register_object.rb +40 -0
- data/lib/kanji/graph/schema.rb +17 -0
- data/lib/kanji/import.rb +5 -0
- data/lib/kanji/instance_define.rb +11 -0
- data/lib/kanji/logger.rb +10 -0
- data/lib/kanji/repository.rb +53 -0
- data/lib/kanji/templates/.gitignore +11 -0
- data/lib/kanji/templates/.keep +0 -0
- data/lib/kanji/templates/.rspec +2 -0
- data/lib/kanji/templates/Gemfile +14 -0
- data/lib/kanji/templates/README.md.tt +12 -0
- data/lib/kanji/templates/Rakefile.tt +113 -0
- data/lib/kanji/templates/app/mutation_type.rb.tt +14 -0
- data/lib/kanji/templates/app/query_type.rb.tt +13 -0
- data/lib/kanji/templates/app/repositories/repo.rb.tt +7 -0
- data/lib/kanji/templates/app/schema.rb.tt +21 -0
- data/lib/kanji/templates/app/types.rb.tt +5 -0
- data/lib/kanji/templates/app/types/type.rb.tt +26 -0
- data/lib/kanji/templates/app/views/graphiql.html.erb +24 -0
- data/lib/kanji/templates/bin/console.tt +7 -0
- data/lib/kanji/templates/bin/setup +7 -0
- data/lib/kanji/templates/config.ru.tt +2 -0
- data/lib/kanji/templates/db/sample_data.rb +1 -0
- data/lib/kanji/templates/db/seed.rb +1 -0
- data/lib/kanji/templates/migration.rb.tt +10 -0
- data/lib/kanji/templates/spec/db_spec_helper.rb.tt +23 -0
- data/lib/kanji/templates/spec/factories/example.rb +9 -0
- data/lib/kanji/templates/spec/spec_helper.rb +61 -0
- data/lib/kanji/templates/spec/support/db/factory.rb +8 -0
- data/lib/kanji/templates/spec/support/db/helpers.rb.tt +13 -0
- data/lib/kanji/templates/system/boot.rb.tt +13 -0
- data/lib/kanji/templates/system/boot/graph.rb.tt +11 -0
- data/lib/kanji/templates/system/boot/monitor.rb.tt +9 -0
- data/lib/kanji/templates/system/boot/repos.rb.tt +23 -0
- data/lib/kanji/templates/system/boot/rom.rb.tt +37 -0
- data/lib/kanji/templates/system/boot/settings.rb.tt +10 -0
- data/lib/kanji/templates/system/project/application.rb.tt +35 -0
- data/lib/kanji/templates/system/project/container.rb.tt +13 -0
- data/lib/kanji/templates/system/project/import.rb.tt +5 -0
- data/lib/kanji/templates/system/project/settings.rb.tt +8 -0
- data/lib/kanji/type.rb +62 -0
- data/lib/kanji/type/argument.rb +12 -0
- data/lib/kanji/type/attribute.rb +14 -0
- data/lib/kanji/type/attribute_definer.rb +33 -0
- data/lib/kanji/type/class_interface.rb +156 -0
- data/lib/kanji/type/mutation.rb +13 -0
- data/lib/kanji/type/mutation_definer.rb +43 -0
- data/lib/kanji/types.rb +11 -0
- data/lib/kanji/types/callable.rb +30 -0
- data/lib/kanji/types/type_interface.rb +43 -0
- data/lib/kanji/version.rb +3 -0
- metadata +538 -0
data/lib/kanji/type.rb
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
require "dry-container"
|
|
2
|
+
require "kanji/types"
|
|
3
|
+
require "kanji/type/class_interface"
|
|
4
|
+
require "kanji/errors"
|
|
5
|
+
|
|
6
|
+
module Kanji
|
|
7
|
+
class Type
|
|
8
|
+
extend Dry::Container::Mixin
|
|
9
|
+
extend ClassInterface
|
|
10
|
+
|
|
11
|
+
include Dry::Container::Mixin
|
|
12
|
+
|
|
13
|
+
def initialize(params)
|
|
14
|
+
result = self.class.resolve(:schema).call(params)
|
|
15
|
+
|
|
16
|
+
if result.success?
|
|
17
|
+
register :value, -> { self.class.resolve(:value_object).new(params) }
|
|
18
|
+
else
|
|
19
|
+
errors = parse_error_messages(result)
|
|
20
|
+
raise ValidationError, "Schema validation failed - #{errors}"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
self.class.instance_variable_get(:@_values).each do |key, value|
|
|
24
|
+
register key.to_sym, value
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
if repo = self.class.resolve(:repo)
|
|
28
|
+
register :repo, repo
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def to_h
|
|
33
|
+
resolve(:value).to_h
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def to_json
|
|
37
|
+
to_h.to_json
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def method_missing(method_name, *args, &block)
|
|
41
|
+
if resolve(:value).respond_to?(method_name)
|
|
42
|
+
resolve(:value).send(method_name, *args)
|
|
43
|
+
else
|
|
44
|
+
super(method_name, *args, &block)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def respond_to_missing?(method_name, include_private = false)
|
|
49
|
+
if resolve(:value).respond_to?(method_name)
|
|
50
|
+
true
|
|
51
|
+
else
|
|
52
|
+
super(method_name, include_private)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
private
|
|
57
|
+
|
|
58
|
+
def parse_error_messages(result)
|
|
59
|
+
result.messages(full: true).values.flatten.join(",")
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
require "dry-struct"
|
|
2
|
+
require "kanji/types"
|
|
3
|
+
|
|
4
|
+
module Kanji
|
|
5
|
+
class Type
|
|
6
|
+
class Attribute < Dry::Struct
|
|
7
|
+
attribute :name, Kanji::Types::String
|
|
8
|
+
attribute :type, Kanji::Types::Class
|
|
9
|
+
attribute :description, Kanji::Types::String
|
|
10
|
+
attribute :options, Kanji::Types::Hash
|
|
11
|
+
attribute :resolve, Kanji::Types::Callable.optional
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
require "kanji/type/attribute"
|
|
2
|
+
require "kanji/instance_define"
|
|
3
|
+
require "kanji/errors"
|
|
4
|
+
|
|
5
|
+
module Kanji
|
|
6
|
+
class Type
|
|
7
|
+
class AttributeDefiner
|
|
8
|
+
extend Kanji::InstanceDefine
|
|
9
|
+
|
|
10
|
+
instance_define :type, :description, :options, :resolve
|
|
11
|
+
|
|
12
|
+
def initialize(name, type = nil, description = nil, **kwargs, &block)
|
|
13
|
+
@_name = name
|
|
14
|
+
@_type = type
|
|
15
|
+
@_description = description
|
|
16
|
+
@_options = kwargs
|
|
17
|
+
self.instance_eval &block if block_given?
|
|
18
|
+
|
|
19
|
+
raise AttributeError unless @_type
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def call
|
|
23
|
+
Attribute.new({
|
|
24
|
+
name: @_name,
|
|
25
|
+
type: @_type,
|
|
26
|
+
description: @_description,
|
|
27
|
+
options: @_options,
|
|
28
|
+
resolve: @_resolve
|
|
29
|
+
})
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
require "dry-validation"
|
|
2
|
+
require "graphql"
|
|
3
|
+
require "dry/core/class_builder"
|
|
4
|
+
require "dry/core/constants"
|
|
5
|
+
require "dry/core/inflector"
|
|
6
|
+
require "kanji/type/attribute"
|
|
7
|
+
require "kanji/graph/register_object"
|
|
8
|
+
require "kanji/graph/register_mutation"
|
|
9
|
+
require "kanji/type/attribute_definer"
|
|
10
|
+
|
|
11
|
+
module Kanji
|
|
12
|
+
class Type
|
|
13
|
+
module ClassInterface
|
|
14
|
+
include Dry::Core::Constants
|
|
15
|
+
|
|
16
|
+
attr_reader :_attributes, :_name, :_description, :_repo_name,
|
|
17
|
+
:_associations
|
|
18
|
+
|
|
19
|
+
def graphql_type(klass)
|
|
20
|
+
Kanji::Graph::RegisterObject.new(
|
|
21
|
+
attributes: klass._attributes + klass._associations,
|
|
22
|
+
name: klass._name,
|
|
23
|
+
description: klass._description
|
|
24
|
+
).call
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def inherited(klass)
|
|
28
|
+
super
|
|
29
|
+
|
|
30
|
+
klass.instance_variable_set(:@_attributes, [])
|
|
31
|
+
klass.instance_variable_set(:@_values, {})
|
|
32
|
+
klass.instance_variable_set(:@_associations, [])
|
|
33
|
+
|
|
34
|
+
klass.attribute(:id, Kanji::Types::Int, "The primary key")
|
|
35
|
+
|
|
36
|
+
TracePoint.trace(:end) do |t|
|
|
37
|
+
if klass == t.self
|
|
38
|
+
self.finalize(klass)
|
|
39
|
+
t.disable
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def finalize(klass)
|
|
45
|
+
klass.register :graphql_type, graphql_type(klass)
|
|
46
|
+
klass.register :schema, -> { klass.register_schema }
|
|
47
|
+
klass.register :value_object, -> { klass.create_value_object }
|
|
48
|
+
klass.instance_variable_set(:@_repo_name, get_repo_name(klass))
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def get_repo_name(klass)
|
|
52
|
+
name = Dry::Core::Inflector.underscore(klass._name)
|
|
53
|
+
Dry::Core::Inflector.pluralize(name).to_sym
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def name(name)
|
|
57
|
+
@_name = name
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def description(description)
|
|
61
|
+
@_description = description
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def attribute(name, type = nil, description = nil, **kwargs, &block)
|
|
65
|
+
if @_attributes.map(&:name).include?(name)
|
|
66
|
+
fail AttributeError, "Attribute #{name} is already defined"
|
|
67
|
+
else
|
|
68
|
+
@_attributes <<
|
|
69
|
+
AttributeDefiner.new(name, type, description, kwargs, &block).call
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def assoc(name, type = nil, description = nil, **kwargs, &block)
|
|
74
|
+
if @_associations.map(&:name).include?(name)
|
|
75
|
+
fail AttributeError, "Association #{name} is already defined"
|
|
76
|
+
else
|
|
77
|
+
@_associations <<
|
|
78
|
+
AttributeDefiner.new(name, type, description, kwargs, &block).call
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def demodulized_type_name
|
|
83
|
+
@_demodulized_type_name ||= Dry::Core::Inflector.demodulize(self.to_s)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def create(&block)
|
|
87
|
+
register :create_mutation do
|
|
88
|
+
Kanji::Graph::RegisterMutation.new(
|
|
89
|
+
return_type: resolve(:graphql_type),
|
|
90
|
+
attributes: @_attributes.reject { |attr| attr.name == :id },
|
|
91
|
+
name: "Create#{demodulized_type_name}Mutation",
|
|
92
|
+
description: "Create a new #{demodulized_type_name}.",
|
|
93
|
+
resolve: block
|
|
94
|
+
).call
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def update(&block)
|
|
99
|
+
register :update_mutation do
|
|
100
|
+
Kanji::Graph::RegisterMutation.new(
|
|
101
|
+
return_type: resolve(:graphql_type),
|
|
102
|
+
attributes: @_attributes,
|
|
103
|
+
name: "Update#{demodulized_type_name}Mutation",
|
|
104
|
+
description: "Update an instance of #{demodulized_type_name}.",
|
|
105
|
+
resolve: block
|
|
106
|
+
).call
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def destroy(&block)
|
|
111
|
+
register :destroy_mutation do
|
|
112
|
+
Kanji::Graph::RegisterMutation.new(
|
|
113
|
+
return_type: resolve(:graphql_type),
|
|
114
|
+
attributes: [@_attributes.find { |attr| attr.name == :id }],
|
|
115
|
+
name: "Destroy#{demodulized_type_name}Mutation",
|
|
116
|
+
description: "Destroy a #{demodulized_type_name}.",
|
|
117
|
+
resolve: block
|
|
118
|
+
).call
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def register_schema
|
|
123
|
+
attributes = _attributes
|
|
124
|
+
|
|
125
|
+
Dry::Validation.JSON do
|
|
126
|
+
configure { config.type_specs = true }
|
|
127
|
+
|
|
128
|
+
attributes.each do |attribute|
|
|
129
|
+
if attribute.options[:required]
|
|
130
|
+
required(attribute.name, attribute.type).filled
|
|
131
|
+
else
|
|
132
|
+
optional(attribute.name, attribute.type).maybe
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def create_value_object
|
|
139
|
+
builder = Dry::Core::ClassBuilder.new(
|
|
140
|
+
name: "#{instance_variable_get(:@_name)}Value",
|
|
141
|
+
parent: Dry::Struct::Value
|
|
142
|
+
)
|
|
143
|
+
klass = builder.call
|
|
144
|
+
|
|
145
|
+
klass.constructor_type(:schema)
|
|
146
|
+
|
|
147
|
+
instance_variable_get(:@_attributes).each do |attribute|
|
|
148
|
+
klass.attribute(attribute.name, attribute.type)
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
klass
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
require "dry-struct"
|
|
2
|
+
require "kanji/types"
|
|
3
|
+
|
|
4
|
+
module Kanji
|
|
5
|
+
class Type
|
|
6
|
+
class Mutation < Dry::Struct::Value
|
|
7
|
+
attribute :name, Kanji::Types::String
|
|
8
|
+
attribute :return_type, Kanji::Types::Class
|
|
9
|
+
attribute :arguments, Kanji::Types::Strict::Array.member(Kanji::Type::Argument)
|
|
10
|
+
attribute :resolve, Kanji::Types::Class
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
require "kanji/instance_define"
|
|
2
|
+
require "kanji/errors"
|
|
3
|
+
require "kanji/type/mutation"
|
|
4
|
+
require "kanji/type/argument"
|
|
5
|
+
|
|
6
|
+
module Kanji
|
|
7
|
+
class Type
|
|
8
|
+
class MutationDefiner
|
|
9
|
+
extend Kanji::InstanceDefine
|
|
10
|
+
|
|
11
|
+
instance_define :name, :return_type, :resolve
|
|
12
|
+
|
|
13
|
+
def initialize(&block)
|
|
14
|
+
@_arguments = []
|
|
15
|
+
self.instance_eval &block
|
|
16
|
+
|
|
17
|
+
raise(AttributeError, "You must supply a name") unless @_name
|
|
18
|
+
raise(AttributeError, "You must supply a return type") unless @_return_type
|
|
19
|
+
raise(AttributeError, "You must supply a resolve proc") unless @_resolve
|
|
20
|
+
raise(AttributeError, "You must supply at least one argument") if @_arguments.empty?
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def argument(name, type, **kwargs)
|
|
24
|
+
raise ArgumentError if @_arguments.map(&:name).include?(name.to_s)
|
|
25
|
+
|
|
26
|
+
@_arguments << Argument.new({
|
|
27
|
+
name: name.to_s,
|
|
28
|
+
type: type,
|
|
29
|
+
options: kwargs
|
|
30
|
+
})
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def call
|
|
34
|
+
Mutation.new({
|
|
35
|
+
name: @_name,
|
|
36
|
+
return_type: @_return_type,
|
|
37
|
+
arguments: @_arguments,
|
|
38
|
+
resolve: @_resolve
|
|
39
|
+
})
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
data/lib/kanji/types.rb
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
require "dry-types"
|
|
2
|
+
require_relative "./type_interface"
|
|
3
|
+
|
|
4
|
+
module Kanji
|
|
5
|
+
module Types
|
|
6
|
+
class Callable
|
|
7
|
+
extend TypeInterface
|
|
8
|
+
|
|
9
|
+
class << self
|
|
10
|
+
def call(obj)
|
|
11
|
+
raise constraint_error(obj) unless valid?(obj)
|
|
12
|
+
obj
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def valid?(obj)
|
|
16
|
+
obj.respond_to?(:call)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def constraint_error(obj)
|
|
22
|
+
Dry::Types::ConstraintError.new(
|
|
23
|
+
"Object must respond to the call method",
|
|
24
|
+
obj
|
|
25
|
+
)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
require "dry-types"
|
|
2
|
+
|
|
3
|
+
module Kanji
|
|
4
|
+
module Types
|
|
5
|
+
module TypeInterface
|
|
6
|
+
def try(obj, &block)
|
|
7
|
+
result = valid?(obj) ? success(obj) : failure(obj, constraint_error(obj))
|
|
8
|
+
|
|
9
|
+
return result if result.success?
|
|
10
|
+
|
|
11
|
+
if block
|
|
12
|
+
yield(result)
|
|
13
|
+
else
|
|
14
|
+
result
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def success(obj)
|
|
19
|
+
Dry::Types::Result::Success.new(obj)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def failure(obj, error)
|
|
23
|
+
Dry::Types::Result::Failure.new(obj, error)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def |(other)
|
|
27
|
+
Dry::Types::Sum::Constrained.new(self, other)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def optional
|
|
31
|
+
Dry::Types::Sum.new(Kanji::Types::Nil, self)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def constrained?
|
|
35
|
+
true
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def valid?
|
|
39
|
+
raise NotImplementedError, "You must implement the valid? method"
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|