gql 0.0.6 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
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