graphql 0.0.1 → 0.0.2

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 (50) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graphql.rb +78 -9
  3. data/lib/graphql/call.rb +7 -0
  4. data/lib/graphql/connection.rb +44 -0
  5. data/lib/graphql/field.rb +117 -0
  6. data/lib/graphql/field_definer.rb +29 -0
  7. data/lib/graphql/introspection/call_node.rb +13 -0
  8. data/lib/graphql/introspection/connection.rb +9 -0
  9. data/lib/graphql/introspection/field_node.rb +19 -0
  10. data/lib/graphql/introspection/root_call_argument_node.rb +5 -0
  11. data/lib/graphql/introspection/root_call_node.rb +16 -0
  12. data/lib/graphql/introspection/schema_call.rb +8 -0
  13. data/lib/graphql/introspection/schema_node.rb +17 -0
  14. data/lib/graphql/introspection/type_call.rb +8 -0
  15. data/lib/graphql/introspection/type_node.rb +16 -0
  16. data/lib/graphql/node.rb +141 -34
  17. data/lib/graphql/parser.rb +19 -8
  18. data/lib/graphql/query.rb +64 -21
  19. data/lib/graphql/root_call.rb +176 -0
  20. data/lib/graphql/root_call_argument.rb +8 -0
  21. data/lib/graphql/root_call_argument_definer.rb +20 -0
  22. data/lib/graphql/schema.rb +99 -0
  23. data/lib/graphql/syntax/call.rb +3 -12
  24. data/lib/graphql/syntax/field.rb +4 -2
  25. data/lib/graphql/syntax/node.rb +3 -10
  26. data/lib/graphql/syntax/query.rb +7 -0
  27. data/lib/graphql/syntax/variable.rb +7 -0
  28. data/lib/graphql/transform.rb +14 -5
  29. data/lib/graphql/types/boolean_field.rb +3 -0
  30. data/lib/graphql/types/connection_field.rb +30 -0
  31. data/lib/graphql/types/cursor_field.rb +9 -0
  32. data/lib/graphql/types/number_field.rb +3 -0
  33. data/lib/graphql/types/object_field.rb +8 -0
  34. data/lib/graphql/types/string_field.rb +3 -0
  35. data/lib/graphql/types/type_field.rb +6 -0
  36. data/lib/graphql/version.rb +3 -0
  37. data/readme.md +142 -10
  38. data/spec/graphql/field_spec.rb +66 -0
  39. data/spec/graphql/node_spec.rb +68 -0
  40. data/spec/graphql/parser_spec.rb +75 -25
  41. data/spec/graphql/query_spec.rb +185 -83
  42. data/spec/graphql/root_call_spec.rb +55 -0
  43. data/spec/graphql/schema_spec.rb +128 -0
  44. data/spec/graphql/transform_spec.rb +124 -39
  45. data/spec/spec_helper.rb +2 -1
  46. data/spec/support/dummy_app.rb +43 -23
  47. data/spec/support/nodes.rb +145 -32
  48. metadata +78 -16
  49. data/lib/graphql/collection_edge.rb +0 -62
  50. data/lib/graphql/syntax/edge.rb +0 -12
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c86e3ad590a80d6430b2fd781fb097e433d5e5c4
4
- data.tar.gz: 60d41b9666ffaa6d63a73238fd572fdbbba38218
3
+ metadata.gz: 290e890d0a805c3587000af2fbbacf910cf8baf0
4
+ data.tar.gz: 745511749686efb279e0ee7b7af2484d0ccd82ed
5
5
  SHA512:
6
- metadata.gz: 27b1a4571a57b15e35dff3dcab058d8f8b9926c374361a6df52930296a1be705a49a5b345e23ad128d73eb81675a0b798e0b225c0c5fc92904cb76ebf3aaccfe
7
- data.tar.gz: 5594ef107005a1d7abca4e9f6099a6c4f515176679d4421126e7a332a433271f367d447a981263a89accd573b8060ec22b1ee7f71809cb5a8c23e19d534eccee
6
+ metadata.gz: 81ba92e0aed50365b5ccd78ab02c035bc0a1ae212669f7d5c668cbe7fec3e48158f9a53a5859b2ece3ebc54d3634187635477a85cf50f2b8d24a822476517b53
7
+ data.tar.gz: 3a46fb67c848c7b85768979071d28df3b2e21e29087395b5a3ab2297d0660bd51ba84c628678e098a1faae392ef9a4e30d8be0007575ef32468f98c2c043badf
data/lib/graphql.rb CHANGED
@@ -1,26 +1,95 @@
1
- require "parslet"
2
- require "active_support/core_ext/string/inflections"
3
1
  require "active_support/core_ext/object/blank"
2
+ require "active_support/core_ext/string/inflections"
3
+ require "json"
4
+ require "parslet"
5
+ require "singleton"
4
6
 
5
7
  module GraphQL
6
- VERSION = "0.0.1"
7
-
8
- autoload(:CollectionEdge, "graphql/collection_edge")
8
+ autoload(:Call, "graphql/call")
9
+ autoload(:Connection, "graphql/connection")
10
+ autoload(:Field, "graphql/field")
11
+ autoload(:FieldDefiner, "graphql/field_definer")
12
+ autoload(:Node, "graphql/node")
9
13
  autoload(:Parser, "graphql/parser")
10
14
  autoload(:Query, "graphql/query")
11
- autoload(:Node, "graphql/node")
15
+ autoload(:RootCall, "graphql/root_call")
16
+ autoload(:RootCallArgument, "graphql/root_call_argument")
17
+ autoload(:RootCallArgumentDefiner, "graphql/root_call_argument_definer")
18
+ autoload(:Schema, "graphql/schema")
12
19
  autoload(:Transform, "graphql/transform")
20
+ autoload(:VERSION, "graphql/version")
21
+
22
+ module Introspection
23
+ autoload(:CallNode, "graphql/introspection/call_node")
24
+ autoload(:Connection, "graphql/introspection/connection")
25
+ autoload(:FieldNode, "graphql/introspection/field_node")
26
+ autoload(:RootCallArgumentNode, "graphql/introspection/root_call_argument_node")
27
+ autoload(:RootCallNode, "graphql/introspection/root_call_node")
28
+ autoload(:SchemaCall, "graphql/introspection/schema_call")
29
+ autoload(:SchemaNode, "graphql/introspection/schema_node")
30
+ autoload(:TypeCall, "graphql/introspection/type_call")
31
+ autoload(:TypeNode, "graphql/introspection/type_node")
32
+ end
33
+
13
34
 
14
35
  module Syntax
15
36
  autoload(:Call, "graphql/syntax/call")
16
- autoload(:Edge, "graphql/syntax/edge")
17
37
  autoload(:Field, "graphql/syntax/field")
38
+ autoload(:Query, "graphql/syntax/query")
18
39
  autoload(:Node, "graphql/syntax/node")
40
+ autoload(:Variable, "graphql/syntax/variable")
41
+ end
42
+
43
+ module Types
44
+ autoload(:BooleanField, "graphql/types/boolean_field")
45
+ autoload(:ConnectionField, "graphql/types/connection_field")
46
+ autoload(:CursorField, "graphql/types/cursor_field")
47
+ autoload(:NumberField, "graphql/types/number_field")
48
+ autoload(:ObjectField, "graphql/types/object_field")
49
+ autoload(:StringField, "graphql/types/string_field")
50
+ autoload(:TypeField, "graphql/types/type_field")
51
+ end
52
+
53
+ class Error < RuntimeError; end
54
+ class FieldNotDefinedError < Error
55
+ def initialize(class_name, field_name)
56
+ super("#{class_name}##{field_name} was requested, but it isn't defined. Defined fields are: #{SCHEMA.field_names}")
57
+ end
58
+ end
59
+ class NodeNotDefinedError < Error
60
+ def initialize(node_name)
61
+ super("#{node_name} was requested but was not found. Defined nodes are: #{SCHEMA.type_names}")
62
+ end
63
+ end
64
+ class ConnectionNotDefinedError < Error
65
+ def initialize(node_name)
66
+ super("#{node_name} was requested but was not found. Defined connections are: #{SCHEMA.connection_names}")
67
+ end
68
+ end
69
+ class RootCallNotDefinedError < Error
70
+ def initialize(name)
71
+ super("Call '#{name}' was requested but was not found. Defined calls are: #{SCHEMA.call_names}")
72
+ end
73
+ end
74
+ class SyntaxError < Error
75
+ def initialize(line, col, string)
76
+ lines = string.split("\n")
77
+ super("Syntax Error at (#{line}, #{col}), check usage: #{string}")
78
+ end
79
+ end
80
+
81
+ class RootCallArgumentError < Error
82
+ def initialize(declaration, actual)
83
+ super("Wrong type for #{declaration.name}: expected a #{declaration.type} but got #{actual}")
84
+ end
19
85
  end
20
86
 
21
87
  PARSER = Parser.new
88
+ SCHEMA = Schema.instance
22
89
  TRANSFORM = Transform.new
23
-
24
- class FieldNotDefinedError < RuntimeError
90
+ # preload these so they're in SCHEMA
91
+ ["types", "introspection"].each do |preload_dir|
92
+ Dir["#{File.dirname(__FILE__)}/graphql/#{preload_dir}/*.rb"].each { |f| require f }
25
93
  end
94
+ Node.field.__type__(:__type__)
26
95
  end
@@ -0,0 +1,7 @@
1
+ class GraphQL::Call
2
+ attr_reader :name, :lambda
3
+ def initialize(name:, lambda:)
4
+ @name = name
5
+ @lambda = lambda
6
+ end
7
+ end
@@ -0,0 +1,44 @@
1
+ class GraphQL::Connection < GraphQL::Node
2
+ exposes "Array"
3
+ field.any(:edges)
4
+
5
+ attr_reader :calls, :syntax_fields, :query
6
+
7
+ def initialize(items, query:, fields: [])
8
+ @target = items
9
+ @syntax_fields = fields
10
+ @query = query
11
+ end
12
+
13
+ def items
14
+ @target
15
+ end
16
+
17
+ def edge_fields
18
+ @edge_fields ||= syntax_fields.find { |f| f.identifier == "edges" }.fields
19
+ end
20
+
21
+ def edges
22
+ raise "#{self.class} expected a connection, but got `nil`" if items.nil?
23
+ items.map do |item|
24
+ node_class = GraphQL::SCHEMA.type_for_object(item)
25
+ node = node_class.new(item, fields: edge_fields, query: query)
26
+ res = node.as_result
27
+ res
28
+ end
29
+ end
30
+
31
+ class << self
32
+ def default_schema_name
33
+ name.split("::").last.sub(/Connection$/, '').underscore
34
+ end
35
+
36
+ attr_accessor :default_connection
37
+ def default_connection!
38
+ GraphQL::Connection.default_connection = self
39
+ end
40
+
41
+ end
42
+
43
+ self.default_connection!
44
+ end
@@ -0,0 +1,117 @@
1
+ class GraphQL::Field
2
+ attr_reader :query, :owner, :calls, :fields
3
+ def initialize(query: nil, owner: nil, calls: [], fields: [])
4
+ @query = query
5
+ @owner = owner
6
+ @calls = calls
7
+ @fields = fields
8
+ end
9
+
10
+ def raw_value
11
+ owner.send(method)
12
+ end
13
+
14
+ def as_result
15
+ finished_value
16
+ end
17
+
18
+ def finished_value
19
+ @finished_value ||= begin
20
+ val = raw_value
21
+ calls.each do |call|
22
+ registered_call = self.class.calls[call.identifier]
23
+ if registered_call.nil?
24
+ raise "Call not found: #{self.class.name}##{call.identifier}"
25
+ end
26
+ val = registered_call.lambda.call(val, *call.arguments)
27
+ end
28
+ val
29
+ end
30
+ end
31
+
32
+ # instance `const_get` reaches up to class namespace
33
+ def const_get(const_name)
34
+ self.class.const_get(const_name)
35
+ rescue
36
+ nil
37
+ end
38
+
39
+ # delegate to class constant
40
+ ["name", "description"].each do |method_name|
41
+ define_method(method_name) do
42
+ const_get(method_name.upcase)
43
+ end
44
+ end
45
+
46
+ def method; name; end
47
+
48
+ class << self
49
+ def inherited(child_class)
50
+ GraphQL::SCHEMA.add_field(child_class)
51
+ end
52
+
53
+ def create_class(name:, owner_class:, type:, description: nil, connection_class_name: nil, node_class_name: nil)
54
+ if type.is_a?(Symbol)
55
+ type = GraphQL::SCHEMA.get_field(type)
56
+ end
57
+
58
+ field_superclass = type || self
59
+ new_class = Class.new(field_superclass)
60
+ new_class.const_set :NAME, name
61
+ new_class.const_set :OWNER_CLASS, owner_class
62
+ new_class.const_set :DESCRIPTION , description
63
+ new_class.const_set :CONNECTION_CLASS_NAME, connection_class_name
64
+ new_class.const_set :NODE_CLASS_NAME, node_class_name
65
+ new_class
66
+ end
67
+
68
+ def to_s
69
+ if const_defined?(:NAME)
70
+ "<FieldClass: #{const_get(:OWNER_CLASS).name}::#{const_get(:NAME)}>"
71
+ else
72
+ super
73
+ end
74
+ end
75
+
76
+ def type(value_type_name)
77
+ @value_type = value_type_name.to_s
78
+ GraphQL::SCHEMA.add_field(self)
79
+ end
80
+
81
+ def value_type
82
+ @value_type || superclass.value_type
83
+ end
84
+
85
+ def schema_name
86
+ @value_type || (name.present? ? default_schema_name : nil)
87
+ end
88
+
89
+ def default_schema_name
90
+ name.split("::").last.sub(/Field$/, '').underscore
91
+ end
92
+
93
+ def field_name
94
+ const_get(:NAME)
95
+ end
96
+
97
+ def description
98
+ const_get(:DESCRIPTION)
99
+ end
100
+
101
+ def calls
102
+ superclass.calls.merge(_calls)
103
+ rescue NoMethodError
104
+ {}
105
+ end
106
+
107
+ def _calls
108
+ @calls ||= {}
109
+ end
110
+
111
+ def call(name, lambda)
112
+ _calls[name.to_s] = GraphQL::Call.new(name: name.to_s, lambda: lambda)
113
+ end
114
+ end
115
+
116
+ type :any
117
+ end
@@ -0,0 +1,29 @@
1
+ class GraphQL::FieldDefiner
2
+ attr_reader :owner_class
3
+ def initialize(owner_class)
4
+ @owner_class = owner_class
5
+ end
6
+
7
+ def method_missing(method_name, *args, &block)
8
+ type = GraphQL::SCHEMA.get_field(method_name)
9
+ if type.present?
10
+ create_field(args[0], type: type, description: args[1])
11
+ else
12
+ super
13
+ end
14
+ end
15
+
16
+ def create_field(field_name, type: nil, description: nil)
17
+ field_name = field_name.to_s
18
+ field_class = GraphQL::Field.create_class({
19
+ name: field_name,
20
+ type: type,
21
+ owner_class: owner_class,
22
+ description: description,
23
+ })
24
+
25
+ field_class_name = field_name.camelize + "Field"
26
+ owner_class.const_set(field_class_name, field_class)
27
+ owner_class.own_fields[field_name] = field_class
28
+ end
29
+ end
@@ -0,0 +1,13 @@
1
+ class GraphQL::Introspection::CallNode < GraphQL::Node
2
+ exposes "GraphQL::Call"
3
+ field.string(:name)
4
+ field.string(:arguments)
5
+
6
+ def arguments
7
+ args = target.lambda.parameters
8
+ args.shift
9
+ args.map { |p| "#{p[1]} (#{p[0]})" }.join(", ")
10
+ rescue StandardError => e
11
+ raise "Errored on #{name} (#{self}): #{e}"
12
+ end
13
+ end
@@ -0,0 +1,9 @@
1
+ class GraphQL::Introspection::Connection < GraphQL::Connection
2
+ type :introspection_connection
3
+
4
+ field.number(:count)
5
+
6
+ def count
7
+ size
8
+ end
9
+ end
@@ -0,0 +1,19 @@
1
+ class GraphQL::Introspection::FieldNode < GraphQL::Node
2
+ exposes "GraphQL::Field"
3
+ field.string(:name)
4
+ field.string(:type)
5
+ field.string(:description)
6
+ field.connection(:calls)
7
+
8
+ def calls
9
+ target.calls.values
10
+ end
11
+
12
+ def name
13
+ schema_name
14
+ end
15
+
16
+ def type
17
+ value_type
18
+ end
19
+ end
@@ -0,0 +1,5 @@
1
+ class GraphQL::Introspection::RootCallArgumentNode < GraphQL::Node
2
+ exposes "GraphQL::RootCallArgument"
3
+ field.string(:name)
4
+ field.string(:type)
5
+ end
@@ -0,0 +1,16 @@
1
+ class GraphQL::Introspection::RootCallNode < GraphQL::Node
2
+ exposes "GraphQL::RootCall"
3
+ field.string(:name)
4
+ field.string(:returns)
5
+ field.connection(:arguments)
6
+
7
+
8
+ def returns
9
+ return_declarations = @target.return_declarations
10
+ return_declarations.keys.map(&:to_s)
11
+ end
12
+
13
+ def name
14
+ schema_name
15
+ end
16
+ end
@@ -0,0 +1,8 @@
1
+ class GraphQL::Introspection::SchemaCall < GraphQL::RootCall
2
+ returns :schema
3
+ argument.none
4
+
5
+ def execute!
6
+ GraphQL::SCHEMA
7
+ end
8
+ end
@@ -0,0 +1,17 @@
1
+ class GraphQL::Introspection::SchemaNode < GraphQL::Node
2
+ exposes "GraphQL::Schema"
3
+ field.connection(:calls)
4
+ field.connection(:types)
5
+
6
+ def cursor
7
+ "schema"
8
+ end
9
+
10
+ def types
11
+ @target.types.values
12
+ end
13
+
14
+ def calls
15
+ target.calls.values
16
+ end
17
+ end
@@ -0,0 +1,8 @@
1
+ class GraphQL::Introspection::TypeCall < GraphQL::RootCall
2
+ returns :type
3
+ argument.string("type_name")
4
+
5
+ def execute!(type_name)
6
+ GraphQL::SCHEMA.get_type(type_name)
7
+ end
8
+ end
@@ -0,0 +1,16 @@
1
+ class GraphQL::Introspection::TypeNode < GraphQL::Node
2
+ exposes "GraphQL::Node"
3
+ field.string(:name)
4
+ field.string(:description)
5
+ field.connection(:fields)
6
+
7
+ cursor :name
8
+
9
+ def fields
10
+ target.all_fields.values
11
+ end
12
+
13
+ def name
14
+ schema_name
15
+ end
16
+ end