gql 0.0.2 → 0.0.3
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 +4 -4
- data/README.md +60 -60
- data/lib/gql/call.rb +46 -7
- data/lib/gql/config.rb +23 -0
- data/lib/gql/connection.rb +22 -22
- data/lib/gql/errors.rb +15 -11
- data/lib/gql/executor.rb +8 -7
- data/lib/gql/field.rb +26 -2
- data/lib/gql/fields/connection.rb +26 -12
- data/lib/gql/fields/object.rb +28 -8
- data/lib/gql/node.rb +75 -90
- data/lib/gql/parser.rb +214 -284
- data/lib/gql/parser.y +40 -110
- data/lib/gql/version.rb +1 -1
- data/lib/gql.rb +33 -16
- metadata +3 -3
- data/lib/gql/schema.rb +0 -21
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c2a09930fd0d6998f3652bd99bacce706c64fb0e
|
4
|
+
data.tar.gz: 0d8a5d883919be233726b729ed4a7bec89c5958f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bf6337d65cc3a325006f1c0f87c932ae3344b035776e9a3d698deffc00421c88714d13009b8659ab2dee75e524d089966edede001ed68b49c0c17e968ece9a8d
|
7
|
+
data.tar.gz: 926bdde87803db064e8f4f9cb6834e4d2181a0bf1cea895a8f2d244140313140e63b3b479ce46a6f424a44cac34bad7548d2e13ded7245acbf3957bcc00b0e74
|
data/README.md
CHANGED
@@ -33,7 +33,7 @@ TODO: Write usage instructions here
|
|
33
33
|
|
34
34
|
## Example
|
35
35
|
|
36
|
-
Run `bin/console` for an interactive prompt and enter the following:
|
36
|
+
Run `bin/console` for an interactive prompt (loaded with example models/data) and enter the following:
|
37
37
|
|
38
38
|
```ruby
|
39
39
|
puts q(<<-QUERY_STRING).to_json
|
@@ -78,69 +78,69 @@ QUERY_STRING
|
|
78
78
|
This should result in the following JSON (after prettyfication):
|
79
79
|
|
80
80
|
```json
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
}
|
81
|
+
{
|
82
|
+
"id": "ma",
|
83
|
+
"is_admin": true,
|
84
|
+
"name": "Martin Andert",
|
85
|
+
"created_year_and_month": {
|
86
|
+
"year": 2010,
|
87
|
+
"month": 3
|
88
|
+
},
|
89
|
+
"created": "March 04, 2010 14:04",
|
90
|
+
"account": {
|
91
|
+
"bank_name": "Foo Bank",
|
92
|
+
"iban": "987654321",
|
93
|
+
"saldo_string": "100000.00 EUR",
|
94
|
+
"saldo": {
|
95
|
+
"currency": "EUR",
|
96
|
+
"cents": 10000000
|
97
|
+
}
|
98
|
+
},
|
99
|
+
"albums": {
|
100
|
+
"count": 2,
|
101
|
+
"edges": [
|
102
|
+
{
|
103
|
+
"cursor": "1",
|
104
|
+
"node": {
|
105
|
+
"artist": "Metallica",
|
106
|
+
"title": "Black Album",
|
107
|
+
"songs": {
|
108
|
+
"edges": [
|
109
|
+
{
|
110
|
+
"id": 1,
|
111
|
+
"upcased_title": "ENTER SANDMAN",
|
112
|
+
"upcased_title_length": 13
|
113
|
+
}, {
|
114
|
+
"id": 2,
|
115
|
+
"upcased_title": "SAD BUT TRUE",
|
116
|
+
"upcased_title_length": 12
|
117
|
+
}
|
118
|
+
]
|
120
119
|
}
|
121
|
-
}
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
120
|
+
}
|
121
|
+
}, {
|
122
|
+
"cursor": "2",
|
123
|
+
"node": {
|
124
|
+
"artist": "Nirvana",
|
125
|
+
"title": "Nevermind",
|
126
|
+
"songs": {
|
127
|
+
"edges": [
|
128
|
+
{
|
129
|
+
"id": 5,
|
130
|
+
"upcased_title": "SMELLS LIKE TEEN SPIRIT",
|
131
|
+
"upcased_title_length": 23
|
132
|
+
}, {
|
133
|
+
"id": 6,
|
134
|
+
"upcased_title": "COME AS YOU ARE",
|
135
|
+
"upcased_title_length": 15
|
136
|
+
}
|
137
|
+
]
|
139
138
|
}
|
140
139
|
}
|
141
|
-
|
142
|
-
|
140
|
+
}
|
141
|
+
]
|
143
142
|
}
|
143
|
+
}
|
144
144
|
```
|
145
145
|
|
146
146
|
|
data/lib/gql/call.rb
CHANGED
@@ -1,6 +1,44 @@
|
|
1
1
|
module GQL
|
2
2
|
class Call
|
3
|
-
|
3
|
+
class Method
|
4
|
+
attr_reader :target, :context
|
5
|
+
|
6
|
+
def initialize(target, context)
|
7
|
+
@target, @context = target, context
|
8
|
+
end
|
9
|
+
|
10
|
+
def execute(method, args)
|
11
|
+
instance_exec(*args, &method)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class_attribute :method, :result_class, instance_accessor: false, instance_predicate: false
|
16
|
+
|
17
|
+
class << self
|
18
|
+
def build_class(result_class, method)
|
19
|
+
if result_class.is_a? Array
|
20
|
+
result_class.unshift Connection if result_class.size == 1
|
21
|
+
result_class.unshift Fields::Connection if result_class.size == 2
|
22
|
+
|
23
|
+
field_type_class, connection_class, node_class = result_class
|
24
|
+
|
25
|
+
unless field_type_class <= Fields::Connection
|
26
|
+
raise Errors::InvalidNodeClass.new(field_type_class, Fields::Connection)
|
27
|
+
end
|
28
|
+
|
29
|
+
result_class = field_type_class.build_class(nil, connection_class, node_class)
|
30
|
+
elsif result_class && !(result_class <= Node)
|
31
|
+
raise Errors::InvalidNodeClass.new(result_class, Node)
|
32
|
+
end
|
33
|
+
|
34
|
+
Class.new(self).tap do |call_class|
|
35
|
+
call_class.method = method
|
36
|
+
call_class.result_class = result_class
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
attr_reader :caller, :ast_node, :target, :variables, :context
|
4
42
|
|
5
43
|
def initialize(caller, ast_node, target, variables, context)
|
6
44
|
@caller, @ast_node, @target = caller, ast_node, target
|
@@ -8,18 +46,19 @@ module GQL
|
|
8
46
|
end
|
9
47
|
|
10
48
|
def execute
|
11
|
-
args = substitute_variables(
|
12
|
-
target = instance_exec(*args, &self.class.const_get(:Function))
|
49
|
+
args = substitute_variables(ast_node.arguments)
|
13
50
|
|
14
|
-
|
51
|
+
method = Method.new(target, context)
|
52
|
+
target = method.execute(self.class.method, args)
|
53
|
+
result_class = self.class.result_class || caller.class
|
15
54
|
|
16
|
-
result = result_class.new(
|
17
|
-
result.
|
55
|
+
result = result_class.new(ast_node, target, variables, context)
|
56
|
+
result.value
|
18
57
|
end
|
19
58
|
|
20
59
|
private
|
21
60
|
def substitute_variables(args)
|
22
|
-
args.map { |arg| arg.is_a?(Symbol) ?
|
61
|
+
args.map { |arg| arg.is_a?(Symbol) ? variables[arg] : arg }
|
23
62
|
end
|
24
63
|
end
|
25
64
|
end
|
data/lib/gql/config.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
module GQL
|
2
|
+
class Config
|
3
|
+
def root_node_class
|
4
|
+
@@root_node_class ||= nil
|
5
|
+
end
|
6
|
+
|
7
|
+
def root_node_class=(value)
|
8
|
+
unless value.nil? || value <= Node
|
9
|
+
raise Errors::InvalidNodeClass.new(value, Node)
|
10
|
+
end
|
11
|
+
|
12
|
+
@@root_node_class = value
|
13
|
+
end
|
14
|
+
|
15
|
+
def field_types
|
16
|
+
@@field_types ||= {}
|
17
|
+
end
|
18
|
+
|
19
|
+
def field_types=(value)
|
20
|
+
@@field_types = value
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/gql/connection.rb
CHANGED
@@ -1,35 +1,35 @@
|
|
1
|
+
require 'active_support/core_ext/class/attribute'
|
2
|
+
|
1
3
|
module GQL
|
2
4
|
class Connection < Node
|
3
|
-
|
4
|
-
|
5
|
-
def initialize(node_class, *args)
|
6
|
-
super(*args)
|
7
|
-
|
8
|
-
@node_class = node_class
|
9
|
-
end
|
5
|
+
class_attribute :node_class, instance_accessor: false, instance_predicate: false
|
10
6
|
|
11
|
-
|
7
|
+
class << self
|
8
|
+
def build_class(node_class)
|
9
|
+
node_class ||= self.node_class
|
12
10
|
|
13
|
-
|
14
|
-
|
15
|
-
|
11
|
+
if node_class.nil?
|
12
|
+
raise Errors::UndefinedNodeClass.new(self, 'node')
|
13
|
+
end
|
16
14
|
|
17
|
-
|
18
|
-
|
15
|
+
unless node_class <= GQL::Node
|
16
|
+
raise Errors::InvalidNodeClass.new(node_class, GQL::Node)
|
17
|
+
end
|
19
18
|
|
20
|
-
|
21
|
-
|
22
|
-
|
19
|
+
Class.new(self).tap do |connection_class|
|
20
|
+
connection_class.node_class = node_class
|
21
|
+
end
|
23
22
|
end
|
24
23
|
end
|
25
24
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
nil
|
25
|
+
def value_of_field(ast_field)
|
26
|
+
if ast_field.name == :edges
|
27
|
+
target.map do |item|
|
28
|
+
node = self.class.node_class.new(ast_field, item, variables, context)
|
29
|
+
node.value
|
32
30
|
end
|
31
|
+
else
|
32
|
+
super
|
33
33
|
end
|
34
34
|
end
|
35
35
|
end
|
data/lib/gql/errors.rb
CHANGED
@@ -1,35 +1,39 @@
|
|
1
1
|
require 'active_support/core_ext/array/conversions'
|
2
|
+
require 'active_support/core_ext/string/inflections'
|
2
3
|
|
3
4
|
module GQL
|
4
5
|
class Error < StandardError
|
5
6
|
end
|
6
7
|
|
7
8
|
module Errors
|
8
|
-
class
|
9
|
+
class UndefinedRoot < Error
|
10
|
+
def initialize
|
11
|
+
super('Root node class is undefined. Define it with `GQL.root_node_class = MyRootNode`.')
|
12
|
+
end
|
9
13
|
end
|
10
14
|
|
11
|
-
class
|
12
|
-
def initialize
|
13
|
-
super(
|
15
|
+
class UndefinedNodeClass < Error
|
16
|
+
def initialize(node_class, name)
|
17
|
+
super("#{node_class} must define a #{name} class. Set it with `self.#{name}_class = My#{method.camelize}Class`.")
|
14
18
|
end
|
15
19
|
end
|
16
20
|
|
17
|
-
class InvalidNodeClass <
|
21
|
+
class InvalidNodeClass < Error
|
18
22
|
def initialize(node_class, super_class)
|
19
|
-
super("#{node_class} must be a subclass of #{super_class}.")
|
23
|
+
super("#{node_class} must be a (subclass of) #{super_class}.")
|
20
24
|
end
|
21
25
|
end
|
22
26
|
|
23
|
-
class
|
27
|
+
class UndefinedFieldType < Error
|
24
28
|
def initialize(name)
|
25
|
-
types =
|
29
|
+
types = GQL.field_types.keys.sort.map { |name| "`#{name}`" }
|
26
30
|
types = types.size > 0 ? " Available types: #{types.to_sentence}." : ''
|
27
31
|
|
28
|
-
super("The field type `#{name}` is
|
32
|
+
super("The field type `#{name}` is undefined. Define it with `GQL.field_types[:#{name}] = My#{name.to_s.camelize}`.#{types}")
|
29
33
|
end
|
30
34
|
end
|
31
35
|
|
32
|
-
class UndefinedCall <
|
36
|
+
class UndefinedCall < Error
|
33
37
|
def initialize(name, node_class)
|
34
38
|
calls = node_class.call_classes.keys.sort.map { |name| "`#{name}`" }
|
35
39
|
calls = calls.size > 0 ? " Available calls: #{calls.to_sentence}." : ''
|
@@ -38,7 +42,7 @@ module GQL
|
|
38
42
|
end
|
39
43
|
end
|
40
44
|
|
41
|
-
class UndefinedField <
|
45
|
+
class UndefinedField < Error
|
42
46
|
def initialize(name, node_class)
|
43
47
|
fields = node_class.field_classes.keys.sort.map { |name| "`#{name}`" }
|
44
48
|
fields = fields.size > 0 ? " Available fields: #{fields.to_sentence}." : ''
|
data/lib/gql/executor.rb
CHANGED
@@ -1,18 +1,19 @@
|
|
1
1
|
module GQL
|
2
2
|
class Executor
|
3
|
+
attr_reader :ast_node, :variables
|
4
|
+
|
3
5
|
def initialize(ast_root)
|
4
|
-
@
|
5
|
-
@variables
|
6
|
+
@ast_node = ast_root.node
|
7
|
+
@variables = ast_root.variables
|
6
8
|
end
|
7
9
|
|
8
10
|
def execute(context = {})
|
9
|
-
|
11
|
+
node_class = GQL.root_node_class
|
10
12
|
|
11
|
-
raise Errors::UndefinedRoot if
|
12
|
-
raise Errors::InvalidNodeClass.new(root_class, Node) unless root_class < Node
|
13
|
+
raise Errors::UndefinedRoot if node_class.nil?
|
13
14
|
|
14
|
-
|
15
|
-
|
15
|
+
node = node_class.new(ast_node, nil, variables, context)
|
16
|
+
node.value
|
16
17
|
end
|
17
18
|
end
|
18
19
|
end
|
data/lib/gql/field.rb
CHANGED
@@ -1,7 +1,31 @@
|
|
1
|
+
require 'active_support/core_ext/class/attribute'
|
2
|
+
|
1
3
|
module GQL
|
2
4
|
class Field < Node
|
3
|
-
|
4
|
-
|
5
|
+
class Method
|
6
|
+
attr_reader :target, :context
|
7
|
+
|
8
|
+
def initialize(target, context)
|
9
|
+
@target, @context = target, context
|
10
|
+
end
|
11
|
+
|
12
|
+
def execute(method)
|
13
|
+
instance_exec(&method)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class_attribute :method, instance_accessor: false, instance_predicate: false
|
18
|
+
|
19
|
+
class << self
|
20
|
+
def build_class(method, connection_class, node_class)
|
21
|
+
Class.new(self).tap do |field_class|
|
22
|
+
field_class.method = method
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def raw_value
|
28
|
+
target
|
5
29
|
end
|
6
30
|
end
|
7
31
|
end
|
@@ -1,22 +1,36 @@
|
|
1
|
+
require 'active_support/core_ext/class/attribute'
|
2
|
+
|
1
3
|
module GQL
|
2
4
|
module Fields
|
3
5
|
class Connection < Field
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
6
|
+
class_attribute :connection_class, instance_accessor: false, instance_predicate: false
|
7
|
+
|
8
|
+
class << self
|
9
|
+
def build_class(method, connection_class, node_class)
|
10
|
+
connection_class ||= self.connection_class
|
11
|
+
|
12
|
+
if connection_class.nil?
|
13
|
+
raise Errors::UndefinedNodeClass.new(self, 'connection')
|
14
|
+
end
|
15
|
+
|
16
|
+
unless connection_class <= GQL::Connection
|
17
|
+
raise Errors::InvalidNodeClass.new(connection_class, GQL::Connection)
|
18
|
+
end
|
19
|
+
|
20
|
+
Class.new(self).tap do |field_class|
|
21
|
+
field_class.method = method
|
22
|
+
field_class.connection_class = connection_class.build_class(node_class)
|
23
|
+
end
|
9
24
|
end
|
10
25
|
end
|
11
26
|
|
12
|
-
def
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
raise Errors::InvalidNodeClass.new(connection_class, GQL::Connection) unless connection_class <= GQL::Connection
|
27
|
+
def value_of_fields(*)
|
28
|
+
connection = self.class.connection_class.new(ast_node, target, variables, context)
|
29
|
+
connection.value
|
30
|
+
end
|
17
31
|
|
18
|
-
|
19
|
-
|
32
|
+
def raw_value
|
33
|
+
nil
|
20
34
|
end
|
21
35
|
end
|
22
36
|
end
|
data/lib/gql/fields/object.rb
CHANGED
@@ -1,17 +1,37 @@
|
|
1
|
+
require 'active_support/core_ext/class/attribute'
|
2
|
+
|
1
3
|
module GQL
|
2
4
|
module Fields
|
3
5
|
class Object < Field
|
4
|
-
|
5
|
-
raise Errors::InvalidNodeClass.new(__node_class__, Node) unless __node_class__ < Node
|
6
|
+
class_attribute :node_class, instance_accessor: false, instance_predicate: false
|
6
7
|
|
7
|
-
|
8
|
-
|
9
|
-
|
8
|
+
class << self
|
9
|
+
def build_class(method, connection_class, node_class)
|
10
|
+
node_class ||= self.node_class
|
11
|
+
|
12
|
+
if node_class.nil?
|
13
|
+
raise Errors::UndefinedNodeClass.new(self, 'node')
|
14
|
+
end
|
10
15
|
|
11
|
-
|
12
|
-
|
13
|
-
|
16
|
+
unless node_class <= GQL::Node
|
17
|
+
raise Errors::InvalidNodeClass.new(node_class, GQL::Node)
|
18
|
+
end
|
19
|
+
|
20
|
+
Class.new(self).tap do |field_class|
|
21
|
+
field_class.method = method
|
22
|
+
field_class.node_class = node_class
|
23
|
+
end
|
14
24
|
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def value_of_fields(*)
|
28
|
+
node = self.class.node_class.new(ast_node, target, variables, context)
|
29
|
+
node.value
|
30
|
+
end
|
31
|
+
|
32
|
+
def raw_value
|
33
|
+
nil
|
34
|
+
end
|
15
35
|
end
|
16
36
|
end
|
17
37
|
end
|