graphql 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/graphql.rb +78 -9
- data/lib/graphql/call.rb +7 -0
- data/lib/graphql/connection.rb +44 -0
- data/lib/graphql/field.rb +117 -0
- data/lib/graphql/field_definer.rb +29 -0
- data/lib/graphql/introspection/call_node.rb +13 -0
- data/lib/graphql/introspection/connection.rb +9 -0
- data/lib/graphql/introspection/field_node.rb +19 -0
- data/lib/graphql/introspection/root_call_argument_node.rb +5 -0
- data/lib/graphql/introspection/root_call_node.rb +16 -0
- data/lib/graphql/introspection/schema_call.rb +8 -0
- data/lib/graphql/introspection/schema_node.rb +17 -0
- data/lib/graphql/introspection/type_call.rb +8 -0
- data/lib/graphql/introspection/type_node.rb +16 -0
- data/lib/graphql/node.rb +141 -34
- data/lib/graphql/parser.rb +19 -8
- data/lib/graphql/query.rb +64 -21
- data/lib/graphql/root_call.rb +176 -0
- data/lib/graphql/root_call_argument.rb +8 -0
- data/lib/graphql/root_call_argument_definer.rb +20 -0
- data/lib/graphql/schema.rb +99 -0
- data/lib/graphql/syntax/call.rb +3 -12
- data/lib/graphql/syntax/field.rb +4 -2
- data/lib/graphql/syntax/node.rb +3 -10
- data/lib/graphql/syntax/query.rb +7 -0
- data/lib/graphql/syntax/variable.rb +7 -0
- data/lib/graphql/transform.rb +14 -5
- data/lib/graphql/types/boolean_field.rb +3 -0
- data/lib/graphql/types/connection_field.rb +30 -0
- data/lib/graphql/types/cursor_field.rb +9 -0
- data/lib/graphql/types/number_field.rb +3 -0
- data/lib/graphql/types/object_field.rb +8 -0
- data/lib/graphql/types/string_field.rb +3 -0
- data/lib/graphql/types/type_field.rb +6 -0
- data/lib/graphql/version.rb +3 -0
- data/readme.md +142 -10
- data/spec/graphql/field_spec.rb +66 -0
- data/spec/graphql/node_spec.rb +68 -0
- data/spec/graphql/parser_spec.rb +75 -25
- data/spec/graphql/query_spec.rb +185 -83
- data/spec/graphql/root_call_spec.rb +55 -0
- data/spec/graphql/schema_spec.rb +128 -0
- data/spec/graphql/transform_spec.rb +124 -39
- data/spec/spec_helper.rb +2 -1
- data/spec/support/dummy_app.rb +43 -23
- data/spec/support/nodes.rb +145 -32
- metadata +78 -16
- data/lib/graphql/collection_edge.rb +0 -62
- data/lib/graphql/syntax/edge.rb +0 -12
data/lib/graphql/node.rb
CHANGED
@@ -1,61 +1,168 @@
|
|
1
|
+
# Node is the base class for your GraphQL nodes.
|
2
|
+
# It's essentially a delegator that only delegates methods you whitelist with {.field}.
|
3
|
+
# To use it:
|
4
|
+
#
|
5
|
+
# - Extend `GraphQL::Node`
|
6
|
+
# - Declare what this node will wrap with {.exposes}
|
7
|
+
# - Declare fields with {.field}
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# class PostNode < GraphQL::Node
|
11
|
+
# exposes('Post')
|
12
|
+
#
|
13
|
+
# cursor(:id)
|
14
|
+
#
|
15
|
+
# field.number(:id)
|
16
|
+
# field.string(:title)
|
17
|
+
# field.string(:content)
|
18
|
+
# field.connection(:comments)
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
|
1
22
|
class GraphQL::Node
|
2
|
-
|
23
|
+
# The object wrapped by this `Node`
|
24
|
+
attr_reader :target
|
25
|
+
# Fields parsed from the query string
|
26
|
+
attr_reader :syntax_fields
|
27
|
+
# The query to which this `Node` belongs. Used for accessing its {Query#context}.
|
28
|
+
attr_reader :query
|
3
29
|
|
4
|
-
def initialize(target=nil)
|
5
|
-
# DONT EXPOSE Node#target! otherwise you might be able to access it
|
30
|
+
def initialize(target=nil, fields:, query:)
|
6
31
|
@target = target
|
32
|
+
@query = query
|
33
|
+
@syntax_fields = fields
|
7
34
|
end
|
8
35
|
|
9
|
-
|
10
|
-
|
11
|
-
|
36
|
+
# If the target responds to `method_name`, send it to target.
|
37
|
+
def method_missing(method_name, *args, &block)
|
38
|
+
if target.respond_to?(method_name)
|
39
|
+
target.public_send(method_name, *args, &block)
|
12
40
|
else
|
13
|
-
|
41
|
+
super
|
14
42
|
end
|
15
43
|
end
|
16
44
|
|
17
|
-
|
45
|
+
# Looks up {#syntax_fields} against this node and returns the results
|
46
|
+
def as_result
|
18
47
|
json = {}
|
19
|
-
|
20
|
-
|
21
|
-
if
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
48
|
+
syntax_fields.each do |syntax_field|
|
49
|
+
key_name = syntax_field.alias_name || syntax_field.identifier
|
50
|
+
if key_name == 'node'
|
51
|
+
clone_node = self.class.new(target, fields: syntax_field.fields, query: query)
|
52
|
+
json[key_name] = clone_node.as_result
|
53
|
+
elsif key_name == 'cursor'
|
54
|
+
json[key_name] = cursor
|
55
|
+
else
|
56
|
+
field = get_field(syntax_field)
|
57
|
+
json[key_name] = field.as_result
|
28
58
|
end
|
29
59
|
end
|
30
60
|
json
|
31
61
|
end
|
32
62
|
|
33
|
-
|
34
|
-
def
|
35
|
-
|
63
|
+
# The object passed to {Query#initialize} as `context`.
|
64
|
+
def context
|
65
|
+
query.context
|
36
66
|
end
|
37
67
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
68
|
+
|
69
|
+
class << self
|
70
|
+
# Registers this node in {GraphQL::SCHEMA}
|
71
|
+
def inherited(child_class)
|
72
|
+
# use name to prevent autoloading Connection
|
73
|
+
if child_class.ancestors.map(&:name).include?("GraphQL::Connection")
|
74
|
+
GraphQL::SCHEMA.add_connection(child_class)
|
42
75
|
end
|
43
76
|
end
|
44
|
-
end
|
45
77
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
78
|
+
# @param [String] class_name name of the class this node will wrap.
|
79
|
+
def exposes(ruby_class_name)
|
80
|
+
@ruby_class_name = ruby_class_name
|
81
|
+
GraphQL::SCHEMA.add_type(self)
|
82
|
+
end
|
83
|
+
|
84
|
+
# The name of the class wrapped by this node
|
85
|
+
def ruby_class_name
|
86
|
+
@ruby_class_name
|
87
|
+
end
|
88
|
+
|
89
|
+
# @param [String] describe
|
90
|
+
# Provide a description for this node which will be accessible from {SCHEMA}
|
91
|
+
def desc(describe)
|
92
|
+
@description = describe
|
93
|
+
end
|
94
|
+
|
95
|
+
# The description of this node
|
96
|
+
def description
|
97
|
+
@description || raise("#{name}.description isn't defined")
|
98
|
+
end
|
99
|
+
|
100
|
+
# @param [String] type_name
|
101
|
+
# Declares an alternative name to use in {GraphQL::SCHEMA}
|
102
|
+
def type(type_name)
|
103
|
+
@type_name = type_name.to_s
|
104
|
+
GraphQL::SCHEMA.add_type(self)
|
105
|
+
end
|
106
|
+
|
107
|
+
# Returns the name of this node used by {GraphQL::SCHEMA}
|
108
|
+
def schema_name
|
109
|
+
@type_name || default_schema_name
|
110
|
+
end
|
111
|
+
|
112
|
+
def default_schema_name
|
113
|
+
name.split("::").last.sub(/Node$/, '').underscore
|
114
|
+
end
|
115
|
+
|
116
|
+
# @param [String] field_name name of the field to be used as the cursor
|
117
|
+
# Declares what field will be used as the cursor for this node.
|
118
|
+
def cursor(field_name)
|
119
|
+
define_method "cursor" do
|
120
|
+
field_class = self.class.all_fields[field_name.to_s]
|
121
|
+
field = field_class.new(query: query, owner: self, calls: [])
|
122
|
+
cursor = GraphQL::Types::CursorField.new(field.as_result)
|
123
|
+
cursor.as_result
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
# All accessible fields on this node (including those defined in parent classes)
|
128
|
+
def all_fields
|
129
|
+
superclass.all_fields.merge(own_fields)
|
130
|
+
rescue NoMethodError
|
131
|
+
own_fields
|
132
|
+
end
|
133
|
+
|
134
|
+
# Fields defined by this class, but not its parents
|
135
|
+
def own_fields
|
136
|
+
@own_fields ||= {}
|
137
|
+
end
|
138
|
+
|
139
|
+
# @return [GraphQL::FieldDefiner] definer
|
140
|
+
def field
|
141
|
+
@field_definer ||= GraphQL::FieldDefiner.new(self)
|
142
|
+
end
|
143
|
+
|
144
|
+
# @param [String] field_name
|
145
|
+
# Un-define field with name `field_name`
|
146
|
+
def remove_field(field_name)
|
147
|
+
own_fields.delete(field_name.to_s)
|
52
148
|
end
|
53
149
|
end
|
54
150
|
|
151
|
+
private
|
55
152
|
|
56
|
-
def
|
57
|
-
|
58
|
-
|
153
|
+
def get_field(syntax_field)
|
154
|
+
field_class = self.class.all_fields[syntax_field.identifier]
|
155
|
+
if syntax_field.identifier == "cursor"
|
156
|
+
cursor
|
157
|
+
elsif field_class.nil?
|
158
|
+
raise GraphQL::FieldNotDefinedError.new(self.class.name, syntax_field.identifier)
|
159
|
+
else
|
160
|
+
field_class.new(
|
161
|
+
query: query,
|
162
|
+
owner: self,
|
163
|
+
calls: syntax_field.calls,
|
164
|
+
fields: syntax_field.fields,
|
165
|
+
)
|
59
166
|
end
|
60
167
|
end
|
61
168
|
end
|
data/lib/graphql/parser.rb
CHANGED
@@ -1,18 +1,29 @@
|
|
1
1
|
class GraphQL::Parser < Parslet::Parser
|
2
|
-
root(:
|
3
|
-
|
2
|
+
root(:query)
|
3
|
+
rule(:query) { node.repeat.as(:nodes) >> variable.repeat.as(:variables) }
|
4
|
+
# node
|
4
5
|
rule(:node) { space? >> call >> space? >> fields.as(:fields) }
|
5
6
|
|
6
|
-
|
7
|
-
|
8
|
-
rule(:edge) { call_chain >> space? >> fields.as(:fields) }
|
9
|
-
rule(:call_chain) { identifier >> (dot >> call).repeat(0).as(:calls) }
|
7
|
+
# field set
|
8
|
+
rule(:fields) { str("{") >> space? >> (field >> separator?).repeat(1) >> space? >> str("}") >> space?}
|
10
9
|
|
11
|
-
|
10
|
+
#call
|
11
|
+
rule(:call) { identifier >> str("(") >> (argument.as(:argument) >> separator?).repeat(0).as(:arguments) >> str(")") }
|
12
12
|
rule(:dot) { str(".") }
|
13
|
+
rule(:argument) { (identifier | variable_identifier)}
|
14
|
+
|
15
|
+
# field
|
16
|
+
rule(:field) { identifier >> call_chain.maybe >> alias_name.maybe >> space? >> fields.as(:fields).maybe }
|
17
|
+
rule(:call_chain) { (dot >> call).repeat(0).as(:calls) }
|
18
|
+
rule(:alias_name) { space >> str("as") >> space >> name.as(:alias_name) }
|
13
19
|
|
14
|
-
|
20
|
+
# variable
|
21
|
+
rule(:variable) { space? >> variable_identifier >> str(":") >> space? >> (name | json_string ).as(:json_string) >> space?}
|
22
|
+
rule(:json_string) { str("{") >> (match('[^{}]') | json_string).repeat >> str("}")}
|
23
|
+
rule(:variable_identifier) { (str("<") >> name >> str(">")).as(:identifier) }
|
15
24
|
|
25
|
+
# general purpose
|
26
|
+
rule(:separator?) { str(",").maybe >> space? }
|
16
27
|
rule(:identifier) { name.as(:identifier) }
|
17
28
|
rule(:name) { match('\w').repeat(1) }
|
18
29
|
rule(:space) { match('[\s\n]+').repeat(1) }
|
data/lib/graphql/query.rb
CHANGED
@@ -1,41 +1,84 @@
|
|
1
|
+
# Executes queries from strings against {GraphQL::SCHEMA}.
|
2
|
+
#
|
3
|
+
# @example
|
4
|
+
# query_str = "post(1) { title, comments { count } }"
|
5
|
+
# query_ctx = {user: current_user}
|
6
|
+
# query = GraphQL::Query.new(query_str, context: query_ctx)
|
7
|
+
# result = query.as_result
|
8
|
+
|
1
9
|
class GraphQL::Query
|
2
|
-
attr_reader :query_string, :root, :
|
3
|
-
|
10
|
+
attr_reader :query_string, :root, :context
|
11
|
+
|
12
|
+
# @param [String] query_string the string to be parsed
|
13
|
+
# @param [Object] context an object which will be available to all nodes and fields in the schema
|
14
|
+
def initialize(query_string, context: nil)
|
4
15
|
if !query_string.is_a?(String) || query_string.length == 0
|
5
16
|
raise "You must send a query string, not a #{query_string.class.name}"
|
6
17
|
end
|
7
18
|
@query_string = query_string
|
8
19
|
@root = parse(query_string)
|
9
|
-
@
|
10
|
-
end
|
11
|
-
|
12
|
-
def to_json
|
13
|
-
root_node = make_call(nil, root.identifier, root.argument)
|
14
|
-
raise "Couldn't find root for #{root.identifier}(#{root.argument})" if root.nil?
|
15
|
-
|
16
|
-
root_node.fields = root.fields
|
17
|
-
{
|
18
|
-
root_node.cursor => root_node.to_json
|
19
|
-
}
|
20
|
+
@context = context
|
20
21
|
end
|
21
22
|
|
22
|
-
|
23
|
-
|
24
|
-
|
23
|
+
# @return [Hash] result the JSON response to this query
|
24
|
+
# Calling {#as_result} more than once won't cause the query to be re-run
|
25
|
+
def as_result
|
26
|
+
@as_result ||= execute!
|
25
27
|
end
|
26
28
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
29
|
+
# @param [String] identifier
|
30
|
+
# returns a query variable named `identifier`, otherwise raises.
|
31
|
+
def get_variable(identifier)
|
32
|
+
syntax_var = @root.variables.find { |v| v.identifier == identifier }
|
33
|
+
if syntax_var.blank?
|
34
|
+
raise "No variable found for #{identifier}, defined variables are #{@root.variables.map(&:identifier)}"
|
31
35
|
end
|
32
|
-
|
36
|
+
syntax_var
|
33
37
|
end
|
34
38
|
|
35
39
|
private
|
36
40
|
|
41
|
+
def execute!
|
42
|
+
root_syntax_node = root.nodes[0]
|
43
|
+
root_call_identifier = root_syntax_node.identifier
|
44
|
+
root_call_class = GraphQL::SCHEMA.get_call(root_call_identifier)
|
45
|
+
root_call = root_call_class.new(query: self, syntax_arguments: root_syntax_node.arguments)
|
46
|
+
result_object = root_call.as_result
|
47
|
+
return_declarations = root_call_class.return_declarations
|
48
|
+
result = {}
|
49
|
+
|
50
|
+
if result_object.is_a?(Hash)
|
51
|
+
result_object.each do |name, value|
|
52
|
+
node_class = GraphQL::SCHEMA.type_for_object(value)
|
53
|
+
field_for_node = root_syntax_node.fields.find {|f| f.identifier == name.to_s }
|
54
|
+
if field_for_node.present?
|
55
|
+
fields_for_node = field_for_node.fields
|
56
|
+
node_value = node_class.new(value,query: self, fields: fields_for_node)
|
57
|
+
result[name.to_s] = node_value.as_result
|
58
|
+
end
|
59
|
+
end
|
60
|
+
elsif result_object.is_a?(Array)
|
61
|
+
fields_for_node = root_syntax_node.fields
|
62
|
+
result_object.each do |item|
|
63
|
+
node_class = GraphQL::SCHEMA.type_for_object(item)
|
64
|
+
node_value = node_class.new(item, query: self, fields: fields_for_node)
|
65
|
+
result[node_value.cursor] = node_value.as_result
|
66
|
+
end
|
67
|
+
else
|
68
|
+
node_class = GraphQL::SCHEMA.type_for_object(result_object)
|
69
|
+
fields_for_node = root_syntax_node.fields
|
70
|
+
node_value = node_class.new(result_object, query: self, fields: fields_for_node)
|
71
|
+
result[node_value.cursor] = node_value.as_result
|
72
|
+
end
|
73
|
+
|
74
|
+
result
|
75
|
+
end
|
76
|
+
|
37
77
|
def parse(query_string)
|
38
78
|
parsed_hash = GraphQL::PARSER.parse(query_string)
|
39
79
|
root_node = GraphQL::TRANSFORM.apply(parsed_hash)
|
80
|
+
rescue Parslet::ParseFailed => error
|
81
|
+
line, col = error.cause.source.line_and_column
|
82
|
+
raise GraphQL::SyntaxError.new(line, col, query_string)
|
40
83
|
end
|
41
84
|
end
|
@@ -0,0 +1,176 @@
|
|
1
|
+
# Every query begins with a root call. It might find data or mutate data and return some results.
|
2
|
+
#
|
3
|
+
# A root call should:
|
4
|
+
#
|
5
|
+
# - declare any arguments with {.argument}, or declare `argument.none`
|
6
|
+
# - declare returns with {.return}
|
7
|
+
# - implement {#execute!} to take those arguments and return values
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# FindPostCall < GraphQL::RootCall
|
11
|
+
# argument.number(:ids, any_number: true)
|
12
|
+
# returns :post
|
13
|
+
#
|
14
|
+
# def execute!(*ids)
|
15
|
+
# ids.map { |id| Post.find(id) }
|
16
|
+
# end
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# @example
|
20
|
+
# CreateCommentCall < GraphQL::RootCall
|
21
|
+
# argument.number(:post_id)
|
22
|
+
# argument.object(:comment)
|
23
|
+
# returns :post, :comment
|
24
|
+
#
|
25
|
+
# def execute!(post_id, comment)
|
26
|
+
# post = Post.find(post_id)
|
27
|
+
# new_comment = post.comments.create!(comment)
|
28
|
+
# {
|
29
|
+
# comment: new_comment,
|
30
|
+
# post: post,
|
31
|
+
# }
|
32
|
+
# end
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
class GraphQL::RootCall
|
36
|
+
attr_reader :query, :arguments
|
37
|
+
def initialize(query:, syntax_arguments:)
|
38
|
+
@query = query
|
39
|
+
|
40
|
+
raise "#{self.class.name} must declare arguments" unless self.class.arguments
|
41
|
+
@arguments = syntax_arguments.each_with_index.map do |syntax_arg, idx|
|
42
|
+
|
43
|
+
value = if syntax_arg[0] == "<"
|
44
|
+
query.get_variable(syntax_arg).json_string
|
45
|
+
else
|
46
|
+
syntax_arg
|
47
|
+
end
|
48
|
+
|
49
|
+
self.class.typecast(idx, value)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def execute!(*args)
|
54
|
+
raise NotImplementedError, "Do work in this method"
|
55
|
+
end
|
56
|
+
|
57
|
+
def context
|
58
|
+
query.context
|
59
|
+
end
|
60
|
+
|
61
|
+
def as_result
|
62
|
+
return_declarations = self.class.return_declarations
|
63
|
+
raise "#{self.class.name} must declare returns" unless return_declarations.present?
|
64
|
+
return_values = execute!(*arguments)
|
65
|
+
|
66
|
+
if return_values.is_a?(Hash)
|
67
|
+
unexpected_returns = return_values.keys - return_declarations.keys
|
68
|
+
missing_returns = return_declarations.keys - return_values.keys
|
69
|
+
if unexpected_returns.any?
|
70
|
+
raise "#{self.class.name} returned #{unexpected_returns}, but didn't declare them."
|
71
|
+
elsif missing_returns.any?
|
72
|
+
raise "#{self.class.name} declared #{missing_returns}, but didn't return them."
|
73
|
+
end
|
74
|
+
end
|
75
|
+
return_values
|
76
|
+
end
|
77
|
+
|
78
|
+
class << self
|
79
|
+
# @param [String] ident_name
|
80
|
+
# Declare an alternative name used in a query string
|
81
|
+
def indentifier(ident_name)
|
82
|
+
@identifier = ident_name
|
83
|
+
GraphQL::SCHEMA.add_call(self)
|
84
|
+
end
|
85
|
+
|
86
|
+
# The name used by {GraphQL::SCHEMA}. Uses {.identifier} or derives a name from the class name.
|
87
|
+
def schema_name
|
88
|
+
@identifier || name.split("::").last.sub(/Call$/, '').underscore
|
89
|
+
end
|
90
|
+
|
91
|
+
def inherited(child_class)
|
92
|
+
GraphQL::SCHEMA.add_call(child_class)
|
93
|
+
end
|
94
|
+
|
95
|
+
# This call won't be visible in `schema()`
|
96
|
+
def abstract!
|
97
|
+
GraphQL::SCHEMA.remove_call(self)
|
98
|
+
end
|
99
|
+
|
100
|
+
# @param [Symbol] return_declarations
|
101
|
+
# Name of returned values from this call
|
102
|
+
def returns(*return_declaration_names)
|
103
|
+
if return_declaration_names.last.is_a?(Hash)
|
104
|
+
return_declarations_hash = return_declaration_names.pop
|
105
|
+
else
|
106
|
+
return_declarations_hash = {}
|
107
|
+
end
|
108
|
+
|
109
|
+
raise "Return keys must be symbols" if (return_declarations.keys + return_declaration_names).any? { |k| !k.is_a?(Symbol) }
|
110
|
+
|
111
|
+
return_declaration_names.each do |return_sym|
|
112
|
+
return_type = return_sym.to_s
|
113
|
+
return_declarations[return_sym] = return_type
|
114
|
+
end
|
115
|
+
|
116
|
+
return_declarations_hash.each do |return_sym, return_type|
|
117
|
+
return_declarations[return_sym] = return_type
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def return_declarations
|
122
|
+
@return_declarations ||= {}
|
123
|
+
end
|
124
|
+
|
125
|
+
# @return [GraphQL::RootCallArgumentDefiner] definer
|
126
|
+
# Use this object to declare arguments.
|
127
|
+
def argument
|
128
|
+
@argument ||= GraphQL::RootCallArgumentDefiner.new(self)
|
129
|
+
end
|
130
|
+
|
131
|
+
def own_arguments
|
132
|
+
@argument && @argument.arguments
|
133
|
+
end
|
134
|
+
|
135
|
+
def arguments
|
136
|
+
own = own_arguments || []
|
137
|
+
own + superclass.arguments
|
138
|
+
rescue NoMethodError
|
139
|
+
own
|
140
|
+
end
|
141
|
+
|
142
|
+
def argument_for_index(idx)
|
143
|
+
if arguments.first.any_number
|
144
|
+
arguments.first
|
145
|
+
else
|
146
|
+
arguments[idx]
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
TYPE_CHECKS = {
|
151
|
+
"object" => Hash,
|
152
|
+
"number" => Numeric,
|
153
|
+
"string" => String,
|
154
|
+
}
|
155
|
+
|
156
|
+
def typecast(idx, value)
|
157
|
+
arg_dec = argument_for_index(idx)
|
158
|
+
expected_type = arg_dec.type
|
159
|
+
expected_type_class = TYPE_CHECKS[expected_type]
|
160
|
+
|
161
|
+
if expected_type == "string"
|
162
|
+
parsed_value = value
|
163
|
+
else
|
164
|
+
parsed_value = JSON.parse('{ "value" : ' + value + '}')["value"]
|
165
|
+
end
|
166
|
+
|
167
|
+
if !parsed_value.is_a?(expected_type_class)
|
168
|
+
raise GraphQL::RootCallArgumentError.new(arg_dec, value)
|
169
|
+
end
|
170
|
+
|
171
|
+
parsed_value
|
172
|
+
rescue JSON::ParserError
|
173
|
+
raise GraphQL::RootCallArgumentError.new(arg_dec, value)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|