gqli 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (89) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +7 -0
  3. data/.rspec +3 -0
  4. data/.rubocop.yml +31 -0
  5. data/.rubocop_todo.yml +44 -0
  6. data/.travis.yml +13 -0
  7. data/.yardopts +4 -0
  8. data/CHANGELOG.md +9 -0
  9. data/Gemfile +9 -0
  10. data/Gemfile.lock +154 -0
  11. data/Guardfile +24 -0
  12. data/LICENSE.txt +21 -0
  13. data/README.md +284 -0
  14. data/Rakefile +33 -0
  15. data/coverage/.last_run.json +5 -0
  16. data/coverage/.resultset.json +512 -0
  17. data/coverage/.resultset.json.lock +0 -0
  18. data/coverage/assets/0.10.2/application.css +799 -0
  19. data/coverage/assets/0.10.2/application.js +1707 -0
  20. data/coverage/assets/0.10.2/colorbox/border.png +0 -0
  21. data/coverage/assets/0.10.2/colorbox/controls.png +0 -0
  22. data/coverage/assets/0.10.2/colorbox/loading.gif +0 -0
  23. data/coverage/assets/0.10.2/colorbox/loading_background.png +0 -0
  24. data/coverage/assets/0.10.2/favicon_green.png +0 -0
  25. data/coverage/assets/0.10.2/favicon_red.png +0 -0
  26. data/coverage/assets/0.10.2/favicon_yellow.png +0 -0
  27. data/coverage/assets/0.10.2/loading.gif +0 -0
  28. data/coverage/assets/0.10.2/magnify.png +0 -0
  29. data/coverage/assets/0.10.2/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  30. data/coverage/assets/0.10.2/smoothness/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  31. data/coverage/assets/0.10.2/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  32. data/coverage/assets/0.10.2/smoothness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  33. data/coverage/assets/0.10.2/smoothness/images/ui-bg_glass_75_dadada_1x400.png +0 -0
  34. data/coverage/assets/0.10.2/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  35. data/coverage/assets/0.10.2/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  36. data/coverage/assets/0.10.2/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  37. data/coverage/assets/0.10.2/smoothness/images/ui-icons_222222_256x240.png +0 -0
  38. data/coverage/assets/0.10.2/smoothness/images/ui-icons_2e83ff_256x240.png +0 -0
  39. data/coverage/assets/0.10.2/smoothness/images/ui-icons_454545_256x240.png +0 -0
  40. data/coverage/assets/0.10.2/smoothness/images/ui-icons_888888_256x240.png +0 -0
  41. data/coverage/assets/0.10.2/smoothness/images/ui-icons_cd0a0a_256x240.png +0 -0
  42. data/coverage/index.html +3255 -0
  43. data/doc/GQLi/Base.html +628 -0
  44. data/doc/GQLi/Client.html +830 -0
  45. data/doc/GQLi/DSL.html +421 -0
  46. data/doc/GQLi/Fragment.html +405 -0
  47. data/doc/GQLi/Introspection.html +837 -0
  48. data/doc/GQLi/Node.html +413 -0
  49. data/doc/GQLi/Query.html +390 -0
  50. data/doc/GQLi/Response.html +370 -0
  51. data/doc/GQLi.html +157 -0
  52. data/doc/_index.html +236 -0
  53. data/doc/class_list.html +51 -0
  54. data/doc/css/common.css +1 -0
  55. data/doc/css/full_list.css +58 -0
  56. data/doc/css/style.css +496 -0
  57. data/doc/file.CHANGELOG.html +76 -0
  58. data/doc/file.LICENSE.html +70 -0
  59. data/doc/file.README.html +365 -0
  60. data/doc/file_list.html +66 -0
  61. data/doc/frames.html +17 -0
  62. data/doc/index.html +365 -0
  63. data/doc/js/app.js +292 -0
  64. data/doc/js/full_list.js +216 -0
  65. data/doc/js/jquery.js +4 -0
  66. data/doc/method_list.html +355 -0
  67. data/doc/top-level-namespace.html +110 -0
  68. data/gqli.gemspec +37 -0
  69. data/lib/gqli/base.rb +53 -0
  70. data/lib/gqli/client.rb +59 -0
  71. data/lib/gqli/dsl.rb +37 -0
  72. data/lib/gqli/fragment.rb +25 -0
  73. data/lib/gqli/introspection.rb +215 -0
  74. data/lib/gqli/node.rb +48 -0
  75. data/lib/gqli/query.rb +29 -0
  76. data/lib/gqli/response.rb +15 -0
  77. data/lib/gqli/version.rb +7 -0
  78. data/lib/gqli.rb +6 -0
  79. data/spec/fixtures/vcr_cassettes/catCollection.yml +63 -0
  80. data/spec/fixtures/vcr_cassettes/client.yml +171 -0
  81. data/spec/fixtures/vcr_cassettes/validation_error.yml +62 -0
  82. data/spec/lib/gqli/client_spec.rb +90 -0
  83. data/spec/lib/gqli/dsl_spec.rb +205 -0
  84. data/spec/lib/gqli/introspection_spec.rb +125 -0
  85. data/spec/spec_helper.rb +25 -0
  86. data/usage.rb +100 -0
  87. data/usage_introspection.rb +63 -0
  88. data/usage_with_inlined_dsl.rb +69 -0
  89. metadata +387 -0
@@ -0,0 +1,90 @@
1
+ require 'spec_helper'
2
+
3
+ describe GQLi::Client do
4
+ let(:client) do
5
+ vcr('client') {
6
+ space_id = 'cfexampleapi'
7
+ token = 'b4c0n73n7fu1'
8
+ GQLi::Client.new(
9
+ "https://graphql.contentful.com/content/v1/spaces/#{space_id}",
10
+ headers: { "Authorization" => "Bearer #{token}" }
11
+ )
12
+ }
13
+ end
14
+
15
+ let(:client_no_validations) do
16
+ vcr('client') {
17
+ space_id = 'cfexampleapi'
18
+ token = 'b4c0n73n7fu1'
19
+ GQLi::Client.new(
20
+ "https://graphql.contentful.com/content/v1/spaces/#{space_id}",
21
+ headers: { "Authorization" => "Bearer #{token}" },
22
+ validate_query: false
23
+ )
24
+ }
25
+ end
26
+
27
+ let(:dsl) { GQLi::DSL }
28
+
29
+ context 'validations enabled' do
30
+ subject { client }
31
+
32
+ it 'upon instantiation caches the schema' do
33
+ expect(subject.schema.types).not_to be_empty
34
+ end
35
+
36
+ it 'when executing a query performs schema validation' do
37
+ vcr('catCollection') {
38
+ expect(subject.execute(
39
+ dsl.query {
40
+ catCollection {
41
+ items {
42
+ name
43
+ }
44
+ }
45
+ }).data
46
+ ).not_to be_empty
47
+
48
+ expect { subject.execute(
49
+ dsl.query {
50
+ foobar
51
+ }
52
+ )}.to raise_exception 'Validation Error: query is invalid - HTTP Request not sent'
53
+ }
54
+ end
55
+
56
+ it 'can skip validations when using `#execute!`' do
57
+ vcr('validation_error') {
58
+ expect { subject.execute!(
59
+ dsl.query {
60
+ foobar
61
+ }
62
+ )}.to raise_exception([
63
+ "Error: Bad Request",
64
+ 'Body: {"errors":[{"message":"Cannot query field \"foobar\" on type \"Query\".","locations":[{"line":2,"column":3}]}]}'
65
+ ].join("\n"))
66
+ }
67
+ end
68
+ end
69
+
70
+ context 'validations disabled' do
71
+ subject { client_no_validations }
72
+
73
+ it 'upon instantiation doesnt cache the schema' do
74
+ expect(subject.schema).to be_nil
75
+ end
76
+
77
+ it 'when executing a query doesnt perform schema validation' do
78
+ vcr('validation_error') {
79
+ expect { subject.execute(
80
+ dsl.query {
81
+ foobar
82
+ }
83
+ )}.to raise_exception([
84
+ "Error: Bad Request",
85
+ 'Body: {"errors":[{"message":"Cannot query field \"foobar\" on type \"Query\".","locations":[{"line":2,"column":3}]}]}'
86
+ ].join("\n"))
87
+ }
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,205 @@
1
+ require 'spec_helper'
2
+
3
+ class MockGQLInterface
4
+ include GQLi::DSL
5
+ end
6
+
7
+ describe GQLi::DSL do
8
+ subject { described_class }
9
+
10
+ describe 'module level methods' do
11
+ describe '::query' do
12
+ it 'can create a query without name' do
13
+ query = subject.query {
14
+ someField
15
+ }
16
+
17
+ expect(query).to be_a GQLi::Query
18
+ expect(query.to_gql).to eq <<~GRAPHQL
19
+ query {
20
+ someField
21
+ }
22
+ GRAPHQL
23
+ end
24
+
25
+ it 'can create a query with a name' do
26
+ query = subject.query('FooBar') {
27
+ someOtherField
28
+ }
29
+
30
+ expect(query).to be_a GQLi::Query
31
+ expect(query.to_gql).to eq <<~GRAPHQL
32
+ query FooBar {
33
+ someOtherField
34
+ }
35
+ GRAPHQL
36
+ end
37
+ end
38
+
39
+ describe '::fragment' do
40
+ it 'can create a fragment' do
41
+ fragment = subject.fragment('FooBar', 'Foo') {
42
+ someFooField
43
+ }
44
+
45
+ expect(fragment).to be_a GQLi::Fragment
46
+ expect(fragment.to_gql).to eq <<~GRAPHQL
47
+ fragment FooBar on Foo {
48
+ someFooField
49
+ }
50
+ GRAPHQL
51
+ end
52
+ end
53
+ end
54
+
55
+ describe 'instance level methods' do
56
+ subject { MockGQLInterface.new }
57
+
58
+ describe '#query does the same as ::query' do
59
+ it 'can create a query without name' do
60
+ query = subject.query {
61
+ someField
62
+ }
63
+
64
+ expect(query).to be_a GQLi::Query
65
+ expect(query.to_gql).to eq <<~GRAPHQL
66
+ query {
67
+ someField
68
+ }
69
+ GRAPHQL
70
+ end
71
+
72
+ it 'can create a query with a name' do
73
+ query = subject.query('FooBar') {
74
+ someOtherField
75
+ }
76
+
77
+ expect(query).to be_a GQLi::Query
78
+ expect(query.to_gql).to eq <<~GRAPHQL
79
+ query FooBar {
80
+ someOtherField
81
+ }
82
+ GRAPHQL
83
+ end
84
+ end
85
+
86
+ describe '#fragment does the same as ::fragment' do
87
+ it 'can create a fragment' do
88
+ fragment = subject.fragment('FooBar', 'Foo') {
89
+ someFooField
90
+ }
91
+
92
+ expect(fragment).to be_a GQLi::Fragment
93
+ expect(fragment.to_gql).to eq <<~GRAPHQL
94
+ fragment FooBar on Foo {
95
+ someFooField
96
+ }
97
+ GRAPHQL
98
+ end
99
+ end
100
+ end
101
+
102
+ describe 'node DSL' do
103
+ it 'nodes can have arguments' do
104
+ query = subject.query {
105
+ someNode(arg1: 100)
106
+ otherNode(arg2: 'some_string')
107
+ moreNodes(arg3: { nestedArg: [1, 2] })
108
+ }
109
+
110
+ expect(query.to_gql).to eq <<~GRAPHQL
111
+ query {
112
+ someNode(arg1: 100)
113
+ otherNode(arg2: "some_string")
114
+ moreNodes(arg3: {nestedArg: [1, 2]})
115
+ }
116
+ GRAPHQL
117
+ end
118
+
119
+ it 'nodes can have arbitrarily nested nodes' do
120
+ query = subject.query {
121
+ aNode {
122
+ withChild {
123
+ moreChildren
124
+ andASibiling(someParameterHere: 'just for fun') {
125
+ withEvenMore
126
+ }
127
+ }
128
+ alsoASibiling
129
+ }
130
+ withoutChild
131
+ }
132
+
133
+ expect(query.to_gql).to eq <<~GRAPHQL
134
+ query {
135
+ aNode {
136
+ withChild {
137
+ moreChildren
138
+ andASibiling(someParameterHere: "just for fun") {
139
+ withEvenMore
140
+ }
141
+ }
142
+ alsoASibiling
143
+ }
144
+ withoutChild
145
+ }
146
+ GRAPHQL
147
+ end
148
+
149
+ it 'queries can have inlined fragments at any level' do
150
+ NameFragment = subject.fragment('NameFragment', 'Foo') {
151
+ name
152
+ }
153
+
154
+ AgeFragment = subject.fragment('AgeFragment', 'Foo') {
155
+ age
156
+ }
157
+
158
+ query = subject.query {
159
+ peopleCollection {
160
+ ___ NameFragment
161
+ ___ AgeFragment
162
+ }
163
+ }
164
+
165
+ expect(query.to_gql).to eq <<~GRAPHQL
166
+ query {
167
+ peopleCollection {
168
+ name
169
+ age
170
+ }
171
+ }
172
+ GRAPHQL
173
+ end
174
+
175
+ it 'queries can have type matchers' do
176
+ query = subject.query {
177
+ catCollection {
178
+ items {
179
+ name
180
+ bestFriend {
181
+ __on('Cat') {
182
+ name
183
+ }
184
+ }
185
+ }
186
+ }
187
+ }
188
+
189
+ expect(query.to_gql).to eq <<~GRAPHQL
190
+ query {
191
+ catCollection {
192
+ items {
193
+ name
194
+ bestFriend {
195
+ ... on Cat {
196
+ name
197
+ }
198
+ }
199
+ }
200
+ }
201
+ }
202
+ GRAPHQL
203
+ end
204
+ end
205
+ end
@@ -0,0 +1,125 @@
1
+ require 'spec_helper'
2
+
3
+ describe GQLi::Introspection do
4
+ let(:dsl) { GQLi::DSL }
5
+ let(:client) do
6
+ vcr('client') {
7
+ space_id = 'cfexampleapi'
8
+ token = 'b4c0n73n7fu1'
9
+ GQLi::Client.new(
10
+ "https://graphql.contentful.com/content/v1/spaces/#{space_id}",
11
+ headers: { "Authorization" => "Bearer #{token}" }
12
+ )
13
+ }
14
+ end
15
+
16
+ subject { client.schema }
17
+
18
+ describe 'introspection schema' do
19
+ it 'queries the API for the schema' do
20
+ expect(subject.types).not_to be_empty
21
+
22
+ expect(subject.types.map(&:name)).to include('Cat', 'CatCollection', 'Human')
23
+ end
24
+ end
25
+
26
+ describe 'validations' do
27
+ it 'valid query returns true' do
28
+ query = dsl.query {
29
+ catCollection(
30
+ locale:"en-US",
31
+ limit: 1,
32
+ where: {
33
+ name:"Nyan Cat",
34
+ OR: {
35
+ name:"Happy Cat"
36
+ }
37
+ }
38
+ ) {
39
+ items {
40
+ name
41
+ color
42
+ birthday
43
+ lives
44
+ bestFriend {
45
+ __on('Cat') {
46
+ name
47
+ }
48
+ }
49
+ image {
50
+ url
51
+ }
52
+ }
53
+ }
54
+ }
55
+
56
+ expect(subject.valid?(query)).to be_truthy
57
+ end
58
+
59
+ it 'wrong node returns false' do
60
+ query = dsl.query {
61
+ foo
62
+ }
63
+
64
+ expect(subject.valid?(query)).to be_falsey
65
+ end
66
+
67
+ it 'object node that doesnt have proper values returns false' do
68
+ query = dsl.query {
69
+ catCollection
70
+ }
71
+
72
+ expect(subject.valid?(query)).to be_falsey
73
+ end
74
+
75
+ it 'object list node that doesnt have proper values returns false' do
76
+ query = dsl.query {
77
+ catCollection {
78
+ items
79
+ }
80
+ }
81
+
82
+ expect(subject.valid?(query)).to be_falsey
83
+ end
84
+
85
+ it 'type matching on invalid type returns false' do
86
+ query = dsl.query {
87
+ catCollection {
88
+ items {
89
+ bestFriend {
90
+ __on('InvalidType') {
91
+ foo
92
+ }
93
+ }
94
+ }
95
+ }
96
+ }
97
+
98
+ expect(subject.valid?(query)).to be_falsey
99
+ end
100
+
101
+ it 'invalid arguments return false' do
102
+ query = dsl.query {
103
+ catCollection(invalidParam: 1) {
104
+ items {
105
+ name
106
+ }
107
+ }
108
+ }
109
+
110
+ expect(subject.valid?(query)).to be_falsey
111
+ end
112
+
113
+ it 'invalid argument type returns false' do
114
+ query = dsl.query {
115
+ catCollection(limit: 'foo') {
116
+ items {
117
+ name
118
+ }
119
+ }
120
+ }
121
+
122
+ expect(subject.valid?(query)).to be_falsey
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,25 @@
1
+ require 'simplecov'
2
+ SimpleCov.start
3
+
4
+ require 'rspec'
5
+ require 'vcr'
6
+
7
+ Dir[File.join('..', File.dirname(__FILE__), 'lib', '**', '*.rb')].each { |f| require f }
8
+
9
+ require 'gqli'
10
+
11
+ RSpec.configure do |config|
12
+ config.filter_run :focus => true
13
+ config.run_all_when_everything_filtered = true
14
+ end
15
+
16
+ VCR.configure do |c|
17
+ c.cassette_library_dir = 'spec/fixtures/vcr_cassettes'
18
+ c.ignore_localhost = true
19
+ c.hook_into :webmock
20
+ c.default_cassette_options = { record: :once }
21
+ end
22
+
23
+ def vcr(name, &block)
24
+ VCR.use_cassette(name, &block)
25
+ end
data/usage.rb ADDED
@@ -0,0 +1,100 @@
1
+ require_relative './lib/gqli'
2
+
3
+ puts GQLi::DSL.query {
4
+ __on('Foo') {
5
+ bar
6
+ }
7
+ }.to_gql
8
+
9
+
10
+ puts GQLi::DSL.query {
11
+ viewer {
12
+ login
13
+ repositories(first: 10) {
14
+ edges {
15
+ node {
16
+ nameWithOwner
17
+ watchers(first: 10) {
18
+ edges {
19
+ node {
20
+ login
21
+ }
22
+ }
23
+ }
24
+ }
25
+ }
26
+ }
27
+ }
28
+ }.to_gql
29
+
30
+ FragmentFoo = GQLi::DSL.fragment('Foo', 'Bar') {
31
+ foo
32
+ bar(b: 10)
33
+ baz {
34
+ qux(c: 3)
35
+ }
36
+ }
37
+
38
+ puts FragmentFoo.to_gql
39
+
40
+ puts GQLi::DSL.query {
41
+ ___ FragmentFoo
42
+ }.to_gql
43
+
44
+ puts "-------------"
45
+
46
+ SPACE_ID = 'cfexampleapi'
47
+ ACCESS_TOKEN = 'b4c0n73n7fu1'
48
+ CONTENTFUL_GQL = GQLi::Client.new(
49
+ "https://graphql.contentful.com/content/v1/spaces/#{SPACE_ID}",
50
+ headers: { "Authorization" => "Bearer #{ACCESS_TOKEN}" }
51
+ )
52
+
53
+ CatBase = GQLi::DSL.fragment('CatBase', 'Cat') {
54
+ name
55
+ likes
56
+ lives
57
+ }
58
+
59
+ CatBestFriend = GQLi::DSL.fragment('CatBestFriend', 'Cat') {
60
+ bestFriend {
61
+ __on('Cat') {
62
+ ___ CatBase
63
+ }
64
+ }
65
+ }
66
+
67
+ CatImportantFields = GQLi::DSL.fragment('CatImportantFields', 'Cat') {
68
+ ___ CatBase
69
+ ___ CatBestFriend
70
+ }
71
+
72
+ Query = GQLi::DSL.query {
73
+ catCollection(limit: 1) {
74
+ items {
75
+ ___ CatImportantFields
76
+ image {
77
+ url
78
+ }
79
+ }
80
+ }
81
+ }
82
+
83
+ response = CONTENTFUL_GQL.execute(Query)
84
+
85
+ puts "Query sent:"
86
+ puts response.query.to_gql
87
+
88
+ puts
89
+ puts "Response received"
90
+ response.data.catCollection.items.each do |c|
91
+ puts "Name: #{c.nam}e"
92
+ puts "Likes: #{c.likes.join(", ")}"
93
+ puts "Lives #: #{c.lives}"
94
+ c.bestFriend.tap do |bf|
95
+ puts "Best Friend:"
96
+ puts "\tName: #{bf.name}"
97
+ puts "\tLikes: #{bf.likes.join(", ")}"
98
+ puts "\tLives #: #{bf.lives}"
99
+ end
100
+ end
@@ -0,0 +1,63 @@
1
+ require_relative './lib/gqli'
2
+
3
+ SPACE_ID = 'cfexampleapi'
4
+ ACCESS_TOKEN = 'b4c0n73n7fu1'
5
+ CONTENTFUL_GQL = GQLi::Client.new(
6
+ "https://graphql.contentful.com/content/v1/spaces/#{SPACE_ID}",
7
+ headers: { "Authorization" => "Bearer #{ACCESS_TOKEN}" }
8
+ )
9
+
10
+ CatBase = GQLi::DSL.fragment('CatBase', 'Cat') {
11
+ name
12
+ likes
13
+ lives
14
+ }
15
+
16
+ CatBestFriend = GQLi::DSL.fragment('CatBestFriend', 'Cat') {
17
+ bestFriend {
18
+ __on('Cat') {
19
+ ___ CatBase
20
+ }
21
+ }
22
+ }
23
+
24
+ CatImportantFields = GQLi::DSL.fragment('CatImportantFields', 'Cat') {
25
+ ___ CatBase
26
+ ___ CatBestFriend
27
+ }
28
+
29
+ Query = GQLi::DSL.query {
30
+ catCollection(limit: 1) {
31
+ items {
32
+ ___ CatImportantFields
33
+ image {
34
+ url
35
+ }
36
+ }
37
+ }
38
+ }
39
+
40
+ response = CONTENTFUL_GQL.execute(Query)
41
+
42
+ puts "Query sent:"
43
+ puts response.query.to_gql
44
+
45
+ puts
46
+ puts "Response received"
47
+ response.data.catCollection.items.each do |c|
48
+ puts "Name: #{c.nam}e"
49
+ puts "Likes: #{c.likes.join(", ")}"
50
+ puts "Lives #: #{c.lives}"
51
+ c.bestFriend.tap do |bf|
52
+ puts "Best Friend:"
53
+ puts "\tName: #{bf.name}"
54
+ puts "\tLikes: #{bf.likes.join(", ")}"
55
+ puts "\tLives #: #{bf.lives}"
56
+ end
57
+ end
58
+
59
+ puts "Trying to execute invalid query"
60
+
61
+ CONTENTFUL_GQL.execute!(GQLi::DSL.query {
62
+ invalidNode
63
+ })
@@ -0,0 +1,69 @@
1
+ require_relative './lib/gqli'
2
+
3
+ class ContentfulClient
4
+ extend GQLi::DSL # Makes DSL available at a class level
5
+ include GQLi::DSL # Makes DSL available at an object level
6
+
7
+ SPACE_ID = 'cfexampleapi'
8
+ ACCESS_TOKEN = 'b4c0n73n7fu1'
9
+ CONTENTFUL_GQL = GQLi::Client.new(
10
+ "https://graphql.contentful.com/content/v1/spaces/#{SPACE_ID}",
11
+ headers: { "Authorization" => "Bearer #{ACCESS_TOKEN}" }
12
+ )
13
+
14
+ CatBase = fragment('CatBase', 'Cat') {
15
+ name
16
+ likes
17
+ lives
18
+ }
19
+
20
+ CatBestFriend = fragment('CatBestFriend', 'Cat') {
21
+ bestFriend {
22
+ __on('Cat') {
23
+ ___ CatBase
24
+ }
25
+ }
26
+ }
27
+
28
+ CatImportantFields = fragment('CatImportantFields', 'Cat') {
29
+ ___ CatBase
30
+ ___ CatBestFriend
31
+ }
32
+
33
+ def cats
34
+ CONTENTFUL_GQL.execute(
35
+ query {
36
+ catCollection(limit: 10, locale: 'tlh') {
37
+ items {
38
+ ___ CatImportantFields
39
+ image {
40
+ url
41
+ }
42
+ }
43
+ }
44
+ }
45
+ )
46
+ end
47
+ end
48
+
49
+ response = ContentfulClient.new.cats
50
+
51
+ puts "Query sent:"
52
+ puts response.query.to_gql
53
+
54
+ puts
55
+ puts "Response received"
56
+ response.data.catCollection.items.each do |c|
57
+ puts "Name: #{c.name}"
58
+ puts "Likes: #{c.likes.join(", ")}"
59
+ puts "Lives #: #{c.lives}"
60
+ puts "Image: #{c.image.url}" if c.image?
61
+ c.bestFriend.tap do |bf|
62
+ next if bf.nil?
63
+ puts "Best Friend:"
64
+ puts "\tName: #{bf.name}"
65
+ puts "\tLikes: #{bf.likes.join(", ")}"
66
+ puts "\tLives #: #{bf.lives}"
67
+ end
68
+ puts
69
+ end