gql 0.0.6 → 0.0.7

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 20b402e587fe168bb43d6b683c6f41d5d313d828
4
- data.tar.gz: 5b5a28f87cce7e5e72df1ef7d366ae06689b269a
3
+ metadata.gz: c91705621b9dd8b0515c2d72859f30af612d5f24
4
+ data.tar.gz: d5f410d2acdc7c18f9832058db5c70fd720f7b08
5
5
  SHA512:
6
- metadata.gz: 1ce7cd9bf3934dbc32adf6c6d1c69db6f1c8cbe78cdc8a0eb3aefcaa7a9f2511a385ffab599a2fe1bac156df59249fdcecaf73f40b2daf43ffd9e5fb5b5280ce
7
- data.tar.gz: 910724d32eb06b282a067955789b2ff554dbe1034911a6e033e18720ab2b140126e85548e2d6c23e1b06fc1f48e77977d6384a712e8d70c0023f505de534f24e
6
+ metadata.gz: 73cb1ed1e7bb3cdbdb4cb6cbe9eea50c8eeec8aac68d708f9bac42e9feb887bc44a03ed477b2f51da659b370c1bea5a2192490730885eaa33d2d9cee10225e73
7
+ data.tar.gz: 2b41f407270151041915c441b9ff6ad790298d43ea925af074b4a7405d3b5475c27db40865c62e37d50d84e4d0fd373d6cd5cd5d26c82c86a9e126f32cb6adc6
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # gql
2
2
 
3
- An attempted implementation of Facebook's yet-to-be-released GraphQL specification, heavily inspired by [graphql-ruby](https://github.com/rmosolgo/graphql-ruby), but with other/more/less features/bugs.
3
+ A Ruby implementation of Facebook's yet-to-be-released GraphQL specification, inspired by [graphql-ruby](https://github.com/rmosolgo/graphql-ruby), but with other/more/less features/bugs.
4
4
 
5
5
  Caution! This is pre-alpha software. Use at your own risk.
6
6
 
data/Rakefile CHANGED
@@ -1,10 +1,6 @@
1
1
  require 'bundler/gem_tasks'
2
2
  require 'rake/testtask'
3
3
 
4
- Rake::TestTask.new :test => :compile do |t|
5
- t.libs << 'test'
6
- end
7
-
8
4
  file 'lib/gql/tokenizer.rb' => 'lib/gql/tokenizer.rex' do |t|
9
5
  sh "bundle exec rex #{t.prerequisites.first} --output-file #{t.name}"
10
6
  end
@@ -19,4 +15,8 @@ end
19
15
 
20
16
  task :compile => ['lib/gql/tokenizer.rb', 'lib/gql/parser.rb']
21
17
 
18
+ Rake::TestTask.new :test => :compile do |t|
19
+ t.libs << 'test'
20
+ end
21
+
22
22
  task :default => :test
data/gql.gemspec CHANGED
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
10
10
  spec.authors = ['Martin Andert']
11
11
  spec.email = ['mandert@gmail.com']
12
12
 
13
- spec.summary = 'An attempted Ruby implementation of Facebook\'s yet-to-be-released GraphQL specification.'
13
+ spec.summary = 'A Ruby implementation of Facebook\'s yet-to-be-released GraphQL specification.'
14
14
  spec.homepage = 'https://github.com/martinandert/gql'
15
15
  spec.license = 'MIT'
16
16
 
data/lib/gql.rb CHANGED
@@ -54,9 +54,9 @@ module GQL
54
54
  end
55
55
 
56
56
  def execute(input, context = {})
57
- ast = parse(input)
57
+ query = parse(input)
58
58
 
59
- executor = Executor.new(ast)
59
+ executor = Executor.new(query)
60
60
  executor.execute context
61
61
  end
62
62
 
data/lib/gql/array.rb CHANGED
@@ -5,27 +5,19 @@ module GQL
5
5
  class_attribute :item_class, instance_accessor: false, instance_predicate: false
6
6
 
7
7
  class << self
8
- def build_class(id, method, options = {})
8
+ def build_class(id, proc, options = {})
9
9
  item_class = options[:item_class] || self.item_class
10
10
 
11
- if item_class.nil?
12
- raise Errors::UndefinedNodeClass.new(self, 'item')
13
- end
14
-
15
- unless item_class <= Node
16
- raise Errors::InvalidNodeClass.new(item_class, Node)
17
- end
11
+ Node.validate_is_subclass! item_class, 'item'
18
12
 
19
13
  Class.new(self).tap do |field_class|
20
14
  field_class.id = id.to_s
21
- field_class.method = method
15
+ field_class.proc = proc
22
16
  field_class.item_class = item_class
23
17
  end
24
18
  end
25
19
  end
26
20
 
27
- call :size, Number, -> { target.size }
28
-
29
21
  def value
30
22
  target.map do |item|
31
23
  node = self.class.item_class.new(ast_node, item, variables, context)
data/lib/gql/call.rb CHANGED
@@ -1,70 +1,37 @@
1
+ require 'active_support/core_ext/class/attribute'
2
+
1
3
  module GQL
2
4
  class Call
3
- class Method
4
- attr_reader :target, :context
5
-
6
- def initialize(target, context)
7
- @target, @context = target, context
8
- end
5
+ class_attribute :result_class, :proc, instance_accessor: false, instance_predicate: false
9
6
 
10
- def execute(method, args)
11
- instance_exec(*args, &method)
7
+ class << self
8
+ def returns(result_class)
9
+ self.result_class = result_class
12
10
  end
13
11
  end
14
12
 
15
- class_attribute :id, :result_class, :method, instance_accessor: false, instance_predicate: false
16
-
17
- class << self
18
- def build_class(id, result_class, method)
19
- if result_class.is_a? ::Array
20
- if result_class.size == 1
21
- result_class.unshift GQL.default_list_class || Connection
22
- end
23
-
24
- list_class, item_class = result_class
25
-
26
- unless list_class <= Connection
27
- raise Errors::InvalidNodeClass.new(list_class, Connection)
28
- end
29
-
30
- unless item_class <= Node
31
- raise Errors::InvalidNodeClass.new(list_class, Node)
32
- end
13
+ attr_reader :target, :context
33
14
 
34
- options = { list_class: list_class, item_class: item_class }
35
- result_class = Connection.build_class(:result, nil, options)
36
- elsif result_class && !(result_class <= Node)
37
- raise Errors::InvalidNodeClass.new(result_class, Node)
38
- end
39
-
40
- Class.new(self).tap do |call_class|
41
- call_class.id = id.to_s
42
- call_class.method = method
43
- call_class.result_class = result_class
44
- end
45
- end
15
+ def initialize(target, context)
16
+ @target, @context = target, context
46
17
  end
47
18
 
48
- attr_reader :caller, :ast_node, :target, :variables, :context
49
-
50
- def initialize(caller, ast_node, target, variables, context)
51
- @caller, @ast_node, @target = caller, ast_node, target
52
- @variables, @context = variables, context
19
+ def execute(*)
20
+ raise NotImplementedError, 'override in subclass'
53
21
  end
54
22
 
55
- def execute
56
- args = substitute_variables(ast_node.arguments)
23
+ def result_for(caller_class, ast_node, variables)
24
+ args = substitute_variables(ast_node.arguments, variables)
25
+ target = execute(*args)
57
26
 
58
- method = Method.new(target, context)
59
- target = method.execute(self.class.method, args)
60
- result_class = self.class.result_class || caller.class
27
+ result_class = self.class.result_class || caller_class
61
28
 
62
29
  result = result_class.new(ast_node, target, variables, context)
63
30
  result.value
64
31
  end
65
32
 
66
33
  private
67
- def substitute_variables(args)
34
+ def substitute_variables(args, variables)
68
35
  args.map { |arg| arg.is_a?(::Symbol) ? variables[arg] : arg }
69
36
  end
70
37
  end
data/lib/gql/config.rb CHANGED
@@ -10,7 +10,7 @@ module GQL
10
10
  end
11
11
 
12
12
  if ENV['DEBUG']
13
- value.call :_schema, Schema::Node, -> { context[:_schema_root] }
13
+ value.call :_schema, -> { context[:_schema_root] }, returns: Schema::Node
14
14
  end
15
15
 
16
16
  @@root_node_class = value
@@ -6,33 +6,18 @@ module GQL
6
6
  class_attribute :item_class, instance_accessor: false, instance_predicate: false
7
7
 
8
8
  class << self
9
- def build_class(id, method, options = {})
9
+ def build_class(id, proc, options = {})
10
10
  list_class = options[:list_class] || self.list_class || GQL.default_list_class
11
11
  item_class = options[:item_class] || self.item_class
12
12
 
13
- if list_class.nil?
14
- raise Errors::UndefinedNodeClass.new(self, 'list')
15
- end
16
-
17
- unless list_class <= Connection
18
- raise Errors::InvalidNodeClass.new(list_class, Connection)
19
- end
20
-
21
- if item_class.nil?
22
- raise Errors::UndefinedNodeClass.new(self, 'item')
23
- end
24
-
25
- unless item_class <= Node
26
- raise Errors::InvalidNodeClass.new(item_class, Node)
27
- end
13
+ Connection.validate_is_subclass! list_class, 'list'
14
+ Node.validate_is_subclass! item_class, 'item'
28
15
 
29
16
  Class.new(list_class).tap do |field_class|
30
17
  field_class.id = id.to_s
31
- field_class.method = method
18
+ field_class.proc = proc
32
19
 
33
- field_class.array :edges, item_class: item_class do
34
- target
35
- end
20
+ field_class.array :edges, -> { target }, item_class: item_class
36
21
  end
37
22
  end
38
23
  end
data/lib/gql/executor.rb CHANGED
@@ -1,10 +1,10 @@
1
1
  module GQL
2
2
  class Executor
3
- attr_reader :ast_node, :variables
3
+ attr_reader :ast_root, :variables
4
4
 
5
- def initialize(ast_root)
6
- @ast_node = ast_root.node
7
- @variables = ast_root.variables
5
+ def initialize(ast_query)
6
+ @ast_root = ast_query.root
7
+ @variables = ast_query.variables
8
8
  end
9
9
 
10
10
  def execute(context = {})
@@ -14,7 +14,7 @@ module GQL
14
14
 
15
15
  context[:_schema_root] = node_class if ENV['DEBUG']
16
16
 
17
- node = node_class.new(ast_node, nil, variables, context)
17
+ node = node_class.new(ast_root, nil, variables, context)
18
18
  node.value
19
19
  end
20
20
  end
data/lib/gql/field.rb CHANGED
@@ -2,25 +2,13 @@ require 'active_support/core_ext/class/attribute'
2
2
 
3
3
  module GQL
4
4
  class Field < Node
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 :id, :method, instance_accessor: false, instance_predicate: false
5
+ class_attribute :id, :proc, instance_accessor: false, instance_predicate: false
18
6
 
19
7
  class << self
20
- def build_class(id, method, options = {})
8
+ def build_class(id, proc, options = {})
21
9
  Class.new(self).tap do |field_class|
22
10
  field_class.id = id.to_s
23
- field_class.method = method
11
+ field_class.proc = proc
24
12
  end
25
13
  end
26
14
  end
data/lib/gql/node.rb CHANGED
@@ -1,57 +1,116 @@
1
1
  require 'active_support/core_ext/class/attribute'
2
2
  require 'active_support/core_ext/string/inflections'
3
3
  require 'active_support/core_ext/array/extract_options'
4
+ require 'active_support/core_ext/object/try'
4
5
 
5
6
  module GQL
6
7
  class Node
8
+ class ExecutionContext
9
+ attr_reader :target, :context
10
+
11
+ def initialize(target, context)
12
+ @target, @context = target, context
13
+ end
14
+
15
+ def execute(method, args = [])
16
+ instance_exec(*args, &method)
17
+ end
18
+ end
19
+
7
20
  class_attribute :calls, :fields, instance_accessor: false, instance_predicate: false
8
21
 
9
22
  self.calls = {}
10
23
  self.fields = {}
11
24
 
12
25
  class << self
13
- def call(id, result_class = nil, body = nil)
14
- if body.nil? && result_class.is_a?(Proc)
15
- body = result_class
16
- result_class = nil
17
- end
26
+ def call(id, *args)
27
+ if id.is_a? Hash
28
+ id.each do |id, call_class|
29
+ call id, call_class
30
+ end
31
+ else
32
+ options = args.extract_options!
18
33
 
19
- body ||= lambda { |*args| target.public_send(id, *args) }
34
+ proc_or_class = args.shift || -> (*args) { target.public_send(id, *args) }
35
+ result_class = options[:returns] || proc_or_class.try(:result_class)
20
36
 
21
- call_class = Call.build_class(id, result_class, body)
37
+ if result_class.is_a? ::Array
38
+ if result_class.size == 1
39
+ result_class.unshift GQL.default_list_class || Connection
40
+ end
22
41
 
23
- self.const_set "#{id.to_s.camelize}Call", call_class
24
- self.calls = calls.merge(id.to_sym => call_class)
25
- end
42
+ options = {
43
+ list_class: result_class.first,
44
+ item_class: result_class.last
45
+ }
26
46
 
27
- def field(*ids, &block)
28
- options = ids.extract_options!
47
+ result_class = Connection.build_class(:result, nil, options)
48
+ elsif result_class
49
+ Node.validate_is_subclass! result_class, 'result'
50
+ end
29
51
 
30
- ids.each do |id|
31
- method = block || lambda { target.public_send(id) }
32
- field_type = options.delete(:type) || Field
52
+ call_class =
53
+ if proc_or_class.is_a? Proc
54
+ Class.new(Call).tap do |call_class|
55
+ call_class.class_eval do
56
+ self.proc = proc_or_class
33
57
 
34
- unless field_type <= Field
35
- raise Errors::InvalidNodeClass.new(field_type, Field)
36
- end
58
+ def execute(*args)
59
+ instance_exec(*args, &self.class.proc)
60
+ end
61
+ end
62
+
63
+ self.const_set "#{id.to_s.camelize}Call", call_class
64
+ end
65
+ else
66
+ proc_or_class
67
+ end
37
68
 
38
- field_class = field_type.build_class(id, method, options)
69
+ call_class.result_class = result_class
39
70
 
71
+ self.calls = calls.merge(id.to_sym => call_class)
72
+ end
73
+ end
74
+
75
+ def field(id, *args)
76
+ options = args.extract_options!
77
+ proc = args.shift || -> { target.public_send(id) }
78
+ type = options.delete(:type) || Field
79
+
80
+ Field.validate_is_subclass! type, 'type'
81
+
82
+ type.build_class(id, proc, options).tap do |field_class|
40
83
  self.const_set "#{id.to_s.camelize}Field", field_class
41
84
  self.fields = fields.merge(id.to_sym => field_class)
42
85
  end
43
86
  end
44
87
 
45
- def cursor(id = nil, &block)
46
- body = id ? -> { target.public_send(id) } : block
47
- field :cursor, { type: Simple }, &body
88
+ def cursor(id_or_proc)
89
+ id = id_or_proc.is_a?(Proc) ? nil : id_or_proc
90
+ proc = id ? -> { target.public_send(id) } : id_or_proc
91
+
92
+ field :cursor, proc, type: Simple
93
+ end
94
+
95
+ def validate_is_subclass!(subclass, name)
96
+ if subclass.nil?
97
+ raise Errors::UndefinedNodeClass.new(self, name)
98
+ end
99
+
100
+ unless subclass <= self
101
+ raise Errors::InvalidNodeClass.new(subclass, self)
102
+ end
103
+ end
104
+
105
+ def respond_to?(method, *args)
106
+ GQL.field_types.has_key?(method) || super
48
107
  end
49
108
 
50
- def method_missing(method, *ids, &block)
51
- if field_type = GQL.field_types[method]
52
- options = ids.extract_options!
109
+ def method_missing(method, *args, &block)
110
+ if type = GQL.field_types[method]
111
+ options = args.extract_options!.merge(type: type)
53
112
 
54
- field(*ids, options.merge(type: field_type), &block)
113
+ field(*args.push(options), &block)
55
114
  else
56
115
  super
57
116
  end
@@ -84,15 +143,15 @@ module GQL
84
143
  raise Errors::UndefinedCall.new(ast_call.id, self.class)
85
144
  end
86
145
 
87
- call = call_class.new(self, ast_call, target, variables, context)
88
- call.execute
146
+ call = call_class.new(target, context)
147
+ call.result_for self.class, ast_call, variables
89
148
  end
90
149
 
91
150
  def value_of_fields(ast_fields)
92
- ast_fields.reduce({}) do |memo, ast_field|
151
+ ast_fields.reduce({}) do |result, ast_field|
93
152
  key = ast_field.alias_id || ast_field.id
94
153
 
95
- memo.merge key => value_of_field(ast_field)
154
+ result.merge key => value_of_field(ast_field)
96
155
  end
97
156
  end
98
157
 
@@ -108,8 +167,8 @@ module GQL
108
167
  raise Errors::UndefinedField.new(ast_field.id, self.class)
109
168
  end
110
169
 
111
- method = Field::Method.new(target, context)
112
- target = method.execute(field_class.method)
170
+ method = ExecutionContext.new(target, context)
171
+ target = method.execute(field_class.proc)
113
172
 
114
173
  field = field_class.new(ast_field, target, variables, context)
115
174
  field.value