stardust_rails 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/MIT-LICENSE +20 -0
  3. data/README.md +140 -0
  4. data/Rakefile +32 -0
  5. data/app/controllers/stardust/graphql_controller.rb +79 -0
  6. data/config/routes.rb +7 -0
  7. data/lib/generators/example/mutations/update_item.rb +22 -0
  8. data/lib/generators/example/queries/foo.rb +31 -0
  9. data/lib/generators/example/queries/foos.rb +29 -0
  10. data/lib/generators/example/types/item.rb +9 -0
  11. data/lib/generators/mutation.template +30 -0
  12. data/lib/generators/query.template +26 -0
  13. data/lib/generators/type.template +8 -0
  14. data/lib/stardust/configuration.rb +13 -0
  15. data/lib/stardust/engine.rb +51 -0
  16. data/lib/stardust/generators/example.rb +17 -0
  17. data/lib/stardust/generators/mutation.rb +22 -0
  18. data/lib/stardust/generators/query.rb +22 -0
  19. data/lib/stardust/generators/type.rb +22 -0
  20. data/lib/stardust/generators.rb +9 -0
  21. data/lib/stardust/graphql/collector.rb +161 -0
  22. data/lib/stardust/graphql/configuration.rb +23 -0
  23. data/lib/stardust/graphql/dsl.rb +45 -0
  24. data/lib/stardust/graphql/extensions/authorize.rb +22 -0
  25. data/lib/stardust/graphql/field.rb +38 -0
  26. data/lib/stardust/graphql/input_object.rb +24 -0
  27. data/lib/stardust/graphql/mutation.rb +61 -0
  28. data/lib/stardust/graphql/object.rb +30 -0
  29. data/lib/stardust/graphql/query.rb +62 -0
  30. data/lib/stardust/graphql/scalar.rb +23 -0
  31. data/lib/stardust/graphql/schema.rb +8 -0
  32. data/lib/stardust/graphql/types/dsl.rb +27 -0
  33. data/lib/stardust/graphql/types.rb +10 -0
  34. data/lib/stardust/graphql/union.rb +27 -0
  35. data/lib/stardust/graphql.rb +25 -0
  36. data/lib/stardust/instance.rb +25 -0
  37. data/lib/stardust/version.rb +3 -0
  38. data/lib/stardust.rb +26 -0
  39. data/lib/tasks/stardust_tasks.rake +13 -0
  40. metadata +167 -0
@@ -0,0 +1,161 @@
1
+ module Stardust
2
+ module GraphQL
3
+ module Collector
4
+ module_function
5
+
6
+ FIXED_TYPES = {
7
+ id: ::GraphQL::Types::ID,
8
+ int: Integer,
9
+ integer: Integer,
10
+ float: Float,
11
+ string: String,
12
+ date: ::GraphQL::Types::ISO8601DateTime,
13
+ datetime: ::GraphQL::Types::ISO8601DateTime,
14
+ boolean: ::GraphQL::Types::Boolean,
15
+ }.freeze
16
+
17
+
18
+ @@__types__ = {}.merge(FIXED_TYPES)
19
+ @@__defined_types__ = []
20
+ @@__queries__ = {}
21
+ @@__mutations__ = {}
22
+
23
+ def add_type(type, block, object_klass)
24
+ if type.in?(@@__types__.keys)
25
+ raise "Type #{type.to_s} is alread defined."
26
+ end
27
+
28
+ @@__defined_types__ << type.to_s.camelize
29
+
30
+ klass = Class.new(object_klass, &block)
31
+
32
+ begin
33
+ klass.graphql_name
34
+ rescue NotImplementedError
35
+ klass.graphql_name(type.to_s.camelize)
36
+ end
37
+
38
+ ::Stardust::GraphQL::Types.const_set("#{type.to_s.camelize}", klass)
39
+ @@__types__[type] = "Stardust::GraphQL::Types::#{type.to_s.camelize}".constantize
40
+ end
41
+
42
+ def self.clear_definitions!
43
+ @@__defined_types__ = []
44
+ @@__types__ = {}.merge(FIXED_TYPES)
45
+ @@__queries__ = {}
46
+ @@__mutations__ = {}
47
+
48
+ ::Stardust::GraphQL.send(:remove_const, "Types")
49
+ ::Stardust::GraphQL.send(:remove_const, "Schema")
50
+ load 'stardust/graphql/types'
51
+ load 'stardust/graphql/schema'
52
+ end
53
+
54
+ def add_query(name, query:)
55
+ if name.in?(@@__queries__.keys)
56
+ raise "Query #{name.to_s} is already defined."
57
+ end
58
+ @@__queries__[name] = query
59
+ end
60
+
61
+ def add_mutation(name, mutation:)
62
+ if name.in?(@@__queries__.keys)
63
+ raise "Mutation #{name.to_s} is already defined."
64
+ end
65
+ @@__mutations__[name] = mutation
66
+ end
67
+
68
+ def self.types
69
+ @@__types__
70
+ end
71
+
72
+ def self.queries
73
+ @@__queries__
74
+ end
75
+
76
+ def self.lookup_type(type)
77
+ if type.is_a?(Array)
78
+ type = type.first
79
+ is_a_array = true
80
+ end
81
+
82
+ raise MissingType, type.to_s unless @@__types__[type]
83
+
84
+ if is_a_array
85
+ [@@__types__[type]]
86
+ else
87
+ @@__types__[type]
88
+ end
89
+ end
90
+
91
+ def self.replace_types!
92
+ @@__types__.values.each do |klass|
93
+ begin
94
+ klass.replace_types! if klass.respond_to?(:replace_types!)
95
+ rescue MissingType => e
96
+ warn <<~TEXT
97
+
98
+ Stardust Compilation Error
99
+ Type #{e.message} is not defined.
100
+
101
+ TEXT
102
+ end
103
+ end
104
+
105
+ query_class = Class.new(::Stardust::GraphQL::Object)
106
+ query_class.graphql_name("QueryRoot")
107
+ @@__queries__.each do |name, query|
108
+
109
+ block = ->(field) {
110
+
111
+ query.get_arguments.each do |name, type, description, kwargs, block|
112
+ field.argument(name, type, description, **kwargs, &block)
113
+ end
114
+
115
+ field.instance_variable_set(:@resolver_class, query)
116
+ field.instance_variable_set(:@resolver_method, :resolve)
117
+ field.extension(Extensions::Authorize)
118
+ }
119
+
120
+ query_class.send(:field,
121
+ name,
122
+ query.get_type,
123
+ query.get_description,
124
+ null: query.get_null,
125
+ &block
126
+ )
127
+ end
128
+
129
+ begin
130
+ query_class.replace_types!
131
+ rescue MissingType => e
132
+ warn <<~TEXT
133
+
134
+ Stardust Compilation Error
135
+ Type #{e.message} is not defined.
136
+
137
+ TEXT
138
+ end
139
+ Schema.query(query_class)
140
+
141
+
142
+ mutation_class = Class.new(::GraphQL::Schema::Object)
143
+ mutation_class.graphql_name("MutationRoot")
144
+ @@__mutations__.each do |name, mutation|
145
+ begin
146
+ mutation.replace_types!
147
+ mutation_class.send(:field, name, mutation: mutation )
148
+ rescue MissingType => e
149
+ warn <<~TEXT
150
+
151
+ Stardust Compilation Error
152
+ Type #{e.message} is not defined.
153
+
154
+ TEXT
155
+ end
156
+ end
157
+ Schema.mutation(mutation_class)
158
+ end
159
+ end
160
+ end
161
+ end
@@ -0,0 +1,23 @@
1
+ module Stardust
2
+ module GraphQL
3
+ class Configuration
4
+
5
+ def setup_context(&block)
6
+ if block_given?
7
+ @setup_context = block
8
+ else
9
+ @setup_context
10
+ end
11
+ end
12
+
13
+ def around_execute(&block)
14
+ if block_given?
15
+ @around_execute = block
16
+ else
17
+ @around_execute
18
+ end
19
+ end
20
+
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,45 @@
1
+ module Stardust
2
+ module GraphQL
3
+ module DSL
4
+
5
+ class MissingType < StandardError; end;
6
+
7
+ def define_types(&block)
8
+ klass = Class.new(Types::DSL)
9
+ klass.class_eval(&block)
10
+ end
11
+
12
+ def define_query(name, &block)
13
+ klass = Class.new(Query)
14
+ klass.class_eval(&block)
15
+ if !klass.get_type
16
+ warn <<~TEXT
17
+
18
+ Stardust Compilation Error
19
+ Missing type definition for query in "#{name}".
20
+ File: "#{calling_file}"
21
+
22
+ ## Define the return type of the query
23
+ type :my_type
24
+
25
+ TEXT
26
+ else
27
+ Collector.add_query(name, query: klass)
28
+ end
29
+ end
30
+
31
+ def define_mutation(name, &block)
32
+ klass = Class.new(Mutation)
33
+ klass.send(:graphql_name, name.to_s.camelize)
34
+ klass.class_eval(&block)
35
+ Collector.add_mutation(name, mutation: klass)
36
+ end
37
+
38
+ def calling_file
39
+ caller[1][/[^:]+/]
40
+ .gsub(Rails.root.to_s, "")[1..-1]
41
+ end
42
+
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,22 @@
1
+ module Stardust
2
+ module GraphQL
3
+ module Extensions
4
+ class Authorize < ::GraphQL::Schema::FieldExtension
5
+
6
+ def after_resolve(object:, arguments:, context:, value:, memo:)
7
+ klass = field.instance_variable_get(:@resolver_class)
8
+ instance = klass.new(object: object, context: context)
9
+
10
+ if instance.respond_to?(:authorized?)
11
+ unless instance.authorized?(value: value, context: context, arguments: arguments)
12
+ raise ::GraphQL::ExecutionError, "Not authorized to access"
13
+ end
14
+ end
15
+
16
+ value
17
+ end
18
+
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,38 @@
1
+ module Stardust
2
+ module GraphQL
3
+ class Field < ::GraphQL::Schema::Field
4
+
5
+ def initialize(*args, **kwargs, &block)
6
+ super(*args, **kwargs, &block)
7
+ end
8
+
9
+ def authorize(proc, &block)
10
+ @authorize = block_given? ? block : proc
11
+ end
12
+
13
+ def authorized?(obj, ctx)
14
+ if @authorize.respond_to?(:call)
15
+ unless @authorize.(obj, ctx)
16
+ raise ::GraphQL::ExecutionError, "Not authorized"
17
+ end
18
+ else
19
+ super(obj, ctx)
20
+ end
21
+ end
22
+
23
+ def resolve(&block)
24
+ @resolve = block
25
+ end
26
+
27
+ def argument(name, type, description = nil, loads: nil, **kwargs)
28
+ actual_type = Collector.lookup_type(type)
29
+ if loads
30
+ kwargs[:prepare] = ->(obj, ctx) { loads.find(obj) }
31
+ end
32
+
33
+ super(name, actual_type, description, **kwargs)
34
+ end
35
+
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,24 @@
1
+ module Stardust
2
+ module GraphQL
3
+ class InputObject < ::GraphQL::Schema::InputObject
4
+
5
+ def self.argument(name, type, description = nil, **kwargs, &block)
6
+
7
+ @__types_to_lookup__ ||= []
8
+ @__types_to_lookup__ << ->(klass) {
9
+ actual_type = Collector.lookup_type(type)
10
+
11
+ klass
12
+ .method(:argument)
13
+ .super_method
14
+ .call(name, actual_type, description, **kwargs, &block)
15
+
16
+ }
17
+ end
18
+
19
+ def self.replace_types!
20
+ @__types_to_lookup__.each {|lookup| lookup.(self)}
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,61 @@
1
+ module Stardust
2
+ module GraphQL
3
+ class Mutation < ::GraphQL::Schema::Mutation
4
+
5
+ def current_user
6
+ context[:current_user]
7
+ end
8
+
9
+ def self.argument(name, type, description = nil, loads: nil, **kwargs)
10
+ @__types_to_lookup__ ||= []
11
+ the_file = caller[0][/[^:]+/].gsub(Rails.root.to_s, "")
12
+ the_line = caller[0].split(":")[1]
13
+ @__types_to_lookup__ << lambda { |klass|
14
+ begin
15
+ actual_type = Collector.lookup_type(type)
16
+
17
+ kwargs[:prepare] = ->(obj, _ctx) { loads.find(obj) } if loads
18
+
19
+ klass.method(:argument)
20
+ .super_method
21
+ .call(name, actual_type, description, **kwargs)
22
+ rescue MissingType => e
23
+ warn <<~TEXT
24
+
25
+ Stardust Compilation Warning
26
+ - MESSAGE: Type #{e.message} is not defined.
27
+ - RESULT: This mutation is not added to the graph.
28
+ - FILE: #{the_file}
29
+ - LINE: #{the_line}
30
+
31
+ TEXT
32
+ end
33
+ }
34
+ end
35
+
36
+ def self.field(name, type, description = nil, **kwargs)
37
+ @__types_to_lookup__ ||= []
38
+ @__types_to_lookup__ << lambda { |klass|
39
+ actual_type = Collector.lookup_type(type)
40
+
41
+ klass.method(:field)
42
+ .super_method
43
+ .call(name, actual_type, description, **kwargs)
44
+ }
45
+ end
46
+
47
+ def self.replace_types!
48
+ return unless @__types_to_lookup__
49
+ @__types_to_lookup__.each { |lookup| lookup.call(self) }
50
+ end
51
+
52
+ def self.resolve_method
53
+ :resolve_wrapper
54
+ end
55
+
56
+ def resolve_wrapper(**kwargs)
57
+ resolve(**kwargs)
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,30 @@
1
+ module Stardust
2
+ module GraphQL
3
+ class Object < ::GraphQL::Schema::Object
4
+
5
+ field_class Field
6
+
7
+ def self.field(name, type, description = nil, **kwargs, &block)
8
+
9
+ @__types_to_lookup__ ||= []
10
+ @__types_to_lookup__ << ->(klass) {
11
+ actual_type = Collector.lookup_type(type)
12
+
13
+ klass
14
+ .method(:field)
15
+ .super_method
16
+ .call(name, actual_type, description, **kwargs, &block)
17
+ }
18
+ end
19
+
20
+ def self.replace_types!
21
+ return unless @__types_to_lookup__
22
+ @__types_to_lookup__.each {|lookup| lookup.(self)}
23
+ end
24
+
25
+ def current_user
26
+ context[:current_user]
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,62 @@
1
+ module Stardust
2
+ module GraphQL
3
+ class Query
4
+ attr_reader :context
5
+
6
+ def initialize(object:, context:)
7
+ @context = context
8
+ end
9
+
10
+ def current_user
11
+ context[:current_user]
12
+ end
13
+
14
+ def self.authorized?(object, context)
15
+ true
16
+ end
17
+
18
+ def self.accessible?(context)
19
+ true
20
+ end
21
+
22
+ def self.visible?(context)
23
+ true
24
+ end
25
+
26
+ class << self
27
+ def type(type)
28
+ @__type__ = type
29
+ end
30
+
31
+ def null(null)
32
+ @__null__ = null
33
+ end
34
+
35
+ def description(description)
36
+ @__description__ = description
37
+ end
38
+
39
+ def argument(name, type, description = nil, **kwargs, &block)
40
+ @__arguments__ ||= []
41
+ @__arguments__ << [name, type, description, kwargs, block]
42
+ end
43
+
44
+ def get_type
45
+ @__type__
46
+ end
47
+
48
+ def get_null
49
+ @__null__
50
+ end
51
+
52
+ def get_description
53
+ @__description__
54
+ end
55
+
56
+ def get_arguments
57
+ @__arguments__ || []
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,23 @@
1
+ module Stardust
2
+ module GraphQL
3
+ class Scalar < GraphQL::Schema::Scalar
4
+
5
+ def self.coerce_input(input_value = nil, context = nil, &block)
6
+ if block_given?
7
+ @@__coerce_input_block__ = block
8
+ else
9
+ @@__coerce_input_block__.(input_value, context)
10
+ end
11
+ end
12
+
13
+ def self.coerce_result(ruby_value = nil, context = nil, &block)
14
+ if block_given?
15
+ @@__coerce_result_block__ = block
16
+ else
17
+ @@__coerce_result_block__.(ruby_value, context)
18
+ end
19
+ end
20
+
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,8 @@
1
+
2
+ module Stardust
3
+ module GraphQL
4
+ class Schema < ::GraphQL::Schema
5
+ use ::GraphQL::Batch
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,27 @@
1
+ module Stardust
2
+ module GraphQL
3
+ module Types
4
+ class DSL
5
+
6
+ class << self
7
+ def object(type, &block)
8
+ Collector.add_type( type, block, Object)
9
+ end
10
+
11
+ def input_object(type, &block)
12
+ Collector.add_type( type, block, InputObject )
13
+ end
14
+
15
+ def scalar(type, &block)
16
+ Collector.add_type( type, block, Scalar )
17
+ end
18
+
19
+ def union(type, &block)
20
+ Collector.add_type( type, block, Union )
21
+ end
22
+ end
23
+
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,10 @@
1
+ module Stardust
2
+ module GraphQL
3
+ module Types
4
+
5
+ def self.types
6
+ Collector.types
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,27 @@
1
+ module Stardust
2
+ module GraphQL
3
+ class Union < GraphQL::Schema::Union
4
+
5
+
6
+ def self.possible_type(type, klass)
7
+ @__possible_types__ ||= {}
8
+ @__possible_types__[type] = klass
9
+ end
10
+
11
+ def self.replace_types!
12
+ return unless @__possible_types__
13
+ @__possible_types__ = @__possible_types__.reduce({}) do |accu, (type, klass)|
14
+ lu_type = Collector.lookup_type(type)
15
+ accu[lu_type] = klass
16
+ accu
17
+ end
18
+ self.send(:possible_types, *@__possible_types__.keys)
19
+ end
20
+
21
+ def self.resolve_type(obj, ctx)
22
+ @__possible_types__.invert[obj.class]
23
+ end
24
+
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,25 @@
1
+ require "graphql"
2
+ require "graphiql/rails"
3
+ require "graphql/rails_logger"
4
+ require "graphql/batch"
5
+
6
+ require "stardust/graphql/types"
7
+ require "stardust/graphql/schema"
8
+
9
+ require "stardust/graphql/dsl"
10
+ require "stardust/graphql/types/dsl"
11
+
12
+ module Stardust
13
+ module GraphQL
14
+ extend DSL
15
+
16
+ class MissingType < StandardError; end
17
+ end
18
+ end
19
+
20
+ require "stardust/graphql/field"
21
+ require "stardust/graphql/object"
22
+ require "stardust/graphql/union"
23
+ require "stardust/graphql/scalar"
24
+ require "stardust/graphql/collector"
25
+ require "stardust/graphql/input_object"
@@ -0,0 +1,25 @@
1
+ module Stardust
2
+ module Instance
3
+
4
+ def instance
5
+ if Rails.env == 'production'
6
+ ENV['INSTANCE'].to_sym || :production
7
+ else
8
+ Rails.env.to_sym
9
+ end
10
+ end
11
+
12
+ def production?
13
+ instance == :production
14
+ end
15
+
16
+ def staging?
17
+ instance == :staging
18
+ end
19
+
20
+ def development?
21
+ instance == :development
22
+ end
23
+
24
+ end
25
+ end
@@ -0,0 +1,3 @@
1
+ module Stardust
2
+ VERSION = '0.1.1'
3
+ end
data/lib/stardust.rb ADDED
@@ -0,0 +1,26 @@
1
+ require "rails"
2
+ require "stardust/engine"
3
+ require "stardust/instance"
4
+ require "stardust/configuration"
5
+
6
+ # this allows us to
7
+ ActiveSupport::Inflector.inflections(:en) do |inflect|
8
+ inflect.acronym "GraphQL"
9
+ inflect.acronym "DSL"
10
+ end
11
+
12
+ module Stardust
13
+ extend Instance
14
+
15
+ def self.configuration
16
+ @configuration ||= Configuration.new
17
+ end
18
+
19
+ def self.configure
20
+ yield(configuration)
21
+ end
22
+
23
+ end
24
+
25
+ require "stardust/graphql"
26
+ require "stardust/generators"
@@ -0,0 +1,13 @@
1
+ # desc "Explaining what the task does"
2
+ # task :stardust do
3
+ # # Task goes here
4
+ # end
5
+
6
+
7
+
8
+ namespace :stardust do
9
+
10
+ task :install do
11
+
12
+ end
13
+ end