graphqlite 0.1.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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +46 -0
- data/LICENSE +21 -0
- data/README.md +339 -0
- data/lib/graphqlite/errors.rb +7 -0
- data/lib/graphqlite/executor.rb +380 -0
- data/lib/graphqlite/introspection.rb +222 -0
- data/lib/graphqlite/lexer.rb +266 -0
- data/lib/graphqlite/parser.rb +354 -0
- data/lib/graphqlite/schema.rb +238 -0
- data/lib/graphqlite/types.rb +336 -0
- data/lib/graphqlite/validator.rb +183 -0
- data/lib/graphqlite/version.rb +3 -0
- data/lib/graphqlite.rb +18 -0
- metadata +85 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: d664c52492cd6c6123fd189d696821d2451226faa81da7a907a7b6f8122f1149
|
|
4
|
+
data.tar.gz: 8a9ba16f20ab76d50eec3193df266ee9408a2d9903d55dd5af25fc26ec4b2d2a
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: ae10a69e2a419d5f999a35fc25ac627f0f178c5da708c49fe2dd377f09909b321fe543a244f0bf885e1be16dd293838d2f3e95d3b94ea93a2b85b84529b6c1a3
|
|
7
|
+
data.tar.gz: 4eadc6a3cf8446dcca32e06a020ce5d2658a305175008a55e3403aa7d1414153243ba90ae9e75634ebe95865463d65e4bea3d26216ec8fce820723d3c1bc3250
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [1.0.0] - 2025-01-17
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- Initial release of GraphQLite
|
|
12
|
+
- Complete GraphQL spec compliance (October 2021)
|
|
13
|
+
- Zero runtime dependencies
|
|
14
|
+
- Full lexer and parser implementation
|
|
15
|
+
- Complete type system (Scalar, Object, Interface, Union, Enum, InputObject)
|
|
16
|
+
- Query execution engine
|
|
17
|
+
- Validation system
|
|
18
|
+
- Full introspection support
|
|
19
|
+
- Clean DSL for schema definition
|
|
20
|
+
- Support for queries, mutations, and subscriptions
|
|
21
|
+
- Built-in scalar types (Int, Float, String, Boolean, ID)
|
|
22
|
+
- Custom scalar type support
|
|
23
|
+
- Field arguments and resolvers
|
|
24
|
+
- Variable support
|
|
25
|
+
- Context passing
|
|
26
|
+
- Comprehensive error handling
|
|
27
|
+
- Full test suite
|
|
28
|
+
- Documentation and examples
|
|
29
|
+
|
|
30
|
+
### Features
|
|
31
|
+
- **Simple DSL**: Easy-to-use schema definition with minimal boilerplate
|
|
32
|
+
- **Fast Execution**: Efficient parser and executor
|
|
33
|
+
- **Production Ready**: Comprehensive validation and error handling
|
|
34
|
+
- **Type Safe**: Full type checking and validation
|
|
35
|
+
- **Extensible**: Easy to add custom scalars and directives
|
|
36
|
+
|
|
37
|
+
## [Unreleased]
|
|
38
|
+
|
|
39
|
+
### Planned
|
|
40
|
+
- Subscription support (real-time updates)
|
|
41
|
+
- Custom directive implementation
|
|
42
|
+
- Query complexity analysis
|
|
43
|
+
- Performance optimizations
|
|
44
|
+
- Additional validation rules
|
|
45
|
+
- Schema stitching support
|
|
46
|
+
- Federation support
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 GraphQLite Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
# GraphQLite
|
|
2
|
+
|
|
3
|
+
## Description
|
|
4
|
+
|
|
5
|
+
A lightweight, production-ready GraphQL implementation for Ruby with zero dependencies. GraphQLite is designed to be simple, minimal, and clean while maintaining full GraphQL spec compliance. It's easier to use and more straightforward than existing solutions.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- Zero runtime dependencies - pure Ruby implementation
|
|
10
|
+
- Simple, intuitive DSL for schema definition
|
|
11
|
+
- Full GraphQL spec compliance (October 2021)
|
|
12
|
+
- Production-ready with comprehensive error handling
|
|
13
|
+
- Complete introspection support
|
|
14
|
+
- Fast execution with efficient parser
|
|
15
|
+
- Clean, maintainable codebase
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
Add to your Gemfile:
|
|
20
|
+
|
|
21
|
+
```ruby
|
|
22
|
+
gem 'graphqlite'
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Or install directly:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
gem install graphqlite
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Basic Usage
|
|
32
|
+
|
|
33
|
+
```ruby
|
|
34
|
+
require 'graphqlite'
|
|
35
|
+
|
|
36
|
+
schema = GraphQLite::Schema.new do
|
|
37
|
+
query do
|
|
38
|
+
field :hello, :String do
|
|
39
|
+
"World"
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
result = schema.execute('{ hello }')
|
|
45
|
+
# => { "data" => { "hello" => "World" } }
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Available Usages
|
|
49
|
+
|
|
50
|
+
### Define Object Types
|
|
51
|
+
|
|
52
|
+
```ruby
|
|
53
|
+
object :User do
|
|
54
|
+
field :id, :ID, null: false
|
|
55
|
+
field :name, :String
|
|
56
|
+
field :email, :String
|
|
57
|
+
end
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Query with Arguments
|
|
61
|
+
|
|
62
|
+
```ruby
|
|
63
|
+
query do
|
|
64
|
+
field :user, :User do |f|
|
|
65
|
+
f.argument :id, :ID
|
|
66
|
+
f.resolve do |args|
|
|
67
|
+
User.find(args[:id])
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Lists and Non-Null Types
|
|
74
|
+
|
|
75
|
+
```ruby
|
|
76
|
+
query do
|
|
77
|
+
field :users, [:User] do # List of users
|
|
78
|
+
User.all
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
field :count, :Int, null: false do # Required field
|
|
82
|
+
User.count
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Mutations
|
|
88
|
+
|
|
89
|
+
```ruby
|
|
90
|
+
mutation do
|
|
91
|
+
field :createUser, :User do |f|
|
|
92
|
+
f.argument :name, :String
|
|
93
|
+
f.argument :email, :String
|
|
94
|
+
f.resolve do |args|
|
|
95
|
+
User.create(name: args[:name], email: args[:email])
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Execute with Variables
|
|
102
|
+
|
|
103
|
+
```ruby
|
|
104
|
+
query = 'query GetUser($id: ID!) { user(id: $id) { name } }'
|
|
105
|
+
result = schema.execute(query, variables: { 'id' => '123' })
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Execute with Context
|
|
109
|
+
|
|
110
|
+
```ruby
|
|
111
|
+
result = schema.execute(
|
|
112
|
+
'{ me { name } }',
|
|
113
|
+
context: { current_user: current_user }
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
# Access in resolvers
|
|
117
|
+
field :me, :User do |_, args, context|
|
|
118
|
+
context[:current_user]
|
|
119
|
+
end
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Advanced Usage
|
|
123
|
+
|
|
124
|
+
### Custom Resolvers
|
|
125
|
+
|
|
126
|
+
```ruby
|
|
127
|
+
object :User do
|
|
128
|
+
field :fullName, :String do |user|
|
|
129
|
+
"#{user[:first_name]} #{user[:last_name]}"
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
field :posts, [:Post] do |user|
|
|
133
|
+
Post.where(user_id: user[:id])
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Enum Types
|
|
139
|
+
|
|
140
|
+
```ruby
|
|
141
|
+
enum :Role, values: {
|
|
142
|
+
'ADMIN' => { value: 'admin', description: 'Administrator' },
|
|
143
|
+
'USER' => { value: 'user', description: 'Regular user' },
|
|
144
|
+
'GUEST' => { value: 'guest', description: 'Guest user' }
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
object :User do
|
|
148
|
+
field :role, :Role
|
|
149
|
+
end
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Custom Scalars
|
|
153
|
+
|
|
154
|
+
```ruby
|
|
155
|
+
scalar :DateTime,
|
|
156
|
+
description: 'ISO 8601 datetime',
|
|
157
|
+
serialize: ->(value) { value.iso8601 },
|
|
158
|
+
parse_value: ->(value) { Time.parse(value) },
|
|
159
|
+
parse_literal: ->(value) {
|
|
160
|
+
value.is_a?(Parser::StringValue) ? Time.parse(value.value) : nil
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Introspection
|
|
165
|
+
|
|
166
|
+
```ruby
|
|
167
|
+
# Schema introspection
|
|
168
|
+
schema.execute('{ __schema { types { name } } }')
|
|
169
|
+
|
|
170
|
+
# Type introspection
|
|
171
|
+
schema.execute('{ __type(name: "User") { name fields { name } } }')
|
|
172
|
+
|
|
173
|
+
# Typename in queries
|
|
174
|
+
schema.execute('{ user { __typename id } }')
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## Detailed Documentation
|
|
178
|
+
|
|
179
|
+
### Built-in Scalar Types
|
|
180
|
+
|
|
181
|
+
- `Int` - 32-bit signed integer
|
|
182
|
+
- `Float` - Double-precision floating-point
|
|
183
|
+
- `String` - UTF-8 character sequence
|
|
184
|
+
- `Boolean` - true or false
|
|
185
|
+
- `ID` - Unique identifier (serialized as string)
|
|
186
|
+
|
|
187
|
+
### Schema Definition API
|
|
188
|
+
|
|
189
|
+
**Object Types**
|
|
190
|
+
```ruby
|
|
191
|
+
object :TypeName do
|
|
192
|
+
field :fieldName, :FieldType
|
|
193
|
+
field :requiredField, :Type, null: false
|
|
194
|
+
field :listField, [:Type]
|
|
195
|
+
field :computedField, :Type do |object, args, context|
|
|
196
|
+
# resolver logic
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
**Fields with Arguments**
|
|
202
|
+
```ruby
|
|
203
|
+
field :search, [:User] do |f|
|
|
204
|
+
f.argument :query, :String
|
|
205
|
+
f.argument :limit, :Int
|
|
206
|
+
f.resolve do |args, context|
|
|
207
|
+
User.search(args[:query]).limit(args[:limit] || 10)
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
**Query Root**
|
|
213
|
+
```ruby
|
|
214
|
+
query do
|
|
215
|
+
field :fieldName, :Type do |f|
|
|
216
|
+
f.argument :arg, :ArgType
|
|
217
|
+
f.resolve { |args| ... }
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
**Mutation Root**
|
|
223
|
+
```ruby
|
|
224
|
+
mutation do
|
|
225
|
+
field :actionName, :ReturnType do |f|
|
|
226
|
+
f.argument :input, :InputType
|
|
227
|
+
f.resolve { |args| ... }
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### Query Execution API
|
|
233
|
+
|
|
234
|
+
```ruby
|
|
235
|
+
# Basic execution
|
|
236
|
+
schema.execute(query_string)
|
|
237
|
+
|
|
238
|
+
# With variables
|
|
239
|
+
schema.execute(query_string, variables: { 'key' => 'value' })
|
|
240
|
+
|
|
241
|
+
# With context
|
|
242
|
+
schema.execute(query_string, context: { user: current_user })
|
|
243
|
+
|
|
244
|
+
# Combined
|
|
245
|
+
schema.execute(
|
|
246
|
+
query_string,
|
|
247
|
+
variables: variables_hash,
|
|
248
|
+
context: context_hash
|
|
249
|
+
)
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
### Error Handling
|
|
253
|
+
|
|
254
|
+
GraphQLite automatically validates and reports errors:
|
|
255
|
+
|
|
256
|
+
```ruby
|
|
257
|
+
result = schema.execute('{ invalidField }')
|
|
258
|
+
# => { "errors" => [{ "message" => "Field 'invalidField' does not exist..." }] }
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
Errors include:
|
|
262
|
+
- Syntax errors in queries
|
|
263
|
+
- Field validation errors
|
|
264
|
+
- Type mismatch errors
|
|
265
|
+
- Argument validation errors
|
|
266
|
+
- Runtime resolver errors
|
|
267
|
+
|
|
268
|
+
### Complete Example
|
|
269
|
+
|
|
270
|
+
```ruby
|
|
271
|
+
schema = GraphQLite::Schema.new do
|
|
272
|
+
enum :Status, values: {
|
|
273
|
+
'ACTIVE' => { value: 'active' },
|
|
274
|
+
'INACTIVE' => { value: 'inactive' }
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
object :Post do
|
|
278
|
+
field :id, :ID, null: false
|
|
279
|
+
field :title, :String
|
|
280
|
+
field :content, :String
|
|
281
|
+
field :status, :Status
|
|
282
|
+
field :author, :User do |post|
|
|
283
|
+
User.find(post[:author_id])
|
|
284
|
+
end
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
object :User do
|
|
288
|
+
field :id, :ID, null: false
|
|
289
|
+
field :name, :String
|
|
290
|
+
field :email, :String
|
|
291
|
+
field :posts, [:Post] do |user|
|
|
292
|
+
Post.where(author_id: user[:id])
|
|
293
|
+
end
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
query do
|
|
297
|
+
field :user, :User do |f|
|
|
298
|
+
f.argument :id, :ID
|
|
299
|
+
f.resolve { |args| User.find(args[:id]) }
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
field :posts, [:Post] do |f|
|
|
303
|
+
f.argument :status, :Status
|
|
304
|
+
f.resolve do |args|
|
|
305
|
+
query = Post.all
|
|
306
|
+
query = query.where(status: args[:status]) if args[:status]
|
|
307
|
+
query
|
|
308
|
+
end
|
|
309
|
+
end
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
mutation do
|
|
313
|
+
field :createPost, :Post do |f|
|
|
314
|
+
f.argument :title, :String
|
|
315
|
+
f.argument :content, :String
|
|
316
|
+
f.argument :authorId, :ID
|
|
317
|
+
f.resolve do |args|
|
|
318
|
+
Post.create(
|
|
319
|
+
title: args[:title],
|
|
320
|
+
content: args[:content],
|
|
321
|
+
author_id: args[:authorId]
|
|
322
|
+
)
|
|
323
|
+
end
|
|
324
|
+
end
|
|
325
|
+
end
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
# Execute queries
|
|
329
|
+
schema.execute('{ posts { title author { name } } }')
|
|
330
|
+
schema.execute('{ posts(status: ACTIVE) { title } }')
|
|
331
|
+
|
|
332
|
+
# Execute mutations
|
|
333
|
+
mutation = 'mutation { createPost(title: "Hello", content: "World", authorId: "1") { id } }'
|
|
334
|
+
schema.execute(mutation)
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
## License
|
|
338
|
+
|
|
339
|
+
MIT License
|