apiql 0.2.2 → 0.3.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 +4 -4
- data/README.md +39 -2
- data/apiql.gemspec +1 -1
- data/lib/apiql.rb +101 -53
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3f34e609be9a7132527a772ee95d0e7dd4e43aff4bb7d8f9fd43010f73e9632e
|
4
|
+
data.tar.gz: 1194eed934b4c0f26084dc0cdec5beac7d51967236e1b6438efa176d176cfdb2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: adca6b4acf505f3a281a352640dbabb23a05b9ded7a9ddd99c10d64f0cbba1b67b054a66e048cbdd0f59c164f9fa2b850c3f8693cf0ec92ad967326e87639ee1
|
7
|
+
data.tar.gz: 0c1c05a98a7cb45868e3d0f3118b4d4ccf87c475e9e88aa84cb49808005ee5f45cdc1df9595eef0be5f5c3f7087213f8ecaeca97e5fad45733dbdf8280a28677
|
data/README.md
CHANGED
@@ -1,6 +1,10 @@
|
|
1
1
|
# APIQL
|
2
2
|
|
3
|
-
Implementation of the API language similar to GraphQL for Ruby on Rails
|
3
|
+
Implementation of the API language similar to GraphQL for Ruby on Rails.
|
4
|
+
|
5
|
+
It compiles requests into Hashes for faster rendering.
|
6
|
+
|
7
|
+
Now, it automatically detects nested entities and eager-loads them for faster DB access!
|
4
8
|
|
5
9
|
Define your responder (requested methods):
|
6
10
|
|
@@ -102,6 +106,37 @@ logout() {
|
|
102
106
|
|
103
107
|
```
|
104
108
|
|
109
|
+
you can call methods on entities:
|
110
|
+
|
111
|
+
```javascript
|
112
|
+
apiql("user", `
|
113
|
+
authenticate(email, password) {
|
114
|
+
token
|
115
|
+
}
|
116
|
+
|
117
|
+
me.reload {
|
118
|
+
email full_name role token
|
119
|
+
|
120
|
+
roles(filter) {
|
121
|
+
id title
|
122
|
+
}
|
123
|
+
|
124
|
+
contacts.primary {
|
125
|
+
full_name
|
126
|
+
email
|
127
|
+
}
|
128
|
+
}
|
129
|
+
`, {
|
130
|
+
email: email,
|
131
|
+
filter: 'all',
|
132
|
+
password: password
|
133
|
+
})
|
134
|
+
.then(response => {
|
135
|
+
let user = response['me.reload'] // name in response equals to called
|
136
|
+
})
|
137
|
+
}
|
138
|
+
```
|
139
|
+
|
105
140
|
You can use initializer like this for authorization using cancancan gem:
|
106
141
|
|
107
142
|
config/initializers/apiql.rb:
|
@@ -126,9 +161,11 @@ class APIQL
|
|
126
161
|
end
|
127
162
|
end
|
128
163
|
end
|
164
|
+
```
|
129
165
|
|
130
|
-
and authorize access to every entity:
|
166
|
+
and even authorize access to every entity:
|
131
167
|
|
168
|
+
```ruby
|
132
169
|
class ApplicationRecord < ActiveRecord::Base
|
133
170
|
class BaseEntity < APIQL::Entity
|
134
171
|
def initialize(object, context)
|
data/apiql.gemspec
CHANGED
data/lib/apiql.rb
CHANGED
@@ -20,13 +20,14 @@ class APIQL
|
|
20
20
|
request = params[:apiql_request]
|
21
21
|
|
22
22
|
if request.present?
|
23
|
-
|
23
|
+
request = compile(request)
|
24
|
+
redis&.set("api-ql-cache-#{request_id}", request.to_json)
|
24
25
|
@@cache = {} if @@cache.count > 1000
|
25
26
|
@@cache[request_id] = request
|
26
27
|
else
|
27
28
|
request = @@cache[request_id]
|
28
|
-
request ||= redis
|
29
|
-
raise CacheMissed unless request.present?
|
29
|
+
request ||= JSON.parse(redis.get("api-ql-cache-#{request_id}")) rescue nil
|
30
|
+
raise CacheMissed unless request.present? && request.is_a?(::Array)
|
30
31
|
end
|
31
32
|
|
32
33
|
request
|
@@ -50,74 +51,97 @@ class APIQL
|
|
50
51
|
nil
|
51
52
|
end
|
52
53
|
end
|
53
|
-
end
|
54
54
|
|
55
|
-
|
56
|
-
|
57
|
-
@context.inject_delegators(self)
|
58
|
-
end
|
55
|
+
def compile(schema)
|
56
|
+
result = []
|
59
57
|
|
60
|
-
|
61
|
-
|
58
|
+
ptr = result
|
59
|
+
pool = []
|
62
60
|
|
63
|
-
|
64
|
-
|
61
|
+
while schema.present? do
|
62
|
+
if reg = schema.match(/\A\s*\{(?<rest>.*)\z/m) # {
|
63
|
+
schema = reg[:rest]
|
65
64
|
|
66
|
-
|
67
|
-
|
68
|
-
|
65
|
+
pool.push(ptr)
|
66
|
+
key = ptr.pop
|
67
|
+
ptr.push(key => (ptr = []))
|
68
|
+
elsif reg = schema.match(/\A\s*\}(?<rest>.*)\z/m) # }
|
69
|
+
schema = reg[:rest]
|
69
70
|
|
70
|
-
|
71
|
-
|
72
|
-
|
71
|
+
ptr = pool.pop
|
72
|
+
elsif pool.any? && (reg = schema.match(/\A\s*(?<name>[\w\.]+)(\((?<params>.*)\))?(?<rest>.*)\z/m))
|
73
|
+
schema = reg[:rest]
|
73
74
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
75
|
+
if reg[:params].nil?
|
76
|
+
key = reg[:name]
|
77
|
+
else
|
78
|
+
key = "#{reg[:name]}(#{reg[:params]})"
|
79
|
+
end
|
78
80
|
|
79
|
-
|
81
|
+
ptr.push(key)
|
82
|
+
elsif reg = schema.match(/\A\s*(?<name>[\w\.]+)(\((?<params>((\w+)(\s*\,\s*\w+)*))?\))?\s*\{(?<rest>.*)\z/m)
|
83
|
+
schema = reg[:rest]
|
80
84
|
|
81
|
-
|
85
|
+
pool.push(ptr)
|
86
|
+
ptr.push("#{reg[:name]}(#{reg[:params]})" => (ptr = []))
|
87
|
+
elsif reg = schema.match(/\A\s*(?<name>[\w\.]+)(\((?<params>((\w+)(\s*\,\s*\w+)*))?\))?\s*\n?(?<rest>.*)\z/m)
|
88
|
+
schema = reg[:rest]
|
82
89
|
|
83
|
-
|
84
|
-
result[function] = @context.render_value(data, last_keys)
|
85
|
-
function = nil
|
90
|
+
ptr.push("#{reg[:name]}(#{reg[:params]})")
|
86
91
|
else
|
87
|
-
|
88
|
-
keys << { last_key => last_keys }
|
92
|
+
raise Error, schema
|
89
93
|
end
|
90
|
-
|
91
|
-
schema = reg[:rest]
|
94
|
+
end
|
92
95
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
keys << reg[:name]
|
97
|
-
end
|
96
|
+
result
|
97
|
+
end
|
98
|
+
end
|
98
99
|
|
99
|
-
|
100
|
-
|
101
|
-
|
100
|
+
def initialize(binder, *fields)
|
101
|
+
@context = ::APIQL::Context.new(binder, *fields)
|
102
|
+
@context.inject_delegators(self)
|
103
|
+
end
|
102
104
|
|
103
|
-
|
104
|
-
|
105
|
+
def eager_load
|
106
|
+
result = @eager_load
|
105
107
|
|
106
|
-
|
108
|
+
@eager_load = nil
|
107
109
|
|
108
|
-
|
109
|
-
|
110
|
+
result
|
111
|
+
end
|
110
112
|
|
111
|
-
|
113
|
+
def render(schema)
|
114
|
+
result = {}
|
112
115
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
116
|
+
schema.map do |call|
|
117
|
+
if call.is_a? ::Hash
|
118
|
+
call.each do |function, sub_schema|
|
119
|
+
reg = function.match(/\A(?<name>[\w\.]+)(\((?<params>.*)\))?\z/)
|
120
|
+
raise Error, function unless reg.present?
|
121
|
+
|
122
|
+
function = reg[:name]
|
123
|
+
params = @context.parse_params(reg[:params])
|
124
|
+
|
125
|
+
@eager_load = eager_loads(sub_schema)
|
126
|
+
data = public_send(function, *params)
|
127
|
+
if @eager_load.present? && !data.is_a?(::Hash)
|
128
|
+
if data.respond_to?(:each) && data.respond_to?(:map)
|
129
|
+
data = data.eager_load(eager_load)
|
130
|
+
elsif data.respond_to?(:id)
|
131
|
+
data = data.class.eager_load(eager_load).find(data.id)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
result[function] = @context.render_value(data, sub_schema)
|
136
|
+
end
|
137
|
+
else
|
138
|
+
reg = call.match(/\A(?<name>[\w\.]+)(\((?<params>.*)\))?\z/)
|
139
|
+
raise Error, call unless reg.present?
|
117
140
|
|
118
141
|
function = reg[:name]
|
119
142
|
params = @context.parse_params(reg[:params])
|
120
143
|
|
144
|
+
@eager_load = ''
|
121
145
|
data = public_send(function, *params)
|
122
146
|
if data.is_a? Array
|
123
147
|
if data.any? { |item| !APIQL::simple_class?(item) }
|
@@ -128,10 +152,30 @@ class APIQL
|
|
128
152
|
end
|
129
153
|
|
130
154
|
result[function] = data
|
155
|
+
end
|
156
|
+
end
|
131
157
|
|
132
|
-
|
133
|
-
|
134
|
-
|
158
|
+
result
|
159
|
+
end
|
160
|
+
|
161
|
+
private
|
162
|
+
|
163
|
+
def eager_loads(schema)
|
164
|
+
result = []
|
165
|
+
|
166
|
+
schema.each do |call|
|
167
|
+
if call.is_a? Hash
|
168
|
+
call.each do |function, sub_schema|
|
169
|
+
next if function.include? '('
|
170
|
+
function = function.split('.').first if function.include? '.'
|
171
|
+
|
172
|
+
sub = eager_loads(sub_schema)
|
173
|
+
if sub.present?
|
174
|
+
result.push(function => sub)
|
175
|
+
else
|
176
|
+
result.push function
|
177
|
+
end
|
178
|
+
end
|
135
179
|
end
|
136
180
|
end
|
137
181
|
|
@@ -316,7 +360,11 @@ class APIQL
|
|
316
360
|
begin
|
317
361
|
"#{value.class.name}::Entity".constantize.new(value, self).render(schema)
|
318
362
|
rescue StandardError
|
319
|
-
|
363
|
+
if ::Rails.env.development?
|
364
|
+
raise
|
365
|
+
else
|
366
|
+
nil
|
367
|
+
end
|
320
368
|
end
|
321
369
|
end
|
322
370
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: apiql
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dmitry Silchenko
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-07-
|
11
|
+
date: 2018-07-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|