graphlyte 0.1.2 → 0.1.6

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7ee4eed2122126ce2d651f0f63210598a48a9fb6a6919a4a20066e932fccdab3
4
- data.tar.gz: 7d74b8dc7a8c46e949a5ea035970f3595ff02020dbc55ad819b31e5bfcd2b3e0
3
+ metadata.gz: 6daff225ff526c8866c59601de914bbf7cac87c0393d5a3918a16f78c249eaa7
4
+ data.tar.gz: 104deccd41d6dc5fd2be8a263f8549e5190f088717737fed61b9ae9da78aca6d
5
5
  SHA512:
6
- metadata.gz: bf5f9ee7465d4120a7c343954fd912c07a14e90b23ecee94dca4c9ba077be0d2c3d2853540c74b243f715f4e30cd00e90587e189abac66e043e5a30baab7ea07
7
- data.tar.gz: f31753943431a52b0296de465a6d7f2901657a2c4be1c69fc0c91f424011bfbb12f5ad5baa090e236ddf3466b70463cf5098a3ecb72e712af8290e3ea13a85e9
6
+ metadata.gz: 539c3dfd3b611f79341f32b8eb61db2c76d0c92fd3e2e3d7312dd6d1946ec51463ac7f7f8db09e663a8681e9afc065f5cf0b7a5788d5129ca6e08d5b4870ce28
7
+ data.tar.gz: 642ca3c211d11e1b6613a6910ed618685d9a87ae3057438f7f5ab3ccc63dba26041691a4a81309b77e41b682b86ad4aefe6789631fd3b0ddccb11b882d959947
@@ -0,0 +1,75 @@
1
+ require_relative "./value"
2
+ require_relative "./../refinements/string_refinement"
3
+ module Graphlyte
4
+ module Arguments
5
+ class Set
6
+ using Refinements::StringRefinement
7
+
8
+ attr_reader :values
9
+
10
+ def initialize(data)
11
+ raise ArgumentError, "input #{data} must be a hash" unless data.nil? || data.is_a?(Hash)
12
+ @values = expand_arguments(data) unless data.nil?
13
+ end
14
+
15
+ def extract_variables(values=@values, variables=[])
16
+ values&.each do |key, value|
17
+ if value.is_a?(Set)
18
+ variables.concat extract_variables(value.values)
19
+ elsif value.symbol?
20
+ variables << value
21
+ elsif value.formal?
22
+ variables << value
23
+ end
24
+ end
25
+ variables
26
+ end
27
+
28
+ def to_h(inner = false)
29
+ return {} unless values && !values.empty?
30
+ values.inject({}) do |memo, (k, v)|
31
+ if v.is_a?(Array)
32
+ memo[k.to_s.to_camel_case] = v.map(&:to_s)
33
+ elsif v.is_a?(Set)
34
+ memo[k.to_s.to_camel_case] = v.to_h
35
+ else
36
+ memo[k.to_s.to_camel_case] = v.to_s
37
+ end
38
+ memo
39
+ end
40
+ end
41
+
42
+ def to_s(inner = false)
43
+ return "" unless values && !values.empty?
44
+ arr = values.map do |k,v|
45
+ if v.is_a?(Array)
46
+ "#{k.to_s.to_camel_case}: [#{v.map(&:to_s).join(", ")}]"
47
+ elsif v.is_a?(Set)
48
+ "#{k.to_s.to_camel_case}: { #{v.to_s(true)} }"
49
+ else
50
+ "#{k.to_s.to_camel_case}: #{v.to_s}"
51
+ end
52
+ end
53
+ return arr.join(", ") if inner
54
+ "(#{arr.join(", ")})"
55
+ end
56
+
57
+ private
58
+
59
+ def expand_arguments(data)
60
+ data.inject({}) do |memo, (k, v)|
61
+ if v.is_a?(Array)
62
+ memo[k] = v.map do |item|
63
+ Value.new(item)
64
+ end
65
+ elsif v.is_a?(Hash)
66
+ memo[k] = Set.new(v)
67
+ else
68
+ memo[k] = Value.new(v)
69
+ end
70
+ memo
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,32 @@
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 symbol?
15
+ value.is_a? Symbol
16
+ end
17
+
18
+ def formal?
19
+ value.is_a? Schema::Types::Base
20
+ end
21
+
22
+ def to_s
23
+ return "$#{value.to_s.to_camel_case}" if value.is_a? Symbol
24
+ return value if value.is_a? Numeric
25
+ return "\"#{value}\"" if value.is_a? String
26
+ return "null" if value.nil?
27
+ return "$#{value.placeholder.to_camel_case}" if value.is_a? Schema::Types::Base
28
+ value.to_s
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,43 @@
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].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 buildable.class.eql? Fragment
17
+ end
18
+
19
+ def method_missing(method, fieldset_or_hargs=nil, hargs={}, &block)
20
+ # todo: camel case method
21
+
22
+ # hack for ruby bug in lower versions
23
+ if [Fieldset, Fragment].include?(fieldset_or_hargs.class)
24
+ field = Field.new(method, fieldset_or_hargs, hargs)
25
+ else
26
+ field = Field.new(method, Fieldset.empty, fieldset_or_hargs)
27
+ end
28
+
29
+ field.fieldset.builder.>.instance_eval(&block) if block
30
+ @fields << field
31
+ field
32
+ end
33
+
34
+ # for internal use only
35
+ def >>
36
+ @fields
37
+ end
38
+
39
+ def >
40
+ self
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,38 @@
1
+ require_relative "./arguments/set"
2
+ require_relative "./refinements/string_refinement"
3
+ module Graphlyte
4
+ class Field
5
+ using Refinements::StringRefinement
6
+
7
+ attr_reader :name, :fieldset, :inputs, :alias
8
+
9
+ def initialize(name, fieldset, hargs, inputs: Arguments::Set.new(hargs))
10
+ @name = name.to_s.to_camel_case
11
+ @fieldset = fieldset
12
+ @inputs = inputs
13
+ @alias = nil
14
+ end
15
+
16
+ def atomic?
17
+ fieldset.empty?
18
+ end
19
+
20
+ def alias(name, &block)
21
+ @alias = name
22
+ fieldset.builder.>.instance_eval(&block) if block
23
+ end
24
+
25
+ def to_s(indent=0)
26
+ str = ""
27
+ actual_indent = ("\s" * indent) * 2
28
+ if @alias
29
+ str += "#{actual_indent}#{@alias}: #{name}"
30
+ str += inputs.to_s.empty? ? "()" : inputs.to_s
31
+ else
32
+ str += "#{actual_indent}#{name}#{inputs.to_s}"
33
+ end
34
+ str += " {\n#{fieldset.to_s(indent + 1)}\n#{actual_indent}}" unless atomic?
35
+ str
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,36 @@
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
@@ -0,0 +1,17 @@
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
@@ -0,0 +1,97 @@
1
+ require_relative "./refinements/string_refinement"
2
+ module Graphlyte
3
+ class Query < Fieldset
4
+ using Refinements::StringRefinement
5
+ attr_reader :name, :type
6
+
7
+ def initialize(query_name=nil, type=:query, **hargs)
8
+ @name = query_name
9
+ @type = type
10
+ super(**hargs)
11
+ end
12
+
13
+ def placeholders
14
+ flatten_variables(builder.>>).map do |value|
15
+ ":#{value.value.placeholder} of #{value.value.name}"
16
+ end.join("\n")
17
+ end
18
+
19
+ def to_json(name="anonymousQuery", **hargs)
20
+ variables = flatten_variables(builder.>>).uniq { |v| v.value }
21
+ types = merge_variable_types(variables, hargs)
22
+
23
+ str = "#{type} #{name}"
24
+ unless types.empty?
25
+ type_new = types.map do |type_arr|
26
+ "$#{type_arr[0].to_camel_case}: #{type_arr[1]}"
27
+ end
28
+ str += "(#{type_new.join(", ")})"
29
+ end
30
+ { query: "#{str} #{to_s(1)}", variables: Arguments::Set.new(hargs).to_h }.to_json
31
+ end
32
+
33
+ def to_s(indent=0)
34
+ "{\n#{super(indent + 1)}\n}#{format_fragments}"
35
+ end
36
+
37
+ def merge_variable_types(variables=[], hargs)
38
+ variables.inject([]) do |memo, var|
39
+ unless var.formal?
40
+ if hargs[var.value].is_a? String
41
+ memo << [var.value.to_camel_case, "String"]
42
+ elsif [TrueClass, FalseClass].include? hargs[var.value].class
43
+ memo << [var.value ,"Boolean"]
44
+ elsif hargs[var.value].is_a? Float
45
+ memo << [var.value, "Float"]
46
+ elsif hargs[var.value].is_a? Integer
47
+ memo << [var.value, "Int"]
48
+ elsif hargs[var.value].is_a? Array
49
+ memo << "[#{merge_variable_types(var.value, hargs).first}]"
50
+ end
51
+ else
52
+ memo << [var.value.placeholder, var.value.name]
53
+ end
54
+ memo
55
+ end
56
+ end
57
+
58
+ def format_fragments
59
+ str = "\n"
60
+ flatten(builder.>>).each do |_, fragment|
61
+ str += "\nfragment #{fragment.fragment}"
62
+ str += " on #{fragment.model_name}" unless fragment.model_name.nil?
63
+ str += " {\n#{fragment.fields.map {|f| f.to_s(1) }.join("\n")}\n}"
64
+ end
65
+ str
66
+ end
67
+
68
+ def flatten_variables(fields, variables=[])
69
+ fields.each do |field|
70
+ variables.concat field.inputs.extract_variables unless field.class.eql?(Fragment)
71
+ if field.class.eql?(Fragment)
72
+ flatten_variables(field.fields, variables)
73
+ else
74
+ flatten_variables(field.fieldset.fields, variables)
75
+ end
76
+ end
77
+ variables
78
+ end
79
+
80
+ def flatten(fields, new_fields = {})
81
+ fields.each do |field|
82
+ if field.class.eql?(Fragment)
83
+ new_fields[field.fragment] = field
84
+ unless field.empty?
85
+ flatten(field.fields, new_fields)
86
+ end
87
+ else
88
+ if field.fieldset.class.eql?(Fragment)
89
+ new_fields[field.fieldset.fragment] = field.fieldset
90
+ end
91
+ flatten(field.fieldset.fields, new_fields) unless field.atomic?
92
+ end
93
+ end
94
+ new_fields
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,23 @@
1
+ module Graphlyte
2
+ module Refinements
3
+ module StringRefinement
4
+ refine Symbol do
5
+ def to_camel_case
6
+ to_s.to_camel_case
7
+ end
8
+ end
9
+ refine String do
10
+ def to_camel_case
11
+ start_of_string = match(/(^_+)/)&.[](0)
12
+ end_of_string = match(/(_+$)/)&.[](0)
13
+
14
+ middle = split("_").reject(&:empty?).inject([]) do |memo, str|
15
+ memo << (memo.empty? ? str : str.capitalize)
16
+ end.join("")
17
+
18
+ "#{start_of_string}#{middle}#{end_of_string}"
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,14 @@
1
+ module Graphlyte
2
+ module Schema
3
+ module Types
4
+ class Base
5
+ attr_reader :name, :placeholder
6
+
7
+ def initialize(name, placeholder)
8
+ @name = name
9
+ @placeholder = placeholder
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,83 @@
1
+
2
+ module Graphlyte
3
+ module SchemaQuery
4
+ def schema_query
5
+ type_ref_fragment = Graphlyte.fragment('TypeRef', '__Type') do
6
+ kind
7
+ name
8
+ of_type {
9
+ kind
10
+ name
11
+ of_type {
12
+ kind
13
+ name
14
+ of_type {
15
+ kind
16
+ name
17
+ of_type {
18
+ kind
19
+ name
20
+ of_type {
21
+ kind
22
+ name
23
+ of_type {
24
+ kind
25
+ name
26
+ of_type {
27
+ kind
28
+ name
29
+ }
30
+ }
31
+ }
32
+ }
33
+ }
34
+ }
35
+ }
36
+ end
37
+
38
+ input_value_fragment = Graphlyte.fragment('InputValues', '__InputValue') do
39
+ name
40
+ description
41
+ type type_ref_fragment
42
+ default_value
43
+ end
44
+
45
+ full_type_fragment = Graphlyte.fragment('FullType', '__Type') do
46
+ kind
47
+ name
48
+ description
49
+ fields(includeDeprecated: true) do
50
+ name
51
+ description
52
+ args input_value_fragment
53
+ type type_ref_fragment
54
+ is_deprecated
55
+ deprecation_reason
56
+ end
57
+ input_fields input_value_fragment
58
+ interfaces type_ref_fragment
59
+ enum_values(includeDeprecated: true) do
60
+ name
61
+ description
62
+ is_deprecated
63
+ deprecation_reason
64
+ end
65
+ possible_types type_ref_fragment
66
+ end
67
+
68
+ Graphlyte.query do
69
+ __schema do
70
+ query_type { name }
71
+ mutation_type { name }
72
+ subscription_type { name }
73
+ types full_type_fragment
74
+ directives do
75
+ name
76
+ description
77
+ args input_value_fragment
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,9 @@
1
+ require_relative "schema/types/base"
2
+
3
+ module Graphlyte
4
+ class Types
5
+ def method_missing(method, placeholder)
6
+ Schema::Types::Base.new(method, placeholder)
7
+ end
8
+ end
9
+ end
data/lib/graphlyte.rb CHANGED
@@ -1,174 +1,36 @@
1
1
  require 'json'
2
- module Graphlyte
3
- def self.query(name = nil, &block)
4
- query = Query.new(name)
5
- block.call(query) if block
6
- query
7
- end
2
+ require_relative "./graphlyte/fieldset"
3
+ require_relative "./graphlyte/query"
4
+ require_relative "./graphlyte/fragment"
5
+ require_relative "./graphlyte/schema_query"
6
+ require_relative "./graphlyte/types"
8
7
 
9
- def self.fragment(fragment_name, model_name=nil, &block)
10
- fragment = Fragment.new(fragment_name, model_name)
11
- block.call(fragment) if block
12
- fragment
13
- end
14
-
15
- def self.fieldset(model_name=nil, &block)
16
- fieldset = Fieldset.new(model_name)
17
- block.call(fieldset) if block
18
- fieldset
19
- end
8
+ module Graphlyte
9
+ extend SchemaQuery
20
10
 
21
- module Buildable
22
- def <<(buildable)
23
- raise "Must pass a Fieldset or Fragment" unless [Fragment, Fieldset].include?(buildable.class)
24
- @fields.concat(buildable._fields) if buildable.class.eql? Fieldset
25
- @fields << buildable if buildable.class.eql? Fragment
26
- end
27
-
28
- def method_missing(method, optional_fieldset_or_args=nil, hargs={}, &block)
29
- field = [Fieldset, Fragment].include?(optional_fieldset_or_args.class) ?
30
- Field.new(method, optional_fieldset_or_args, hargs) :
31
- Field.new(method, Fieldset.empty, optional_fieldset_or_args)
32
- block.call(field.value) if block
33
- @fields << field
34
- field
35
- end
36
- end
37
-
38
- class Fieldset
39
- include Buildable
40
-
41
- def self.empty
42
- new
43
- end
44
-
45
- def initialize(model_name = nil)
46
- @model_name = model_name
47
- @fields = []
48
- end
49
-
50
- def _model_name
51
- @model_name
52
- end
53
-
54
- def _fields
55
- @fields
56
- end
11
+ TYPES = Types.new
57
12
 
58
- def empty?
59
- @fields.empty?
60
- end
61
-
62
- def can_validate?
63
- !@model_name.nil?
64
- end
65
-
66
- def to_s(indent=0)
67
- @fields.map { |field| field.to_s(indent)}.join("\n")
68
- end
13
+ def self.query(name = nil, &block)
14
+ Query.new(name, :query, builder: build(&block))
69
15
  end
70
16
 
71
- class Query < Fieldset
72
-
73
- def initialize(query_name=nil)
74
- @query_name = query_name
75
- @fields = []
76
- end
77
-
78
- def to_json
79
- { query: to_s }.to_json
80
- end
81
-
82
- def to_s(indent=0)
83
- "{\n#{super(indent + 1)}\n}\n#{format_fragments}\n"
84
- end
85
-
86
- def format_fragments
87
- str = ""
88
- flatten(@fields).each do |_, fragment|
89
- str += "\nfragment #{fragment.name}"
90
- str += " on #{fragment._model_name}" unless fragment._model_name.nil?
91
- str += " {\n#{fragment._fields.map {|f| f.to_s(1) }.join("\n")}\n}"
92
- end
93
- str
94
- end
95
-
96
- def flatten(fields=@fields, new_fields = {})
97
- fields.each do |field|
98
- if field.class.eql?(Fragment)
99
- new_fields[field.name] = field
100
- unless field._fields.empty?
101
- flatten(field._fields, new_fields)
102
- end
103
- else
104
- if field.value.class.eql?(Fragment)
105
- new_fields[field.value.name] = field.value
106
- flatten(field.value._fields, new_fields) unless field.value._fields.empty?
107
- else
108
- flatten(field.value._fields, new_fields) unless field.value._fields.empty?
109
- end
110
- end
111
- end
112
- new_fields
113
- end
17
+ def self.mutation(name = nil, &block)
18
+ Query.new(name, :mutation, builder: build(&block))
114
19
  end
115
20
 
116
- class Fragment < Fieldset
117
- attr_reader :fragment_name, :name
118
-
119
- def initialize(fragment_name, model_name=nil)
120
- @fragment_name = fragment_name
121
- @name = fragment_name
122
- super(model_name)
123
- end
124
-
125
- def to_s(indent=0)
126
- actual_indent = ("\s" * indent) * 2
127
- "#{actual_indent}...#{@fragment_name}#{actual_indent}"
128
- end
21
+ def self.fragment(fragment_name, model_name, &block)
22
+ Fragment.new(fragment_name, model_name, builder: build(&block))
129
23
  end
130
24
 
131
- class FieldArguments
132
- def initialize(data)
133
- @data = data
134
- end
135
-
136
- def to_s
137
- return @data && !@data.empty? ? "(#{@data.map{|k, v| "#{k}: \"#{v}\""}.join(", ")})" : ""
138
- end
25
+ def self.fieldset(model_name=nil, &block)
26
+ Fieldset.new(model_name, builder: build(&block))
139
27
  end
140
28
 
141
- class Field
142
- attr_reader :name, :value, :inputs, :alias
143
-
144
- def initialize(name, value=nil, hargs)
145
- @name = name
146
- @value = value
147
- @inputs = FieldArguments.new(hargs)
148
- @alias = nil
149
- end
150
-
151
- def atomic?
152
- value.empty?
153
- end
154
-
155
- def alias(name, &block)
156
- @alias = name
157
- block.call(value) if block
158
- end
29
+ private
159
30
 
160
- def to_s(indent=0)
161
- str = ""
162
- actual_indent = ("\s" * indent) * 2
163
- if @alias
164
- str += "#{actual_indent}#{@alias}: #{name}"
165
- str += inputs.to_s.empty? ? "()" : inputs.to_s
166
- str += " "
167
- else
168
- str += "#{actual_indent}#{name}#{inputs.to_s}"
169
- end
170
- str += "{\n#{value.to_s(indent + 1)}#{actual_indent}\n#{actual_indent}}" unless atomic?
171
- str
172
- end
31
+ def self.build(&block)
32
+ builder = Builder.new
33
+ builder.>.instance_eval(&block)
34
+ builder
173
35
  end
174
36
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graphlyte
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sean Gregory
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-07-11 00:00:00.000000000 Z
11
+ date: 2021-11-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -45,6 +45,17 @@ extensions: []
45
45
  extra_rdoc_files: []
46
46
  files:
47
47
  - lib/graphlyte.rb
48
+ - lib/graphlyte/arguments/set.rb
49
+ - lib/graphlyte/arguments/value.rb
50
+ - lib/graphlyte/builder.rb
51
+ - lib/graphlyte/field.rb
52
+ - lib/graphlyte/fieldset.rb
53
+ - lib/graphlyte/fragment.rb
54
+ - lib/graphlyte/query.rb
55
+ - lib/graphlyte/refinements/string_refinement.rb
56
+ - lib/graphlyte/schema/types/base.rb
57
+ - lib/graphlyte/schema_query.rb
58
+ - lib/graphlyte/types.rb
48
59
  homepage: https://rubygems.org/gems/graphlyte
49
60
  licenses:
50
61
  - MIT
@@ -65,7 +76,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
65
76
  - !ruby/object:Gem::Version
66
77
  version: '0'
67
78
  requirements: []
68
- rubygems_version: 3.2.3
79
+ rubygems_version: 3.2.22
69
80
  signing_key:
70
81
  specification_version: 4
71
82
  summary: craft graphql queries with ruby