graphlyte 0.1.2 → 0.1.6

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.
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