graphlyte 0.3.0 → 1.0.0

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