gql 0.0.19 → 0.0.20
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/gql.rb +6 -20
- data/lib/gql/array.rb +8 -14
- data/lib/gql/config.rb +19 -53
- data/lib/gql/connection.rb +4 -8
- data/lib/gql/errors.rb +1 -1
- data/lib/gql/executor.rb +2 -2
- data/lib/gql/field.rb +6 -14
- data/lib/gql/lazy.rb +31 -0
- data/lib/gql/mixins/common.rb +31 -0
- data/lib/gql/mixins/has_calls.rb +120 -0
- data/lib/gql/mixins/has_fields.rb +138 -0
- data/lib/gql/object.rb +16 -4
- data/lib/gql/registry.rb +39 -0
- data/lib/gql/schema/call.rb +2 -2
- data/lib/gql/schema/field.rb +2 -2
- data/lib/gql/version.rb +1 -1
- metadata +21 -5
- data/lib/gql/has_calls.rb +0 -104
- data/lib/gql/has_fields.rb +0 -132
- data/lib/gql/test_case.rb +0 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 84b1f6dbe8ad680fd457f4f14dbd0183b4b18d46
|
4
|
+
data.tar.gz: 192058fde95f568491e87c6edd719b9a4c85b1c1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dee4291ac7f769adf121c145b8f2a2a93bd0a3417aba29e218120cc2106045f2e4f19cbcd2491b778aff4531f6c04d88acb39500bae1d24763be054131cfe29a
|
7
|
+
data.tar.gz: ae67282c3aa9cf8b48155429c7f6714f16562ba3ff77fdf9f4a9edcfabc8fae59d02b7472490599f3959b36a2af82971ad8ff36754be8f3b8b61cf71d757ff64
|
data/lib/gql.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
require 'active_support/dependencies/autoload'
|
2
|
+
|
2
3
|
require 'gql/version'
|
4
|
+
require 'gql/errors'
|
3
5
|
|
4
6
|
module GQL
|
5
7
|
extend ActiveSupport::Autoload
|
@@ -9,33 +11,17 @@ module GQL
|
|
9
11
|
autoload :Call
|
10
12
|
autoload :Config
|
11
13
|
autoload :Connection
|
12
|
-
autoload :Error, 'gql/errors'
|
13
14
|
autoload :Executor
|
14
15
|
autoload :Field
|
16
|
+
autoload :Lazy
|
15
17
|
autoload :Number
|
16
18
|
autoload :Object
|
17
19
|
autoload :Parser
|
20
|
+
autoload :Registry
|
18
21
|
autoload :Scalar
|
19
22
|
autoload :String
|
20
|
-
autoload :TestCase
|
21
23
|
autoload :Tokenizer
|
22
24
|
|
23
|
-
module Errors
|
24
|
-
extend ActiveSupport::Autoload
|
25
|
-
|
26
|
-
autoload_at 'gql/errors' do
|
27
|
-
autoload :CallNotFound
|
28
|
-
autoload :FieldClassNotSet
|
29
|
-
autoload :FieldNotFound
|
30
|
-
autoload :InvalidFieldClass
|
31
|
-
autoload :NoMethodError
|
32
|
-
autoload :RootClassNotSet
|
33
|
-
autoload :ScanError
|
34
|
-
autoload :SyntaxError
|
35
|
-
autoload :VariableNotFound
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
25
|
module Schema
|
40
26
|
extend ActiveSupport::Autoload
|
41
27
|
|
@@ -55,8 +41,8 @@ module GQL
|
|
55
41
|
Thread.current[:gql_config] = value
|
56
42
|
end
|
57
43
|
|
58
|
-
%w(
|
59
|
-
|
44
|
+
%w(root_class root_target_proc field_types
|
45
|
+
default_list_class default_field_proc
|
60
46
|
default_call_proc debug).each do |method|
|
61
47
|
module_eval <<-DELEGATORS, __FILE__, __LINE__ + 1
|
62
48
|
def #{method}
|
data/lib/gql/array.rb
CHANGED
@@ -2,32 +2,26 @@ require 'active_support/core_ext/class/attribute'
|
|
2
2
|
|
3
3
|
module GQL
|
4
4
|
class Array < Field
|
5
|
-
class_attribute :
|
5
|
+
class_attribute :item_class, instance_accessor: false, instance_predicate: false
|
6
6
|
|
7
7
|
class << self
|
8
8
|
def build_class(id, proc, options = {})
|
9
|
-
|
10
|
-
|
11
|
-
if item_field_class.is_a?(Hash)
|
12
|
-
item_field_class.values.each do |ifc|
|
13
|
-
Field.validate_is_subclass! ifc, 'item_field_class'
|
14
|
-
end
|
15
|
-
else
|
16
|
-
Field.validate_is_subclass! item_field_class, 'item_field_class'
|
17
|
-
item_field_class = Hash.new(item_field_class)
|
18
|
-
end
|
9
|
+
item_class = options.delete(:item_class) || self.item_class
|
10
|
+
item_class = ::Hash.new(item_class) unless item_class.is_a?(::Hash)
|
19
11
|
|
20
12
|
Class.new(self).tap do |field_class|
|
21
|
-
field_class.id = id
|
13
|
+
field_class.id = id
|
22
14
|
field_class.proc = proc
|
23
|
-
field_class.
|
15
|
+
field_class.item_class = item_class
|
24
16
|
end
|
25
17
|
end
|
26
18
|
end
|
27
19
|
|
28
20
|
def value
|
29
21
|
target.map do |item|
|
30
|
-
|
22
|
+
field_class = Registry.fetch(self.class.item_class[item.class])
|
23
|
+
|
24
|
+
field = field_class.new(ast_node, item, variables, context)
|
31
25
|
field.value
|
32
26
|
end
|
33
27
|
end
|
data/lib/gql/config.rb
CHANGED
@@ -1,17 +1,11 @@
|
|
1
|
-
require 'active_support/core_ext/class/subclasses'
|
2
|
-
|
3
1
|
module GQL
|
4
2
|
class Config
|
5
|
-
def
|
6
|
-
@@
|
3
|
+
def root_class
|
4
|
+
@@root_class ||= nil
|
7
5
|
end
|
8
6
|
|
9
|
-
def
|
10
|
-
|
11
|
-
raise Errors::InvalidFieldClass.new(value, Field)
|
12
|
-
end
|
13
|
-
|
14
|
-
@@root_field_class = value
|
7
|
+
def root_class=(value)
|
8
|
+
@@root_class = value
|
15
9
|
end
|
16
10
|
|
17
11
|
def root_target_proc
|
@@ -24,12 +18,12 @@ module GQL
|
|
24
18
|
|
25
19
|
def field_types
|
26
20
|
@@field_types ||= {
|
27
|
-
array: Array,
|
28
|
-
boolean: Boolean,
|
29
|
-
connection: Connection,
|
30
|
-
number: Number,
|
31
|
-
object: Object,
|
32
|
-
string: String
|
21
|
+
array: 'GQL::Array',
|
22
|
+
boolean: 'GQL::Boolean',
|
23
|
+
connection: 'GQL::Connection',
|
24
|
+
number: 'GQL::Number',
|
25
|
+
object: 'GQL::Object',
|
26
|
+
string: 'GQL::String'
|
33
27
|
}
|
34
28
|
end
|
35
29
|
|
@@ -37,16 +31,12 @@ module GQL
|
|
37
31
|
@@field_types = value
|
38
32
|
end
|
39
33
|
|
40
|
-
def
|
41
|
-
@@
|
34
|
+
def default_list_class
|
35
|
+
@@default_list_class ||= 'GQL::Field'
|
42
36
|
end
|
43
37
|
|
44
|
-
def
|
45
|
-
|
46
|
-
raise Errors::InvalidFieldClass.new(value, Field)
|
47
|
-
end
|
48
|
-
|
49
|
-
@@default_list_field_class = value
|
38
|
+
def default_list_class=(value)
|
39
|
+
@@default_list_class = value
|
50
40
|
end
|
51
41
|
|
52
42
|
def default_field_proc
|
@@ -77,6 +67,8 @@ module GQL
|
|
77
67
|
def debug=(value)
|
78
68
|
value = !!value
|
79
69
|
|
70
|
+
@@debug = nil unless defined?(@@debug)
|
71
|
+
|
80
72
|
return if value == @@debug
|
81
73
|
|
82
74
|
value ? switch_debug_on : switch_debug_off
|
@@ -86,39 +78,13 @@ module GQL
|
|
86
78
|
|
87
79
|
private
|
88
80
|
def switch_debug_on
|
89
|
-
|
90
|
-
switch_on_execution_context
|
91
|
-
end
|
92
|
-
|
93
|
-
def switch_debug_off
|
94
|
-
switch_off_type_field
|
95
|
-
switch_off_execution_context
|
96
|
-
end
|
97
|
-
|
98
|
-
def switch_on_type_field
|
99
|
-
return if Field.has_field? :__type__
|
100
|
-
|
101
|
-
type_field_class = Field.object :__type__, -> { field_class }, field_class: Schema::Field
|
102
|
-
|
103
|
-
Field.descendants.each do |field_class|
|
104
|
-
field_class.fields[:__type__] = type_field_class
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
def switch_off_type_field
|
109
|
-
return unless Field.has_field? :__type__
|
110
|
-
|
111
|
-
[Field, *Field.descendants].each do |field_class|
|
112
|
-
field_class.remove_field :__type__
|
113
|
-
end
|
114
|
-
end
|
115
|
-
|
116
|
-
def switch_on_execution_context
|
81
|
+
Field.object :__type__, -> { field_class }, class: Schema::Field
|
117
82
|
Field.send :remove_const, :ExecutionContext if Field.const_defined?(:ExecutionContext)
|
118
83
|
Field.const_set :ExecutionContext, Field::ExecutionContextDebug
|
119
84
|
end
|
120
85
|
|
121
|
-
def
|
86
|
+
def switch_debug_off
|
87
|
+
Field.remove_field :__type__
|
122
88
|
Field.send :remove_const, :ExecutionContext if Field.const_defined?(:ExecutionContext)
|
123
89
|
Field.const_set :ExecutionContext, Field::ExecutionContextNoDebug
|
124
90
|
end
|
data/lib/gql/connection.rb
CHANGED
@@ -1,16 +1,12 @@
|
|
1
|
-
require 'active_support/core_ext/class/attribute'
|
2
|
-
|
3
1
|
module GQL
|
4
2
|
class Connection < Field
|
5
3
|
class << self
|
6
4
|
def build_class(id, proc, options = {})
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
Field.validate_is_subclass! list_field_class, 'list_field_class'
|
5
|
+
list_class = options.delete(:list_class) || GQL.default_list_class
|
6
|
+
item_class = options.delete(:item_class)
|
11
7
|
|
12
|
-
|
13
|
-
field_class.array :edges, -> { target },
|
8
|
+
Registry.fetch(list_class).build_class(id, proc, options).tap do |field_class|
|
9
|
+
field_class.array :edges, -> { target }, item_class: item_class
|
14
10
|
end
|
15
11
|
end
|
16
12
|
end
|
data/lib/gql/errors.rb
CHANGED
data/lib/gql/executor.rb
CHANGED
@@ -8,7 +8,7 @@ module GQL
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def execute(context = {}, vars = {})
|
11
|
-
field_class = GQL.
|
11
|
+
field_class = GQL.root_class
|
12
12
|
|
13
13
|
raise Errors::RootClassNotSet if field_class.nil?
|
14
14
|
|
@@ -16,7 +16,7 @@ module GQL
|
|
16
16
|
|
17
17
|
target = GQL.root_target_proc.call(context)
|
18
18
|
|
19
|
-
field = field_class.new(ast_root, target, variables, context)
|
19
|
+
field = Registry.fetch(field_class).new(ast_root, target, variables, context)
|
20
20
|
field.value
|
21
21
|
end
|
22
22
|
end
|
data/lib/gql/field.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
require 'active_support/core_ext/class/attribute'
|
2
2
|
|
3
|
-
require 'gql/
|
4
|
-
require 'gql/
|
3
|
+
require 'gql/mixins/common'
|
4
|
+
require 'gql/mixins/has_calls'
|
5
|
+
require 'gql/mixins/has_fields'
|
5
6
|
|
6
7
|
module GQL
|
7
8
|
class Field
|
@@ -14,20 +15,11 @@ module GQL
|
|
14
15
|
field_class.proc = proc
|
15
16
|
end
|
16
17
|
end
|
17
|
-
|
18
|
-
def validate_is_subclass!(subclass, name)
|
19
|
-
if subclass.nil?
|
20
|
-
raise Errors::FieldClassNotSet.new(self, name)
|
21
|
-
end
|
22
|
-
|
23
|
-
unless subclass <= self
|
24
|
-
raise Errors::InvalidFieldClass.new(subclass, self)
|
25
|
-
end
|
26
|
-
end
|
27
18
|
end
|
28
19
|
|
29
|
-
|
30
|
-
include
|
20
|
+
extend Mixins::Common
|
21
|
+
include Mixins::HasCalls
|
22
|
+
include Mixins::HasFields
|
31
23
|
|
32
24
|
attr_reader :ast_node, :target, :variables, :context
|
33
25
|
|
data/lib/gql/lazy.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'active_support/core_ext/class/attribute'
|
2
|
+
|
3
|
+
module GQL
|
4
|
+
class Lazy < Field
|
5
|
+
class_attribute :owner, :type, :options, instance_accessor: false, instance_predicate: false
|
6
|
+
|
7
|
+
class << self
|
8
|
+
def build_class(id, proc, options)
|
9
|
+
Class.new(self).tap do |field_class|
|
10
|
+
field_class.owner = options.delete(:owner)
|
11
|
+
field_class.type = options.delete(:type)
|
12
|
+
field_class.id = id
|
13
|
+
field_class.proc = proc
|
14
|
+
field_class.options = options
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def resolve
|
19
|
+
owner.remove_field id
|
20
|
+
owner.add_field id, proc, options.merge(type: Registry.fetch(type))
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def value
|
25
|
+
field_class = self.class.resolve
|
26
|
+
|
27
|
+
field = field_class.new(ast_node, target, variables, context)
|
28
|
+
field.value
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'active_support/concern'
|
2
|
+
require 'active_support/core_ext/string/inflections'
|
3
|
+
|
4
|
+
module GQL
|
5
|
+
module Mixins
|
6
|
+
module Common
|
7
|
+
def propagate(type, id, klass)
|
8
|
+
const_name = send("const_name_for_#{type}", id)
|
9
|
+
accessor = type.to_s.pluralize
|
10
|
+
|
11
|
+
const_set const_name, klass
|
12
|
+
|
13
|
+
[self, *descendants].each do |c|
|
14
|
+
c.send "#{accessor}=", c.send(accessor).merge(id => klass)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def shutdown(type, id)
|
19
|
+
const_name = send("const_name_for_#{type}", id)
|
20
|
+
accessor = type.to_s.pluralize
|
21
|
+
|
22
|
+
[self, *descendants].each do |c|
|
23
|
+
next unless c.send("has_#{type}?", id)
|
24
|
+
|
25
|
+
c.send :remove_const, const_name if c.const_defined?(const_name, false)
|
26
|
+
c.send(accessor).delete id
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
require 'active_support/concern'
|
2
|
+
require 'active_support/core_ext/array/extract_options'
|
3
|
+
require 'active_support/core_ext/class/attribute'
|
4
|
+
require 'active_support/core_ext/class/subclasses'
|
5
|
+
require 'active_support/core_ext/object/try'
|
6
|
+
require 'active_support/core_ext/string/inflections'
|
7
|
+
|
8
|
+
module GQL
|
9
|
+
module Mixins
|
10
|
+
module HasCalls
|
11
|
+
extend ActiveSupport::Concern
|
12
|
+
|
13
|
+
included do
|
14
|
+
class_attribute :calls, :call_proc, instance_accessor: false, instance_predicate: false
|
15
|
+
self.calls = {}
|
16
|
+
end
|
17
|
+
|
18
|
+
module ClassMethods
|
19
|
+
def add_call(id, *args, &block)
|
20
|
+
remove_call id
|
21
|
+
|
22
|
+
id = id.to_sym
|
23
|
+
options = args.extract_options!
|
24
|
+
call_spec = args.shift || block || proc_for_call(id)
|
25
|
+
result_spec = options[:returns] || call_spec.try(:result_class)
|
26
|
+
result_class = result_class_from_spec(result_spec)
|
27
|
+
|
28
|
+
build_call_class(call_spec, id, result_class).tap do |call_class|
|
29
|
+
propagate :call, id, call_class
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
alias :call :add_call
|
34
|
+
|
35
|
+
def remove_call(id)
|
36
|
+
shutdown :call, id.to_sym
|
37
|
+
end
|
38
|
+
|
39
|
+
def has_call?(id)
|
40
|
+
calls.has_key? id.to_sym
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
def build_call_class(spec, id, result_class)
|
45
|
+
call_class_from_spec(spec).tap do |call_class|
|
46
|
+
call_class.id = id
|
47
|
+
call_class.result_class = result_class
|
48
|
+
|
49
|
+
if result_class && result_class.name.nil?
|
50
|
+
call_class.const_set :Result, result_class
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def call_class_from_spec(spec)
|
56
|
+
return Class.new(spec) unless spec.is_a?(Proc)
|
57
|
+
|
58
|
+
Class.new(Call).tap do |call_class|
|
59
|
+
call_class.class_eval do
|
60
|
+
self.proc = spec
|
61
|
+
|
62
|
+
def execute(*args)
|
63
|
+
instance_exec(*args, &self.class.proc)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def const_name_for_call(id)
|
70
|
+
:"#{id.to_s.camelize}Call"
|
71
|
+
end
|
72
|
+
|
73
|
+
def proc_for_call(id)
|
74
|
+
instance_exec id, &(call_proc || GQL.default_call_proc)
|
75
|
+
end
|
76
|
+
|
77
|
+
def result_class_from_spec(spec)
|
78
|
+
result_class =
|
79
|
+
case spec
|
80
|
+
when ::Array
|
81
|
+
result_class_from_connection_spec spec.dup
|
82
|
+
when ::Hash
|
83
|
+
result_class_from_mapping_spec spec.dup
|
84
|
+
else
|
85
|
+
spec
|
86
|
+
end
|
87
|
+
|
88
|
+
result_class && Registry.fetch(result_class)
|
89
|
+
end
|
90
|
+
|
91
|
+
def result_class_from_connection_spec(spec)
|
92
|
+
if spec.size == 1
|
93
|
+
spec.unshift GQL.default_list_class
|
94
|
+
end
|
95
|
+
|
96
|
+
options = {
|
97
|
+
list_class: spec.first,
|
98
|
+
item_class: spec.last
|
99
|
+
}
|
100
|
+
|
101
|
+
Connection.build_class :result, nil, options
|
102
|
+
end
|
103
|
+
|
104
|
+
def result_class_from_mapping_spec(spec)
|
105
|
+
Object.build_class :result, nil, class: spec
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
private
|
110
|
+
def value_of_call(ast_call)
|
111
|
+
call_class = call_class_for_id(ast_call.id)
|
112
|
+
call_class.execute(self.class, ast_call, target, variables, context)
|
113
|
+
end
|
114
|
+
|
115
|
+
def call_class_for_id(id)
|
116
|
+
self.class.calls[id] or raise Errors::CallNotFound.new(id, self.class)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,138 @@
|
|
1
|
+
require 'active_support/concern'
|
2
|
+
require 'active_support/core_ext/array/extract_options'
|
3
|
+
require 'active_support/core_ext/class/attribute'
|
4
|
+
require 'active_support/core_ext/class/subclasses'
|
5
|
+
require 'active_support/core_ext/string/inflections'
|
6
|
+
|
7
|
+
module GQL
|
8
|
+
module Mixins
|
9
|
+
module HasFields
|
10
|
+
extend ActiveSupport::Concern
|
11
|
+
|
12
|
+
included do
|
13
|
+
class_attribute :fields, :field_proc, instance_accessor: false, instance_predicate: false
|
14
|
+
self.fields = {}
|
15
|
+
end
|
16
|
+
|
17
|
+
module ClassMethods
|
18
|
+
def add_field(id, *args, &block)
|
19
|
+
remove_field id
|
20
|
+
|
21
|
+
id = id.to_sym
|
22
|
+
options = args.extract_options!
|
23
|
+
type = options.delete(:type) || Field
|
24
|
+
proc = args.shift || block || proc_for_field(id)
|
25
|
+
|
26
|
+
build_field_class(type, id, proc, options).tap do |field_class|
|
27
|
+
propagate :field, id, field_class
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
alias :field :add_field
|
32
|
+
|
33
|
+
def remove_field(id)
|
34
|
+
shutdown :field, id.to_sym
|
35
|
+
end
|
36
|
+
|
37
|
+
def has_field?(id)
|
38
|
+
fields.has_key? id.to_sym
|
39
|
+
end
|
40
|
+
|
41
|
+
def cursor(id_or_proc)
|
42
|
+
id = id_or_proc.is_a?(Proc) ? nil : id_or_proc
|
43
|
+
proc = id ? -> { target.public_send(id) } : id_or_proc
|
44
|
+
|
45
|
+
add_field :cursor, proc, type: Scalar
|
46
|
+
end
|
47
|
+
|
48
|
+
def respond_to?(method, *args)
|
49
|
+
super || GQL.field_types.has_key?(method)
|
50
|
+
end
|
51
|
+
|
52
|
+
def method_missing(method, *args, &block)
|
53
|
+
if type = GQL.field_types[method]
|
54
|
+
define_field_method method, type
|
55
|
+
send method, *args, &block
|
56
|
+
else
|
57
|
+
super
|
58
|
+
end
|
59
|
+
rescue NoMethodError => exc
|
60
|
+
raise Errors::NoMethodError.new(self, method, exc)
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
def build_field_class(type, id, proc, options)
|
65
|
+
if type.is_a? ::String
|
66
|
+
Lazy.build_class id, proc, options.merge(owner: self, type: type)
|
67
|
+
else
|
68
|
+
Registry.fetch(type).build_class id, proc, options
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def const_name_for_field(id)
|
73
|
+
prefix = id == :__type__ ? 'Schema_Type' : id.to_s.camelize
|
74
|
+
:"#{prefix}Field"
|
75
|
+
end
|
76
|
+
|
77
|
+
def proc_for_field(id)
|
78
|
+
instance_exec id, &(field_proc || GQL.default_field_proc)
|
79
|
+
end
|
80
|
+
|
81
|
+
def define_field_method(name, type)
|
82
|
+
Field.define_singleton_method name do |*args, &block|
|
83
|
+
options = args.extract_options!.merge(type: type)
|
84
|
+
args = args.push(options)
|
85
|
+
|
86
|
+
add_field(*args, &block)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
private
|
92
|
+
def value_of_fields(ast_fields)
|
93
|
+
ast_fields.reduce({}) do |result, ast_field|
|
94
|
+
key = ast_field.alias_id || ast_field.id
|
95
|
+
|
96
|
+
result.merge key => value_of_field(ast_field)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def value_of_field(ast_field)
|
101
|
+
if ast_field.id == :node
|
102
|
+
field = self.class.new(ast_field, target, variables, context)
|
103
|
+
field.value
|
104
|
+
else
|
105
|
+
field_class = field_class_for_id(ast_field.id)
|
106
|
+
next_target = target_for_field(target, field_class.proc)
|
107
|
+
|
108
|
+
field = field_class.new(ast_field, next_target, variables, context)
|
109
|
+
field.value
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def field_class_for_id(id)
|
114
|
+
self.class.fields[id] or raise Errors::FieldNotFound.new(id, self.class)
|
115
|
+
end
|
116
|
+
|
117
|
+
def target_for_field(current_target, proc)
|
118
|
+
args = [current_target, context]
|
119
|
+
args.push self.class if GQL.debug
|
120
|
+
|
121
|
+
method = self.class.const_get(:ExecutionContext).new(*args)
|
122
|
+
method.execute proc
|
123
|
+
end
|
124
|
+
|
125
|
+
class ExecutionContextNoDebug < Struct.new(:target, :context)
|
126
|
+
def execute(method, args = [])
|
127
|
+
instance_exec(*args, &method)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
class ExecutionContextDebug < Struct.new(:target, :context, :field_class)
|
132
|
+
def execute(method, args = [])
|
133
|
+
instance_exec(*args, &method)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
data/lib/gql/object.rb
CHANGED
@@ -2,14 +2,26 @@ require 'active_support/core_ext/class/attribute'
|
|
2
2
|
|
3
3
|
module GQL
|
4
4
|
class Object < Field
|
5
|
+
class_attribute :object_class, instance_accessor: false, instance_predicate: false
|
6
|
+
|
5
7
|
class << self
|
6
8
|
def build_class(id, proc, options = {})
|
7
|
-
|
8
|
-
|
9
|
-
Field.validate_is_subclass! field_class, 'field_class'
|
9
|
+
object_class = options.delete(:class) || options.delete(:as)
|
10
|
+
object_class = ::Hash.new(object_class) unless object_class.is_a?(::Hash)
|
10
11
|
|
11
|
-
|
12
|
+
Class.new(self).tap do |klass|
|
13
|
+
klass.id = id
|
14
|
+
klass.proc = proc
|
15
|
+
klass.object_class = object_class
|
16
|
+
end
|
12
17
|
end
|
13
18
|
end
|
19
|
+
|
20
|
+
def value
|
21
|
+
field_class = Registry.fetch(self.class.object_class[target.class])
|
22
|
+
|
23
|
+
field = field_class.new(ast_node, target, variables, context)
|
24
|
+
field.value
|
25
|
+
end
|
14
26
|
end
|
15
27
|
end
|
data/lib/gql/registry.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'active_support/core_ext/string/inflections'
|
2
|
+
require 'active_support/per_thread_registry'
|
3
|
+
|
4
|
+
module GQL
|
5
|
+
class Registry
|
6
|
+
extend ActiveSupport::PerThreadRegistry
|
7
|
+
|
8
|
+
attr_reader :cache
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
reset
|
12
|
+
end
|
13
|
+
|
14
|
+
def reset
|
15
|
+
@cache = {}
|
16
|
+
end
|
17
|
+
|
18
|
+
def fetch(key, baseclass = Field)
|
19
|
+
cache[key] || begin
|
20
|
+
raise Errors::FieldClassNotSet.new(baseclass, 'TODO') if key.nil?
|
21
|
+
|
22
|
+
const, name =
|
23
|
+
if key.instance_of? ::Class
|
24
|
+
[key, key.name]
|
25
|
+
else
|
26
|
+
[key.constantize, key]
|
27
|
+
end
|
28
|
+
|
29
|
+
raise Errors::InvalidFieldClass.new(const, baseclass) unless const <= baseclass
|
30
|
+
|
31
|
+
cache.update name => const, const => const
|
32
|
+
|
33
|
+
cache[key]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
alias_method :[], :fetch
|
38
|
+
end
|
39
|
+
end
|
data/lib/gql/schema/call.rb
CHANGED
@@ -5,8 +5,8 @@ module GQL
|
|
5
5
|
|
6
6
|
string :id
|
7
7
|
string :name
|
8
|
-
object :result_class, -> { target.result_class || CallerClass },
|
9
|
-
array :parameters, -> { (target.proc || target.instance_method(:execute)).parameters },
|
8
|
+
object :result_class, -> { target.result_class || CallerClass }, class: Field
|
9
|
+
array :parameters, -> { (target.proc || target.instance_method(:execute)).parameters }, item_class: Parameter
|
10
10
|
|
11
11
|
def scalar_value
|
12
12
|
target.name
|
data/lib/gql/schema/field.rb
CHANGED
@@ -5,8 +5,8 @@ module GQL
|
|
5
5
|
|
6
6
|
string :id
|
7
7
|
string :name
|
8
|
-
connection :calls, -> { target.calls.values },
|
9
|
-
connection :fields, -> { target.fields.values },
|
8
|
+
connection :calls, -> { target.calls.values }, list_class: List, item_class: Call
|
9
|
+
connection :fields, -> { target.fields.values }, list_class: List, item_class: Field
|
10
10
|
|
11
11
|
def scalar_value
|
12
12
|
target.name
|
data/lib/gql/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gql
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.20
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Martin Andert
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-03-
|
11
|
+
date: 2015-03-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -66,6 +66,20 @@ dependencies:
|
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0.4'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: minitest-perf
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0.1'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0.1'
|
69
83
|
- !ruby/object:Gem::Dependency
|
70
84
|
name: activesupport
|
71
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -112,11 +126,14 @@ files:
|
|
112
126
|
- lib/gql/errors.rb
|
113
127
|
- lib/gql/executor.rb
|
114
128
|
- lib/gql/field.rb
|
115
|
-
- lib/gql/
|
116
|
-
- lib/gql/
|
129
|
+
- lib/gql/lazy.rb
|
130
|
+
- lib/gql/mixins/common.rb
|
131
|
+
- lib/gql/mixins/has_calls.rb
|
132
|
+
- lib/gql/mixins/has_fields.rb
|
117
133
|
- lib/gql/number.rb
|
118
134
|
- lib/gql/object.rb
|
119
135
|
- lib/gql/parser.rb
|
136
|
+
- lib/gql/registry.rb
|
120
137
|
- lib/gql/scalar.rb
|
121
138
|
- lib/gql/schema/call.rb
|
122
139
|
- lib/gql/schema/caller_class.rb
|
@@ -124,7 +141,6 @@ files:
|
|
124
141
|
- lib/gql/schema/list.rb
|
125
142
|
- lib/gql/schema/parameter.rb
|
126
143
|
- lib/gql/string.rb
|
127
|
-
- lib/gql/test_case.rb
|
128
144
|
- lib/gql/tokenizer.rb
|
129
145
|
- lib/gql/version.rb
|
130
146
|
homepage: https://github.com/martinandert/gql
|
data/lib/gql/has_calls.rb
DELETED
@@ -1,104 +0,0 @@
|
|
1
|
-
require 'active_support/concern'
|
2
|
-
require 'active_support/core_ext/array/extract_options'
|
3
|
-
require 'active_support/core_ext/class/attribute'
|
4
|
-
require 'active_support/core_ext/string/inflections'
|
5
|
-
require 'active_support/core_ext/object/try'
|
6
|
-
|
7
|
-
module GQL
|
8
|
-
module HasCalls
|
9
|
-
extend ActiveSupport::Concern
|
10
|
-
|
11
|
-
included do
|
12
|
-
class_attribute :calls, :call_proc, instance_accessor: false, instance_predicate: false
|
13
|
-
self.calls = {}
|
14
|
-
end
|
15
|
-
|
16
|
-
module ClassMethods
|
17
|
-
def add_call(id, *args, &block)
|
18
|
-
options = args.extract_options!
|
19
|
-
|
20
|
-
call_spec = args.shift || block || proc_for_call(id)
|
21
|
-
result_spec = options[:returns] || call_spec.try(:result_class)
|
22
|
-
result_class = result_class_from_spec(result_spec)
|
23
|
-
|
24
|
-
Field.validate_is_subclass! result_class, 'result_class' if result_class
|
25
|
-
|
26
|
-
call_class = call_class_from_spec(call_spec)
|
27
|
-
call_class.id = id.to_s
|
28
|
-
call_class.result_class = result_class
|
29
|
-
|
30
|
-
if result_class && result_class.name.nil?
|
31
|
-
call_class.const_set :Result, result_class
|
32
|
-
end
|
33
|
-
|
34
|
-
self.const_set const_name_for_call(id), call_class
|
35
|
-
self.calls = calls.merge(id.to_sym => call_class)
|
36
|
-
end
|
37
|
-
|
38
|
-
alias :call :add_call
|
39
|
-
|
40
|
-
def remove_call(id)
|
41
|
-
const_name = const_name_for_call(id)
|
42
|
-
|
43
|
-
send :remove_const, const_name if const_defined?(const_name)
|
44
|
-
calls.delete id
|
45
|
-
end
|
46
|
-
|
47
|
-
def has_call?(id)
|
48
|
-
calls.has_key? id
|
49
|
-
end
|
50
|
-
|
51
|
-
private
|
52
|
-
def const_name_for_call(id)
|
53
|
-
:"#{id.to_s.camelize}Call"
|
54
|
-
end
|
55
|
-
|
56
|
-
def proc_for_call(id)
|
57
|
-
instance_exec id, &(call_proc || GQL.default_call_proc)
|
58
|
-
end
|
59
|
-
|
60
|
-
def result_class_from_spec(spec)
|
61
|
-
return spec unless spec.is_a? ::Array
|
62
|
-
|
63
|
-
result_class_from_connection_spec spec.dup
|
64
|
-
end
|
65
|
-
|
66
|
-
def result_class_from_connection_spec(spec)
|
67
|
-
if spec.size == 1
|
68
|
-
spec.unshift GQL.default_list_field_class
|
69
|
-
end
|
70
|
-
|
71
|
-
options = {
|
72
|
-
list_field_class: spec.first,
|
73
|
-
item_field_class: spec.last
|
74
|
-
}
|
75
|
-
|
76
|
-
Connection.build_class :result, nil, options
|
77
|
-
end
|
78
|
-
|
79
|
-
def call_class_from_spec(spec)
|
80
|
-
return Class.new(spec) unless spec.is_a?(Proc)
|
81
|
-
|
82
|
-
Class.new(Call).tap do |call_class|
|
83
|
-
call_class.class_eval do
|
84
|
-
self.proc = spec
|
85
|
-
|
86
|
-
def execute(*args)
|
87
|
-
instance_exec(*args, &self.class.proc)
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
private
|
95
|
-
def value_of_call(ast_call)
|
96
|
-
call_class = call_class_for_id(ast_call.id)
|
97
|
-
call_class.execute(self.class, ast_call, target, variables, context)
|
98
|
-
end
|
99
|
-
|
100
|
-
def call_class_for_id(id)
|
101
|
-
self.class.calls[id] or raise Errors::CallNotFound.new(id, self.class)
|
102
|
-
end
|
103
|
-
end
|
104
|
-
end
|
data/lib/gql/has_fields.rb
DELETED
@@ -1,132 +0,0 @@
|
|
1
|
-
require 'active_support/concern'
|
2
|
-
require 'active_support/core_ext/array/extract_options'
|
3
|
-
require 'active_support/core_ext/class/attribute'
|
4
|
-
require 'active_support/core_ext/string/inflections'
|
5
|
-
|
6
|
-
module GQL
|
7
|
-
module HasFields
|
8
|
-
extend ActiveSupport::Concern
|
9
|
-
|
10
|
-
included do
|
11
|
-
class_attribute :fields, :field_proc, instance_accessor: false, instance_predicate: false
|
12
|
-
self.fields = {}
|
13
|
-
end
|
14
|
-
|
15
|
-
module ClassMethods
|
16
|
-
def add_field(id, *args, &block)
|
17
|
-
options = args.extract_options!
|
18
|
-
type = options.delete(:type) || Field
|
19
|
-
proc = args.shift || block || proc_for_field(id)
|
20
|
-
|
21
|
-
Field.validate_is_subclass! type, 'type'
|
22
|
-
|
23
|
-
type.build_class(id, proc, options).tap do |field_class|
|
24
|
-
const_name = const_name_for_field(id)
|
25
|
-
|
26
|
-
const_set const_name, field_class unless const_defined?(const_name)
|
27
|
-
self.fields = fields.merge(id.to_sym => field_class)
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
alias :field :add_field
|
32
|
-
|
33
|
-
def remove_field(id)
|
34
|
-
const_name = const_name_for_field(id)
|
35
|
-
|
36
|
-
send :remove_const, const_name if const_defined?(const_name)
|
37
|
-
fields.delete id
|
38
|
-
end
|
39
|
-
|
40
|
-
def has_field?(id)
|
41
|
-
fields.has_key? id
|
42
|
-
end
|
43
|
-
|
44
|
-
def cursor(id_or_proc)
|
45
|
-
id = id_or_proc.is_a?(Proc) ? nil : id_or_proc
|
46
|
-
proc = id ? -> { target.public_send(id) } : id_or_proc
|
47
|
-
|
48
|
-
add_field :cursor, proc, type: Scalar
|
49
|
-
end
|
50
|
-
|
51
|
-
def respond_to?(method, *args)
|
52
|
-
super || GQL.field_types.has_key?(method)
|
53
|
-
end
|
54
|
-
|
55
|
-
def method_missing(method, *args, &block)
|
56
|
-
if type = GQL.field_types[method]
|
57
|
-
define_field_method method, type
|
58
|
-
send method, *args, &block
|
59
|
-
else
|
60
|
-
super
|
61
|
-
end
|
62
|
-
rescue NoMethodError => exc
|
63
|
-
raise Errors::NoMethodError.new(self, method, exc)
|
64
|
-
end
|
65
|
-
|
66
|
-
private
|
67
|
-
def const_name_for_field(id)
|
68
|
-
prefix = id == :__type__ ? 'Schema_Type' : id.to_s.camelize
|
69
|
-
:"#{prefix}Field"
|
70
|
-
end
|
71
|
-
|
72
|
-
def proc_for_field(id)
|
73
|
-
instance_exec id, &(field_proc || GQL.default_field_proc)
|
74
|
-
end
|
75
|
-
|
76
|
-
def define_field_method(name, type)
|
77
|
-
Field.define_singleton_method name do |*args, &block|
|
78
|
-
options = args.extract_options!.merge(type: type)
|
79
|
-
args = args.push(options)
|
80
|
-
|
81
|
-
add_field(*args, &block)
|
82
|
-
end
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
private
|
87
|
-
def value_of_fields(ast_fields)
|
88
|
-
ast_fields.reduce({}) do |result, ast_field|
|
89
|
-
key = ast_field.alias_id || ast_field.id
|
90
|
-
|
91
|
-
result.merge key => value_of_field(ast_field)
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
|
-
def value_of_field(ast_field)
|
96
|
-
if ast_field.id == :node
|
97
|
-
field = self.class.new(ast_field, target, variables, context)
|
98
|
-
field.value
|
99
|
-
else
|
100
|
-
field_class = field_class_for_id(ast_field.id)
|
101
|
-
next_target = target_for_field(target, field_class.proc)
|
102
|
-
|
103
|
-
field = field_class.new(ast_field, next_target, variables, context)
|
104
|
-
field.value
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
def field_class_for_id(id)
|
109
|
-
self.class.fields[id] or raise Errors::FieldNotFound.new(id, self.class)
|
110
|
-
end
|
111
|
-
|
112
|
-
def target_for_field(current_target, proc)
|
113
|
-
args = [current_target, context]
|
114
|
-
args.push self.class if GQL.debug
|
115
|
-
|
116
|
-
method = self.class.const_get(:ExecutionContext).new(*args)
|
117
|
-
method.execute proc
|
118
|
-
end
|
119
|
-
|
120
|
-
class ExecutionContextNoDebug < Struct.new(:target, :context)
|
121
|
-
def execute(method, args = [])
|
122
|
-
instance_exec(*args, &method)
|
123
|
-
end
|
124
|
-
end
|
125
|
-
|
126
|
-
class ExecutionContextDebug < Struct.new(:target, :context, :field_class)
|
127
|
-
def execute(method, args = [])
|
128
|
-
instance_exec(*args, &method)
|
129
|
-
end
|
130
|
-
end
|
131
|
-
end
|
132
|
-
end
|
data/lib/gql/test_case.rb
DELETED