graphlyte 0.3.0 → 1.0.0

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 (41) 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 -18
  30. data/lib/graphlyte/arguments/set.rb +0 -88
  31. data/lib/graphlyte/arguments/value.rb +0 -32
  32. data/lib/graphlyte/builder.rb +0 -53
  33. data/lib/graphlyte/directive.rb +0 -21
  34. data/lib/graphlyte/field.rb +0 -65
  35. data/lib/graphlyte/fieldset.rb +0 -36
  36. data/lib/graphlyte/fragment.rb +0 -17
  37. data/lib/graphlyte/inline_fragment.rb +0 -29
  38. data/lib/graphlyte/query.rb +0 -148
  39. data/lib/graphlyte/schema/parser.rb +0 -674
  40. data/lib/graphlyte/schema/types/base.rb +0 -54
  41. data/lib/graphlyte/types.rb +0 -9
@@ -1,53 +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.name == field_symbol.to_s
22
- end
23
- end
24
-
25
- def method_missing(method, fieldset_or_hargs=nil, hargs={}, &block)
26
- # todo: camel case method
27
-
28
- # hack for ruby bug in lower versions
29
- if [Fieldset, Fragment, InlineFragment].include?(fieldset_or_hargs.class)
30
- field = Field.new(method, fieldset_or_hargs, hargs)
31
- else
32
- field = Field.new(method, Fieldset.empty, fieldset_or_hargs)
33
- end
34
-
35
- field.fieldset.builder.>.instance_eval(&block) if block
36
- @fields << field
37
- field
38
- end
39
-
40
- def respond_to_missing
41
- true
42
- end
43
-
44
- # for internal use only
45
- def >>
46
- @fields
47
- end
48
-
49
- def >
50
- self
51
- end
52
- end
53
- end
@@ -1,21 +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)
13
- # add directive after fieldname?
14
- string += ' ' * indent
15
- string += "#{field} " if field
16
- string += "@#{name}"
17
- string += @inputs.to_s unless @inputs.to_s.empty?
18
- string
19
- end
20
- end
21
- 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)
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(inflate_indent, 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,148 +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
- modify(field.fieldset.fields, [token], &block)
15
- field.fieldset.builder.instance_eval(&block) unless field.fieldset.fields.empty?
16
- end
17
- else
18
- needle = fields.find do |field|
19
- field.name == token
20
- end
21
-
22
- raise "#{token} not found in query" unless needle
23
-
24
- if selector_tokens.size.zero?
25
- needle.fieldset.builder.instance_eval(&block)
26
- else
27
- modify(needle.fieldset.fields, selector_tokens, &block)
28
- end
29
- end
30
- end
31
- end
32
-
33
- class Query < Fieldset
34
- using Refinements::StringRefinement
35
- attr_reader :name, :type
36
-
37
- def initialize(query_name=nil, type=:query, **hargs)
38
- @name = query_name || "anonymousQuery"
39
- @type = type
40
- super(**hargs)
41
- end
42
-
43
- def at(selector, &block)
44
- Selector.new(selector).modify(fields, &block)
45
- end
46
-
47
- def placeholders
48
- flatten_variables(builder.>>).map do |value|
49
- unless value.formal?
50
- str = ":#{value.value.to_sym.inspect} of unknown"
51
- else
52
- str = ":#{value.value.placeholder} of #{value.value.name}"
53
- end
54
-
55
- if value.value.default
56
- str += " with default "
57
- value.value.default.merge!(str)
58
- end
59
- str
60
- end.join("\n")
61
- end
62
-
63
- def to_json(query_name=name, **hargs)
64
- variables = flatten_variables(builder.>>).uniq { |v| v.value }
65
- types = merge_variable_types(variables, hargs)
66
-
67
- str = "#{type} #{query_name}"
68
- unless types.empty?
69
- type_new = types.map do |type_arr|
70
- type_str = "$#{type_arr[0].to_camel_case}: #{type_arr[1]}"
71
- unless type_arr[2].nil?
72
- type_str << " = "
73
- type_arr[2].merge!(type_str)
74
- end
75
- type_str
76
- end
77
- str += "(#{type_new.join(", ")})"
78
- end
79
- { query: "#{str} #{to_s(1)}", variables: Arguments::Set.new(hargs).to_h(true) }.to_json
80
- end
81
-
82
- def to_s(indent=0)
83
- "{\n#{super(indent + 1)}\n}#{format_fragments}"
84
- end
85
-
86
- def merge_variable_types(variables=[], hargs)
87
- variables.inject([]) do |memo, var|
88
- unless var.formal?
89
- if hargs[var.value].is_a? String
90
- memo << [var.value.to_camel_case, "String"]
91
- elsif [TrueClass, FalseClass].include? hargs[var.value].class
92
- memo << [var.value ,"Boolean"]
93
- elsif hargs[var.value].is_a? Float
94
- memo << [var.value, "Float"]
95
- elsif hargs[var.value].is_a? Integer
96
- memo << [var.value, "Int"]
97
- elsif hargs[var.value].is_a? Array
98
- memo << "[#{merge_variable_types(var.value, hargs).first}]"
99
- end
100
- else
101
- memo << [var.value.placeholder, var.value.name, var.value.default]
102
- end
103
- memo
104
- end
105
- end
106
-
107
- def format_fragments
108
- str = "\n"
109
- flatten(builder.>>).each do |_, fragment|
110
- str += "\nfragment #{fragment.fragment}"
111
- str += " on #{fragment.model_name}" unless fragment.model_name.nil?
112
- str += " {\n#{fragment.fields.map {|f| f.to_s(1) }.join("\n")}\n}"
113
- end
114
- str
115
- end
116
-
117
- def flatten_variables(fields, variables=[])
118
- fields.each do |field|
119
- variables.concat field.inputs.extract_variables unless [InlineFragment, Fragment].include? field.class
120
- variables.concat field.directive.inputs.extract_variables if field.class == InlineFragment && field.directive
121
- if [InlineFragment, Fragment].include? field.class
122
- flatten_variables(field.fields, variables)
123
- else
124
- flatten_variables(field.fieldset.fields, variables)
125
- end
126
- end
127
- variables
128
- end
129
-
130
- def flatten(fields, new_fields = {})
131
- fields.each do |field|
132
- next if field.class == InlineFragment
133
- if field.class.eql?(Fragment)
134
- new_fields[field.fragment] = field
135
- unless field.empty?
136
- flatten(field.fields, new_fields)
137
- end
138
- else
139
- if field.fieldset.class.eql?(Fragment)
140
- new_fields[field.fieldset.fragment] = field.fieldset
141
- end
142
- flatten(field.fieldset.fields, new_fields) unless field.atomic?
143
- end
144
- end
145
- new_fields
146
- end
147
- end
148
- end