graphlyte 0.3.2 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graphlyte/data.rb +68 -0
  3. data/lib/graphlyte/document.rb +131 -0
  4. data/lib/graphlyte/dsl.rb +86 -0
  5. data/lib/graphlyte/editor.rb +288 -0
  6. data/lib/graphlyte/editors/annotate_types.rb +75 -0
  7. data/lib/graphlyte/editors/canonicalize.rb +26 -0
  8. data/lib/graphlyte/editors/collect_variable_references.rb +36 -0
  9. data/lib/graphlyte/editors/infer_signature.rb +36 -0
  10. data/lib/graphlyte/editors/inline_fragments.rb +37 -0
  11. data/lib/graphlyte/editors/remove_unneeded_spreads.rb +64 -0
  12. data/lib/graphlyte/editors/select_operation.rb +116 -0
  13. data/lib/graphlyte/editors/with_variables.rb +106 -0
  14. data/lib/graphlyte/errors.rb +33 -0
  15. data/lib/graphlyte/lexer.rb +392 -0
  16. data/lib/graphlyte/lexing/location.rb +43 -0
  17. data/lib/graphlyte/lexing/token.rb +31 -0
  18. data/lib/graphlyte/parser.rb +269 -0
  19. data/lib/graphlyte/parsing/backtracking_parser.rb +160 -0
  20. data/lib/graphlyte/refinements/string_refinement.rb +14 -8
  21. data/lib/graphlyte/refinements/syntax_refinements.rb +62 -0
  22. data/lib/graphlyte/schema.rb +165 -0
  23. data/lib/graphlyte/schema_query.rb +82 -65
  24. data/lib/graphlyte/selection_builder.rb +189 -0
  25. data/lib/graphlyte/selector.rb +75 -0
  26. data/lib/graphlyte/serializer.rb +223 -0
  27. data/lib/graphlyte/syntax.rb +369 -0
  28. data/lib/graphlyte.rb +24 -42
  29. metadata +88 -19
  30. data/lib/graphlyte/arguments/set.rb +0 -94
  31. data/lib/graphlyte/arguments/value.rb +0 -42
  32. data/lib/graphlyte/arguments/value_literal.rb +0 -17
  33. data/lib/graphlyte/builder.rb +0 -59
  34. data/lib/graphlyte/directive.rb +0 -25
  35. data/lib/graphlyte/field.rb +0 -65
  36. data/lib/graphlyte/fieldset.rb +0 -36
  37. data/lib/graphlyte/fragment.rb +0 -17
  38. data/lib/graphlyte/inline_fragment.rb +0 -29
  39. data/lib/graphlyte/query.rb +0 -150
  40. data/lib/graphlyte/schema/parser.rb +0 -687
  41. data/lib/graphlyte/schema/types/base.rb +0 -54
  42. data/lib/graphlyte/types.rb +0 -9
@@ -1,42 +0,0 @@
1
- require_relative "./../refinements/string_refinement"
2
- module Graphlyte
3
- module Arguments
4
- class Value
5
- using Refinements::StringRefinement
6
-
7
- attr_reader :value
8
-
9
- def initialize(value)
10
- raise ArgumentError, "Hash not allowed in this context" if value.is_a? Hash
11
- @value = value
12
- end
13
-
14
- def self.from(value)
15
- return value if value.is_a? self
16
-
17
- new(value)
18
- end
19
-
20
- def symbol?
21
- value.is_a? Symbol
22
- end
23
-
24
- def formal?
25
- value.is_a? Schema::Types::Base
26
- end
27
-
28
- def refresh(args)
29
- @value = value.call(args) if value.is_a? Proc
30
- end
31
-
32
- def to_s(raw = false)
33
- return "$#{value.to_s.to_camel_case}" if value.is_a? Symbol
34
- return value if value.is_a? Numeric
35
- return "\"#{value}\"" if value.is_a?(String) && !raw
36
- return "null" if value.nil?
37
- return "$#{value.placeholder.to_camel_case}" if value.is_a? Schema::Types::Base
38
- value.to_s
39
- end
40
- end
41
- end
42
- end
@@ -1,17 +0,0 @@
1
- module Graphlyte
2
- module Arguments
3
- class ValueLiteral
4
- attr_reader :value
5
-
6
- def initialize(string)
7
- raise 'Value must be a string' unless string.class == String
8
-
9
- @value = string
10
- end
11
-
12
- def to_s
13
- @value
14
- end
15
- end
16
- end
17
- end
@@ -1,59 +0,0 @@
1
- require_relative "./field"
2
- require_relative "./fieldset"
3
-
4
- module Graphlyte
5
- class Builder
6
- def initialize(fields = [])
7
- @fields = fields
8
- end
9
-
10
- def <<(buildable)
11
- raise "Must pass a Fieldset or Fragment" unless [Fragment, Fieldset, InlineFragment].include?(buildable.class)
12
-
13
- @fields.concat(buildable.fields) if buildable.class.eql? Fieldset
14
-
15
- # todo: handle fragments better, it's not a field
16
- @fields << buildable if [InlineFragment, Fragment].include? buildable.class
17
- end
18
-
19
- def remove(field_symbol)
20
- @fields.reject! do |field|
21
- field.class == Fragment ? false : field.name == field_symbol.to_s
22
- end
23
-
24
- @fields.select { |field| field.class == Fragment }.each do |fragment|
25
- fragment.fields.reject! do |field|
26
- field.name == field_symbol.to_s
27
- end
28
- end
29
- end
30
-
31
- def method_missing(method, fieldset_or_hargs=nil, hargs={}, &block)
32
- # todo: camel case method
33
-
34
- # hack for ruby bug in lower versions
35
- if [Fieldset, Fragment, InlineFragment].include?(fieldset_or_hargs.class)
36
- field = Field.new(method, fieldset_or_hargs, hargs)
37
- else
38
- field = Field.new(method, Fieldset.empty, fieldset_or_hargs)
39
- end
40
-
41
- field.fieldset.builder.>.instance_eval(&block) if block
42
- @fields << field
43
- field
44
- end
45
-
46
- def respond_to_missing
47
- true
48
- end
49
-
50
- # for internal use only
51
- def >>
52
- @fields
53
- end
54
-
55
- def >
56
- self
57
- end
58
- end
59
- end
@@ -1,25 +0,0 @@
1
- require_relative 'arguments/set'
2
-
3
- module Graphlyte
4
- class Directive
5
- attr_reader :name, :inputs
6
-
7
- def initialize(name, **hargs)
8
- @name = name
9
- @inputs = Arguments::Set.new(hargs)
10
- end
11
-
12
- def inflate(indent, string, field: nil, args: nil)
13
- # add directive after fieldname?
14
- string += ' ' * indent unless indent.nil?
15
- if !args.nil? && args.to_s.empty?
16
- string += "#{field} " if field
17
- else
18
- string += "#{field}#{args.to_s} "
19
- end
20
- string += "@#{name}"
21
- string += @inputs.to_s unless @inputs.to_s.empty?
22
- string
23
- end
24
- end
25
- end
@@ -1,65 +0,0 @@
1
- require_relative "./arguments/set"
2
- require_relative 'directive'
3
- require_relative "./refinements/string_refinement"
4
- module Graphlyte
5
- class Field
6
- using Refinements::StringRefinement
7
-
8
- attr_reader :name, :fieldset, :inputs, :directive
9
-
10
- def initialize(name, fieldset, hargs, directive: nil, inputs: Arguments::Set.new(hargs))
11
- @name = name.to_s.to_camel_case
12
- @fieldset = fieldset
13
- @inputs = inputs
14
- @alias = nil
15
- @directive = directive
16
- end
17
-
18
- def atomic?
19
- fieldset.empty?
20
- end
21
-
22
- def alias(name, &block)
23
- @alias = name
24
- if block
25
- fieldset.builder.>.instance_eval(&block)
26
- else
27
- self
28
- end
29
- end
30
-
31
- def include(**hargs, &block)
32
- make_directive('include', **hargs, &block)
33
- end
34
-
35
- def skip(**hargs, &block)
36
- make_directive('skip', **hargs, &block)
37
- end
38
-
39
- def to_s(indent=0)
40
- str = ""
41
- actual_indent = ("\s" * indent) * 2
42
- if @alias
43
- str += "#{actual_indent}#{@alias}: #{name}"
44
- str += inputs.to_s.empty? ? "()" : inputs.to_s
45
- elsif @directive
46
- str = @directive.inflate(indent * 2, str, field: name, args: inputs)
47
- else
48
- str += "#{actual_indent}#{name}#{inputs.to_s}"
49
- end
50
- str += " {\n#{fieldset.to_s(indent + 1)}\n#{actual_indent}}" unless atomic?
51
- str
52
- end
53
-
54
- private
55
-
56
- def method_missing(symbol, **hargs, &block)
57
- make_directive(symbol.to_s, **hargs, &block)
58
- end
59
-
60
- def make_directive(name, **hargs, &block)
61
- @directive = Directive.new(name, **hargs)
62
- fieldset.builder.>.instance_eval(&block) if block
63
- end
64
- end
65
- end
@@ -1,36 +0,0 @@
1
- require_relative "./builder"
2
-
3
- module Graphlyte
4
- class Fieldset
5
- def self.empty
6
- new
7
- end
8
-
9
- attr_reader :model_name, :builder
10
-
11
- def initialize(model_name = nil, builder: Builder.new)
12
- @model_name = model_name
13
- @builder = builder
14
- end
15
-
16
- def fields
17
- builder.>>
18
- end
19
-
20
- def empty?
21
- fields.empty?
22
- end
23
-
24
- def to_s(indent=0)
25
- fields.map { |field| field.to_s(indent) }.join("\n")
26
- end
27
-
28
- def to_a
29
- [ to_s ]
30
- end
31
-
32
- def +(fieldset)
33
- to_s + "\n" + fieldset.to_s
34
- end
35
- end
36
- end
@@ -1,17 +0,0 @@
1
- require_relative "./fieldset"
2
-
3
- module Graphlyte
4
- class Fragment < Fieldset
5
- attr_reader :fragment
6
-
7
- def initialize(fragment_name, model_name=nil, **hargs)
8
- @fragment = fragment_name
9
- super(model_name, **hargs)
10
- end
11
-
12
- def to_s(indent=0)
13
- actual_indent = ("\s" * indent) * 2
14
- "#{actual_indent}...#{fragment}#{actual_indent}"
15
- end
16
- end
17
- end
@@ -1,29 +0,0 @@
1
- require_relative "./fieldset"
2
-
3
- module Graphlyte
4
- class InlineFragment < Fieldset
5
- attr_reader :directive
6
-
7
- def self.from_directive(directive, **hargs)
8
- new(nil, directive: directive, **hargs)
9
- end
10
-
11
- def initialize(model = nil, directive: nil, **hargs)
12
- @directive = directive
13
- super(model, **hargs)
14
- end
15
-
16
- def to_s(indent=0)
17
- actual_indent = ("\s" * indent) * 2
18
- string = '...'
19
- string += " on #{model_name}" if model_name
20
- inflate_indent = model_name ? 1 : 0
21
- string = directive.inflate(0, string) if directive
22
- string += " {\n"
23
- string += super(indent + 1)
24
- string += "\n#{actual_indent}}"
25
-
26
- "#{actual_indent}#{string}"
27
- end
28
- end
29
- end
@@ -1,150 +0,0 @@
1
- require_relative "./refinements/string_refinement"
2
- require "json"
3
- module Graphlyte
4
- class Selector
5
- def initialize(selector)
6
- @selector_tokens = selector.split('.')
7
- end
8
-
9
- def modify(fields, selector_tokens = @selector_tokens, &block)
10
- token = selector_tokens.shift
11
-
12
- if token == '*'
13
- fields.each do |field|
14
- next if field.class == Fragment
15
-
16
- modify(field.fieldset.fields, [token], &block)
17
- field.fieldset.builder.instance_eval(&block) unless field.fieldset.fields.empty?
18
- end
19
- else
20
- needle = fields.find do |field|
21
- field.name == token
22
- end
23
-
24
- raise "#{token} not found in query" unless needle
25
-
26
- if selector_tokens.size.zero?
27
- needle.fieldset.builder.instance_eval(&block)
28
- else
29
- modify(needle.fieldset.fields, selector_tokens, &block)
30
- end
31
- end
32
- end
33
- end
34
-
35
- class Query < Fieldset
36
- using Refinements::StringRefinement
37
- attr_reader :name, :type
38
-
39
- def initialize(query_name=nil, type=:query, **hargs)
40
- @name = query_name || "anonymousQuery"
41
- @type = type
42
- super(**hargs)
43
- end
44
-
45
- def at(selector, &block)
46
- Selector.new(selector).modify(fields, &block)
47
- end
48
-
49
- def placeholders
50
- flatten_variables(builder.>>).map do |value|
51
- unless value.formal?
52
- str = ":#{value.value.to_sym.inspect} of unknown"
53
- else
54
- str = ":#{value.value.placeholder} of #{value.value.name}"
55
- end
56
-
57
- if value.value.default
58
- str += " with default "
59
- value.value.default.merge!(str)
60
- end
61
- str
62
- end.join("\n")
63
- end
64
-
65
- def to_json(query_name=name, **hargs)
66
- variables = flatten_variables(builder.>>).uniq { |v| v.value }
67
- types = merge_variable_types(variables, hargs)
68
-
69
- str = "#{type} #{query_name}"
70
- unless types.empty?
71
- type_new = types.map do |type_arr|
72
- type_str = "$#{type_arr[0].to_camel_case}: #{type_arr[1]}"
73
- unless type_arr[2].nil?
74
- type_str << " = "
75
- type_arr[2].merge!(type_str)
76
- end
77
- type_str
78
- end
79
- str += "(#{type_new.join(", ")})"
80
- end
81
- { query: "#{str} #{to_s(1)}", variables: Arguments::Set.new(hargs).to_h(true) }.to_json
82
- end
83
-
84
- def to_s(indent=0)
85
- "{\n#{super(indent + 1)}\n}#{format_fragments}"
86
- end
87
-
88
- def merge_variable_types(variables=[], hargs)
89
- variables.inject([]) do |memo, var|
90
- unless var.formal?
91
- if hargs[var.value].is_a? String
92
- memo << [var.value.to_camel_case, "String"]
93
- elsif [TrueClass, FalseClass].include? hargs[var.value].class
94
- memo << [var.value ,"Boolean"]
95
- elsif hargs[var.value].is_a? Float
96
- memo << [var.value, "Float"]
97
- elsif hargs[var.value].is_a? Integer
98
- memo << [var.value, "Int"]
99
- elsif hargs[var.value].is_a? Array
100
- memo << "[#{merge_variable_types(var.value, hargs).first}]"
101
- end
102
- else
103
- memo << [var.value.placeholder, var.value.name, var.value.default]
104
- end
105
- memo
106
- end
107
- end
108
-
109
- def format_fragments
110
- str = "\n"
111
- flatten(builder.>>).each do |_, fragment|
112
- str += "\nfragment #{fragment.fragment}"
113
- str += " on #{fragment.model_name}" unless fragment.model_name.nil?
114
- str += " {\n#{fragment.fields.map {|f| f.to_s(1) }.join("\n")}\n}"
115
- end
116
- str
117
- end
118
-
119
- def flatten_variables(fields, variables=[])
120
- fields.each do |field|
121
- variables.concat field.inputs.extract_variables unless [InlineFragment, Fragment].include? field.class
122
- variables.concat field.directive.inputs.extract_variables if field.respond_to?(:directive) && field.directive
123
- if [InlineFragment, Fragment].include? field.class
124
- flatten_variables(field.fields, variables)
125
- else
126
- flatten_variables(field.fieldset.fields, variables)
127
- end
128
- end
129
- variables
130
- end
131
-
132
- def flatten(fields, new_fields = {})
133
- fields.each do |field|
134
- next if field.class == InlineFragment
135
- if field.class.eql?(Fragment)
136
- new_fields[field.fragment] = field
137
- unless field.empty?
138
- flatten(field.fields, new_fields)
139
- end
140
- else
141
- if field.fieldset.class.eql?(Fragment)
142
- new_fields[field.fieldset.fragment] = field.fieldset
143
- end
144
- flatten(field.fieldset.fields, new_fields) unless field.atomic?
145
- end
146
- end
147
- new_fields
148
- end
149
- end
150
- end