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.
Files changed (73) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/Rakefile +1 -0
  4. data/bin/kanji +6 -0
  5. data/lib/kanji.rb +5 -0
  6. data/lib/kanji/application.rb +37 -0
  7. data/lib/kanji/cli.rb +36 -0
  8. data/lib/kanji/cli/generate.rb +13 -0
  9. data/lib/kanji/container.rb +12 -0
  10. data/lib/kanji/errors.rb +10 -0
  11. data/lib/kanji/generate.rb +48 -0
  12. data/lib/kanji/generators/abstract_generator.rb +50 -0
  13. data/lib/kanji/generators/project.rb +155 -0
  14. data/lib/kanji/generators/type.rb +110 -0
  15. data/lib/kanji/graph.rb +1 -0
  16. data/lib/kanji/graph/coerce_type.rb +66 -0
  17. data/lib/kanji/graph/container.rb +18 -0
  18. data/lib/kanji/graph/helpers.rb +27 -0
  19. data/lib/kanji/graph/import.rb +8 -0
  20. data/lib/kanji/graph/query.rb +16 -0
  21. data/lib/kanji/graph/register_mutation.rb +43 -0
  22. data/lib/kanji/graph/register_object.rb +40 -0
  23. data/lib/kanji/graph/schema.rb +17 -0
  24. data/lib/kanji/import.rb +5 -0
  25. data/lib/kanji/instance_define.rb +11 -0
  26. data/lib/kanji/logger.rb +10 -0
  27. data/lib/kanji/repository.rb +53 -0
  28. data/lib/kanji/templates/.gitignore +11 -0
  29. data/lib/kanji/templates/.keep +0 -0
  30. data/lib/kanji/templates/.rspec +2 -0
  31. data/lib/kanji/templates/Gemfile +14 -0
  32. data/lib/kanji/templates/README.md.tt +12 -0
  33. data/lib/kanji/templates/Rakefile.tt +113 -0
  34. data/lib/kanji/templates/app/mutation_type.rb.tt +14 -0
  35. data/lib/kanji/templates/app/query_type.rb.tt +13 -0
  36. data/lib/kanji/templates/app/repositories/repo.rb.tt +7 -0
  37. data/lib/kanji/templates/app/schema.rb.tt +21 -0
  38. data/lib/kanji/templates/app/types.rb.tt +5 -0
  39. data/lib/kanji/templates/app/types/type.rb.tt +26 -0
  40. data/lib/kanji/templates/app/views/graphiql.html.erb +24 -0
  41. data/lib/kanji/templates/bin/console.tt +7 -0
  42. data/lib/kanji/templates/bin/setup +7 -0
  43. data/lib/kanji/templates/config.ru.tt +2 -0
  44. data/lib/kanji/templates/db/sample_data.rb +1 -0
  45. data/lib/kanji/templates/db/seed.rb +1 -0
  46. data/lib/kanji/templates/migration.rb.tt +10 -0
  47. data/lib/kanji/templates/spec/db_spec_helper.rb.tt +23 -0
  48. data/lib/kanji/templates/spec/factories/example.rb +9 -0
  49. data/lib/kanji/templates/spec/spec_helper.rb +61 -0
  50. data/lib/kanji/templates/spec/support/db/factory.rb +8 -0
  51. data/lib/kanji/templates/spec/support/db/helpers.rb.tt +13 -0
  52. data/lib/kanji/templates/system/boot.rb.tt +13 -0
  53. data/lib/kanji/templates/system/boot/graph.rb.tt +11 -0
  54. data/lib/kanji/templates/system/boot/monitor.rb.tt +9 -0
  55. data/lib/kanji/templates/system/boot/repos.rb.tt +23 -0
  56. data/lib/kanji/templates/system/boot/rom.rb.tt +37 -0
  57. data/lib/kanji/templates/system/boot/settings.rb.tt +10 -0
  58. data/lib/kanji/templates/system/project/application.rb.tt +35 -0
  59. data/lib/kanji/templates/system/project/container.rb.tt +13 -0
  60. data/lib/kanji/templates/system/project/import.rb.tt +5 -0
  61. data/lib/kanji/templates/system/project/settings.rb.tt +8 -0
  62. data/lib/kanji/type.rb +62 -0
  63. data/lib/kanji/type/argument.rb +12 -0
  64. data/lib/kanji/type/attribute.rb +14 -0
  65. data/lib/kanji/type/attribute_definer.rb +33 -0
  66. data/lib/kanji/type/class_interface.rb +156 -0
  67. data/lib/kanji/type/mutation.rb +13 -0
  68. data/lib/kanji/type/mutation_definer.rb +43 -0
  69. data/lib/kanji/types.rb +11 -0
  70. data/lib/kanji/types/callable.rb +30 -0
  71. data/lib/kanji/types/type_interface.rb +43 -0
  72. data/lib/kanji/version.rb +3 -0
  73. metadata +538 -0
@@ -0,0 +1,5 @@
1
+ require_relative "container"
2
+
3
+ module <%= config[:camel_cased_app_name] %>
4
+ Import = <%= config[:camel_cased_app_name] %>::Container.injector
5
+ end
@@ -0,0 +1,8 @@
1
+ require "dry/web/settings"
2
+ require "kanji/types"
3
+
4
+ module <%= config[:camel_cased_app_name] %>
5
+ class Settings < Dry::Web::Settings
6
+ setting :database_url, Kanji::Types::Strict::String.constrained(filled: true)
7
+ end
8
+ end
@@ -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,12 @@
1
+ require "dry-struct"
2
+ require "kanji/types"
3
+
4
+ module Kanji
5
+ class Type
6
+ class Argument < Dry::Struct
7
+ attribute :name, Kanji::Types::String
8
+ attribute :type, Kanji::Types::Class
9
+ attribute :options, Kanji::Types::Hash
10
+ end
11
+ end
12
+ 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
@@ -0,0 +1,11 @@
1
+ require "dry-types"
2
+ require_relative "./types/callable"
3
+
4
+ module Kanji
5
+ module Types
6
+ include Dry::Types.module
7
+
8
+ ID = String | Integer
9
+ Email = String.constrained(format: /.+\@.+\..+/)
10
+ end
11
+ end
@@ -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