pggraphql 0.0.1 → 0.0.2
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/.gitignore +1 -0
- data/lib/pggraphql/version.rb +1 -1
- data/lib/pggraphql.rb +110 -90
- data/test/test_pggraphql.rb +624 -235
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6b77f4fa17f6d73f558fa894e56b662a4226c5ea
|
4
|
+
data.tar.gz: 66fcd016a628c740e127968d054dd0bbeec29ce3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 52e5bdbdd564c508160e4688e87b66ef9e3fa9cb74fe9d21c977ca2ff9436fcb25c5e94768b346ffe4540b71a61e500ca50c17a4358f12e66e5e3587aaf39746
|
7
|
+
data.tar.gz: 437d33d4175c3ac772eae462e65d3ec08156a266f30f479264a47cfdc3e1303aa6efbf80aef8a62ad6fbb8edb31886c5da1bb71fbe9df1379d2ecc0890f5f5c1
|
data/.gitignore
CHANGED
data/lib/pggraphql/version.rb
CHANGED
data/lib/pggraphql.rb
CHANGED
@@ -1,126 +1,146 @@
|
|
1
|
-
require "json"
|
2
1
|
require "active_support/all"
|
3
2
|
require "pggraphql/version"
|
4
3
|
|
5
4
|
module PgGraphQl
|
6
5
|
|
7
6
|
class Schema
|
8
|
-
|
9
|
-
|
7
|
+
attr_reader :types, :roots
|
10
8
|
def initialize
|
11
|
-
@roots = {}
|
12
9
|
@types = {}
|
10
|
+
@roots = []
|
13
11
|
yield(self) if block_given?
|
14
12
|
|
15
|
-
@
|
16
|
-
t = memo[e[0]] = Type.new(e[0])
|
17
|
-
|
18
|
-
e[1].each_pair do |key, val|
|
19
|
-
t.send("#{key}=", val) unless key == :block
|
20
|
-
end
|
21
|
-
|
22
|
-
e[1][:block].call(t) if e[1][:block]
|
23
|
-
memo
|
24
|
-
end
|
13
|
+
@roots = @roots.map{|r| @types[r]}
|
25
14
|
end
|
26
15
|
|
27
|
-
def root(name
|
28
|
-
|
29
|
-
@roots[name] = opts
|
16
|
+
def root(name)
|
17
|
+
@roots << name
|
30
18
|
end
|
31
19
|
|
32
|
-
def type(name, opts={}
|
33
|
-
|
34
|
-
|
20
|
+
def type(name, opts={})
|
21
|
+
type = @types[name] = Type.new(self, name)
|
22
|
+
opts.each_pair{|key, val| type.send(:"#{key}=", val) }
|
23
|
+
yield(type) if block_given?
|
35
24
|
end
|
36
25
|
|
37
|
-
def
|
38
|
-
|
39
|
-
|
26
|
+
def to_sql(query, level=0, parent=nil, link_name=nil)
|
27
|
+
if level > 0
|
28
|
+
query.map do |e|
|
29
|
+
link = parent ? parent.links[link_name] : nil
|
30
|
+
type = link ? link.type : self.types[e[0].to_s.singularize.to_sym]
|
31
|
+
ids = e[1][:id]
|
40
32
|
|
41
|
-
|
33
|
+
columns = e[1].map do |f|
|
34
|
+
column_name = f[0]
|
42
35
|
|
43
|
-
|
44
|
-
|
45
|
-
if parent_link
|
46
|
-
if parent_link[:fk].is_a?(Symbol)
|
47
|
-
where_conditions << (parent_link[:invert] ? " #{requested_type.pk} = #{parent_type.as}.#{parent_link[:fk]}" : " #{parent_link[:fk]} = #{parent_type.pk!}")
|
48
|
-
elsif parent_link[:fk].is_a?(Proc)
|
49
|
-
where_conditions << (" #{requested_type.pk} in (" + parent_link[:fk].call(parent_link) + ")")
|
50
|
-
else
|
51
|
-
raise "unsupported"
|
52
|
-
end
|
53
|
-
|
54
|
-
where_conditions << parent_link[:filter] if parent_link[:filter]
|
55
|
-
end
|
36
|
+
raise "unknown field #{column_name.inspect} on type #{type.name.inspect}" if !f[1].is_a?(Hash) && !type.fields.include?(column_name)
|
37
|
+
raise "unknown link #{column_name.inspect} on type #{type.name.inspect}" if f[1].is_a?(Hash) && !type.links.include?(column_name)
|
56
38
|
|
57
|
-
|
39
|
+
(f[1].is_a?(Hash) ? "(" + to_sql([f].to_h, level + 1, type, column_name) + ") as #{column_name}" : column_name.to_s)
|
40
|
+
end.join(",")
|
58
41
|
|
59
|
-
|
60
|
-
|
61
|
-
field.to_s
|
62
|
-
elsif field.is_a?(Array)
|
42
|
+
is_many = (link && link.many?) || !ids || ids.is_a?(Array)
|
43
|
+
order_by = link.try(:order_by) || type.try(:order_by)
|
63
44
|
|
64
|
-
|
65
|
-
requested_link = requested_type.links[requested_link_field][1]
|
66
|
-
requested_link_ids = field[0][1]
|
67
|
-
requested_link_type = @types[requested_link[:type]]
|
45
|
+
wheres = []
|
68
46
|
|
69
|
-
|
70
|
-
|
47
|
+
if ids && ids.to_s != "id"
|
48
|
+
if ids.is_a?(Array)
|
49
|
+
wheres << "id in (#{ids.join(',')})" unless ids.empty?
|
50
|
+
else
|
51
|
+
wheres << "id = #{ids}"
|
52
|
+
end
|
71
53
|
end
|
72
54
|
|
73
|
-
"(" +
|
74
|
-
else
|
75
|
-
raise "unsupported"
|
76
|
-
end
|
55
|
+
wheres << ("(" + type.filter + ")") if type.filter
|
77
56
|
|
78
|
-
|
57
|
+
if link
|
58
|
+
wheres << if link.invert
|
59
|
+
"id = #{parent.table}.#{link.fk}"
|
60
|
+
else
|
61
|
+
"#{link.fk} = #{parent.table}.id"
|
62
|
+
end
|
79
63
|
|
80
|
-
|
64
|
+
wheres << ("(" + link.filter + ")") if link.filter
|
65
|
+
end
|
81
66
|
|
82
|
-
|
67
|
+
sql = "select to_json("
|
68
|
+
sql += "coalesce(json_agg(" if is_many
|
69
|
+
sql += "x.*"
|
70
|
+
sql += "), '[]'::json)" if is_many
|
71
|
+
sql += ") from (select #{columns} from #{type.table}"
|
72
|
+
sql += " where #{wheres.join(' and ')}" unless wheres.empty?
|
73
|
+
sql += " order by #{order_by}" if order_by
|
74
|
+
sql += " limit 1" if !is_many
|
75
|
+
sql += ") x"
|
76
|
+
|
77
|
+
end.join
|
78
|
+
else
|
79
|
+
wrap_root(query.map do |e|
|
80
|
+
sql = to_sql([e].to_h, 1)
|
81
|
+
"select '#{e[0]}'::text as key, (#{sql}) as value"
|
82
|
+
end.join("\nunion all\n"))
|
83
|
+
end
|
84
|
+
end
|
83
85
|
|
84
|
-
|
85
|
-
|
86
|
+
def wrap_root(sql)
|
87
|
+
"select ('{'||string_agg(to_json(t1.key)||':'||coalesce(to_json(t1.value), 'null'), ',')||'}')::json res from (\n" + sql + ") t1"
|
88
|
+
end
|
86
89
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
+
class Type
|
91
|
+
attr_accessor :name, :table, :filter, :links, :order_by, :fields
|
92
|
+
attr_reader :schema
|
93
|
+
def initialize(schema, name)
|
94
|
+
@schema = schema
|
95
|
+
@name = name
|
96
|
+
@table = name.to_s.pluralize.to_sym
|
97
|
+
@fields = [:id]
|
98
|
+
@filter = nil
|
99
|
+
@order_by = nil
|
100
|
+
@links = {}
|
90
101
|
end
|
102
|
+
def one(name, opts={})
|
103
|
+
create_link(name, false, opts)
|
104
|
+
end
|
105
|
+
def many(name, opts={})
|
106
|
+
create_link(name, true, opts)
|
107
|
+
end
|
108
|
+
def create_link(name, many, opts)
|
109
|
+
link = @links[name] = Link.new(self, name, many)
|
110
|
+
opts.each_pair do |key, val|
|
111
|
+
link.send(:"#{key}=", val)
|
112
|
+
end
|
113
|
+
link
|
114
|
+
end
|
115
|
+
end
|
91
116
|
|
92
|
-
|
93
|
-
|
94
|
-
|
117
|
+
class Link
|
118
|
+
attr_accessor :name, :invert, :filter, :order_by
|
119
|
+
def initialize(owner, name, many)
|
120
|
+
@owner = owner
|
121
|
+
@name = name
|
122
|
+
@many = many
|
123
|
+
@invert = false
|
124
|
+
@order_by = nil
|
125
|
+
end
|
126
|
+
def fk=(_fk)
|
127
|
+
@_fk = fk
|
128
|
+
end
|
129
|
+
def fk
|
130
|
+
@invert ? "#{@name.to_s.singularize}_id" : "#{@owner.name}_id"
|
131
|
+
end
|
132
|
+
def type=(_type)
|
133
|
+
@_type = _type
|
134
|
+
end
|
135
|
+
def type
|
136
|
+
@owner.schema.types[@_type||@name.to_s.singularize.to_sym]
|
137
|
+
end
|
138
|
+
def many?
|
139
|
+
!!@many
|
140
|
+
end
|
95
141
|
end
|
96
142
|
|
97
143
|
end
|
98
144
|
|
99
|
-
|
100
|
-
attr_accessor :pk, :sql, :fields, :as, :filter
|
101
|
-
attr_reader :links, :name
|
102
|
-
def initialize(name)
|
103
|
-
@name = name
|
104
|
-
@links = {}
|
105
|
-
@fields = [:id]
|
106
|
-
@as = name.to_s.pluralize.to_sym
|
107
|
-
@sql = "select * from #{name.to_s.pluralize}"
|
108
|
-
@pk = :id
|
109
|
-
end
|
110
|
-
def pk!
|
111
|
-
:"#{@as}.#{@pk}"
|
112
|
-
end
|
113
|
-
def one(name, opts={})
|
114
|
-
opts[:fk] = :"#{@name}_id" unless opts[:fk]
|
115
|
-
opts[:type] = name.to_s.singularize.to_sym unless opts[:type]
|
116
|
-
opts[:name] = name
|
117
|
-
@links[name] = [:one, opts]
|
118
|
-
end
|
119
|
-
def many(name, opts={})
|
120
|
-
opts[:fk] = :"#{@name}_id" unless opts[:fk]
|
121
|
-
opts[:type] = name.to_s.singularize.to_sym unless opts[:type]
|
122
|
-
opts[:name] = name
|
123
|
-
@links[name] = [:many, opts]
|
124
|
-
end
|
125
|
-
end
|
145
|
+
|
126
146
|
end
|
data/test/test_pggraphql.rb
CHANGED
@@ -9,154 +9,491 @@ module PgGraphQl
|
|
9
9
|
sql.strip.split(" ").map{|e| e.strip}.reject{|e| e.empty?}
|
10
10
|
end
|
11
11
|
|
12
|
-
def query
|
13
|
-
Schema.new(&block)
|
12
|
+
def to_sql(query, print_sql=nil, &block)
|
13
|
+
s = Schema.new(&block)
|
14
|
+
s.class.send(:define_method, :wrap_root) do |unwrapped_sql|
|
15
|
+
unwrapped_sql
|
16
|
+
end
|
17
|
+
|
18
|
+
sql = s.to_sql(query)
|
19
|
+
puts sql if print_sql
|
20
|
+
sql
|
14
21
|
end
|
15
22
|
|
16
|
-
|
23
|
+
def test_simple
|
24
|
+
res = to_sql({user: {id: 1, email: "email"}}) do |s|
|
25
|
+
s.root :user
|
26
|
+
s.type :user, fields: [:id, :email]
|
27
|
+
end
|
17
28
|
|
18
|
-
|
19
|
-
|
20
|
-
|
29
|
+
assert_equal token(<<-SQL
|
30
|
+
select 'user'::text as key,
|
31
|
+
(select to_json(x.*)
|
32
|
+
from (select id,
|
33
|
+
email
|
34
|
+
from users
|
35
|
+
where id = 1 limit 1) x) as value
|
36
|
+
SQL
|
37
|
+
), token(res)
|
21
38
|
end
|
22
39
|
|
23
|
-
|
40
|
+
def test_simple_pk_array_empty
|
41
|
+
res = to_sql({user: {id: [], email: "email"}}) do |s|
|
42
|
+
s.root :user
|
43
|
+
s.type :user, fields: [:id, :email]
|
44
|
+
end
|
24
45
|
|
25
|
-
|
26
|
-
|
46
|
+
assert_equal token(<<-SQL
|
47
|
+
select 'user'::text as key,
|
48
|
+
(select to_json(coalesce(json_agg(x.*), '[]'::json))
|
49
|
+
from (select id,
|
50
|
+
email
|
51
|
+
from users) x) as value
|
52
|
+
SQL
|
53
|
+
), token(res)
|
27
54
|
end
|
28
55
|
|
29
|
-
|
56
|
+
def test_simple_pk_array_one
|
57
|
+
res = to_sql({user: {id: [1], email: "email"}}) do |s|
|
58
|
+
s.root :user
|
59
|
+
s.type :user, fields: [:id, :email]
|
60
|
+
end
|
30
61
|
|
31
|
-
|
32
|
-
|
62
|
+
assert_equal token(<<-SQL
|
63
|
+
select 'user'::text as key,
|
64
|
+
(select to_json(coalesce(json_agg(x.*), '[]'::json))
|
65
|
+
from (select id,
|
66
|
+
email
|
67
|
+
from users where id in (1)) x) as value
|
68
|
+
SQL
|
69
|
+
), token(res)
|
33
70
|
end
|
34
71
|
|
35
|
-
def
|
36
|
-
|
37
|
-
|
38
|
-
|
72
|
+
def test_simple_pk_array_multiple
|
73
|
+
res = to_sql({user: {id: [1,2], email: "email"}}) do |s|
|
74
|
+
s.root :user
|
75
|
+
s.type :user, fields: [:id, :email]
|
76
|
+
end
|
39
77
|
|
40
|
-
|
41
|
-
|
42
|
-
|
78
|
+
assert_equal token(<<-SQL
|
79
|
+
select 'user'::text as key,
|
80
|
+
(select to_json(coalesce(json_agg(x.*), '[]'::json))
|
81
|
+
from (select id,
|
82
|
+
email
|
83
|
+
from users where id in (1,2)) x) as value
|
84
|
+
SQL
|
85
|
+
), token(res)
|
43
86
|
end
|
44
87
|
|
45
|
-
def
|
46
|
-
|
88
|
+
def test_simple_filter
|
89
|
+
res = to_sql({user: {id: 1, email: "email"}}) do |s|
|
90
|
+
s.root :user
|
91
|
+
s.type :user, fields: [:id, :email], filter: "id > 100"
|
92
|
+
end
|
93
|
+
|
94
|
+
assert_equal token(<<-SQL
|
95
|
+
select 'user'::text as key,
|
96
|
+
(select to_json(x.*)
|
97
|
+
from (select id,
|
98
|
+
email
|
99
|
+
from users
|
100
|
+
where id = 1 and (id > 100) limit 1) x) as value
|
101
|
+
SQL
|
102
|
+
), token(res)
|
47
103
|
end
|
48
104
|
|
49
|
-
def
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
105
|
+
def test_simple_multiple
|
106
|
+
res = to_sql({user: {id: 1, email: "email"}, educator: {id: "id"}}) do |s|
|
107
|
+
s.root :user
|
108
|
+
s.type :user, fields: [:id, :email]
|
109
|
+
s.type :educator
|
110
|
+
end
|
111
|
+
|
112
|
+
assert_equal token(<<-SQL
|
113
|
+
select 'user'::text as key,
|
114
|
+
(select to_json(x.*)
|
115
|
+
from (select id,
|
116
|
+
email
|
117
|
+
from users
|
118
|
+
where id = 1 limit 1) x) as value
|
119
|
+
union all
|
120
|
+
select 'educator'::text as key,
|
121
|
+
(select to_json(x.*)
|
122
|
+
from (select id
|
123
|
+
from educators limit 1) x) as value
|
124
|
+
SQL
|
125
|
+
), token(res)
|
54
126
|
end
|
55
127
|
|
56
|
-
|
57
|
-
|
58
|
-
|
128
|
+
#####################
|
129
|
+
# one
|
130
|
+
#####################
|
59
131
|
|
60
|
-
assert_equal :person, Type.new(:sometype).tap{|e| e.one(:people) }.links[:people][1][:type]
|
61
|
-
assert_equal :person, Type.new(:sometype).tap{|e| e.many(:people) }.links[:people][1][:type]
|
62
132
|
|
63
|
-
|
64
|
-
|
133
|
+
def test_link_one
|
134
|
+
res = to_sql({user: {id: 1, email: "email", address: {id: "id"}}}) do |s|
|
135
|
+
s.root :user
|
136
|
+
s.type :user, fields: [:id, :email] do |t|
|
137
|
+
t.one :address
|
138
|
+
end
|
139
|
+
s.type :address
|
140
|
+
end
|
141
|
+
|
142
|
+
assert_equal token(<<-SQL
|
143
|
+
select 'user'::text as key,
|
144
|
+
(select to_json(x.*)
|
145
|
+
from (select id,
|
146
|
+
email,
|
147
|
+
(select to_json(x.*)
|
148
|
+
from (select id
|
149
|
+
from addresses
|
150
|
+
where user_id = users.id limit 1) x) as address
|
151
|
+
from users
|
152
|
+
where id = 1 limit 1) x) as value
|
153
|
+
SQL
|
154
|
+
), token(res)
|
65
155
|
end
|
66
156
|
|
67
|
-
def
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
157
|
+
def test_link_one_invert
|
158
|
+
res = to_sql({address: {id: 1, user: {id: "id", email: "email"}}}) do |s|
|
159
|
+
s.root :address
|
160
|
+
s.type :address do |t|
|
161
|
+
t.one :user, invert: true
|
72
162
|
end
|
163
|
+
s.type :user, fields: [:id, :email]
|
73
164
|
end
|
165
|
+
|
166
|
+
assert_equal token(<<-SQL
|
167
|
+
select 'address'::text as key,
|
168
|
+
(select to_json(x.*)
|
169
|
+
from (select id,
|
170
|
+
(select to_json(x.*)
|
171
|
+
from (select id,
|
172
|
+
email
|
173
|
+
from users
|
174
|
+
where id = addresses.user_id limit 1) x) as user
|
175
|
+
from addresses
|
176
|
+
where id = 1 limit 1) x) as value
|
177
|
+
SQL
|
178
|
+
), token(res)
|
74
179
|
end
|
75
180
|
|
76
|
-
def
|
77
|
-
res =
|
181
|
+
def test_link_one_filter
|
182
|
+
res = to_sql({user: {id: 1, email: "email", address: {id: "id"}}}) do |s|
|
78
183
|
s.root :user
|
79
|
-
s.type :user, fields: [:id, :
|
184
|
+
s.type :user, fields: [:id, :email] do |t|
|
185
|
+
t.one :address, filter: "id > 100"
|
186
|
+
end
|
187
|
+
s.type :address
|
80
188
|
end
|
81
189
|
|
82
|
-
assert_equal token(
|
190
|
+
assert_equal token(<<-SQL
|
191
|
+
select 'user'::text as key,
|
192
|
+
(select to_json(x.*)
|
193
|
+
from (select id,
|
194
|
+
email,
|
195
|
+
(select to_json(x.*)
|
196
|
+
from (select id
|
197
|
+
from addresses
|
198
|
+
where user_id = users.id and (id > 100) limit 1) x) as address
|
199
|
+
from users
|
200
|
+
where id = 1 limit 1) x) as value
|
201
|
+
SQL
|
202
|
+
), token(res)
|
83
203
|
end
|
84
204
|
|
85
|
-
def
|
86
|
-
res =
|
205
|
+
def test_link_one_order_by
|
206
|
+
res = to_sql({user: {id: 1, email: "email", address: {id: "id"}}}) do |s|
|
87
207
|
s.root :user
|
88
|
-
s.type :user, fields: [:id, :
|
208
|
+
s.type :user, fields: [:id, :email] do |t|
|
209
|
+
t.one :address, order_by: "id desc"
|
210
|
+
end
|
211
|
+
s.type :address
|
89
212
|
end
|
90
213
|
|
91
|
-
assert_equal token(
|
214
|
+
assert_equal token(<<-SQL
|
215
|
+
select 'user'::text as key,
|
216
|
+
(select to_json(x.*)
|
217
|
+
from (select id,
|
218
|
+
email,
|
219
|
+
(select to_json(x.*)
|
220
|
+
from (select id
|
221
|
+
from addresses
|
222
|
+
where user_id = users.id order by id desc limit 1) x) as address
|
223
|
+
from users
|
224
|
+
where id = 1 limit 1) x) as value
|
225
|
+
SQL
|
226
|
+
), token(res)
|
227
|
+
end
|
228
|
+
|
229
|
+
#####################
|
230
|
+
# one-in-one
|
231
|
+
#####################
|
92
232
|
|
93
|
-
|
233
|
+
def test_link_one_in_one
|
234
|
+
res = to_sql({user: {id: 1, email: "email", address: {id: "id", country: {id: "id"}}}}) do |s|
|
94
235
|
s.root :user
|
95
|
-
s.type :user, fields: [:id, :
|
236
|
+
s.type :user, fields: [:id, :email] do |t|
|
237
|
+
t.one :address
|
238
|
+
end
|
239
|
+
s.type :address do |t|
|
240
|
+
t.one :country
|
241
|
+
end
|
242
|
+
s.type :country
|
96
243
|
end
|
97
244
|
|
98
|
-
assert_equal token(
|
245
|
+
assert_equal token(<<-SQL
|
246
|
+
select 'user'::text as key,
|
247
|
+
(select to_json(x.*)
|
248
|
+
from (select id,
|
249
|
+
email,
|
250
|
+
(select to_json(x.*)
|
251
|
+
from (select id,
|
252
|
+
(select to_json(x.*)
|
253
|
+
from (select id
|
254
|
+
from countries
|
255
|
+
where address_id = addresses.id limit 1) x) as country
|
256
|
+
from addresses
|
257
|
+
where user_id = users.id limit 1) x) as address
|
258
|
+
from users
|
259
|
+
where id = 1 limit 1) x) as value
|
260
|
+
SQL
|
261
|
+
), token(res)
|
99
262
|
end
|
100
263
|
|
101
|
-
def
|
102
|
-
res =
|
264
|
+
def test_link_one_in_one_invert
|
265
|
+
res = to_sql({user: {id: 1, email: "email", address: {id: "id", country: {id: "id"}}}}) do |s|
|
103
266
|
s.root :user
|
104
|
-
s.type :user do |t|
|
267
|
+
s.type :user, fields: [:id, :email] do |t|
|
105
268
|
t.one :address
|
106
269
|
end
|
270
|
+
s.type :address do |t|
|
271
|
+
t.one :country, invert: true
|
272
|
+
end
|
273
|
+
s.type :country
|
274
|
+
end
|
275
|
+
|
276
|
+
assert_equal token(<<-SQL
|
277
|
+
select 'user'::text as key,
|
278
|
+
(select to_json(x.*)
|
279
|
+
from (select id,
|
280
|
+
email,
|
281
|
+
(select to_json(x.*)
|
282
|
+
from (select id,
|
283
|
+
(select to_json(x.*)
|
284
|
+
from (select id
|
285
|
+
from countries
|
286
|
+
where id = addresses.country_id limit 1) x) as country
|
287
|
+
from addresses
|
288
|
+
where user_id = users.id limit 1) x) as address
|
289
|
+
from users
|
290
|
+
where id = 1 limit 1) x) as value
|
291
|
+
SQL
|
292
|
+
), token(res)
|
293
|
+
end
|
294
|
+
|
295
|
+
#####################
|
296
|
+
# many
|
297
|
+
#####################
|
298
|
+
|
299
|
+
|
300
|
+
def test_link_many
|
301
|
+
res = to_sql({user: {id: 1, email: "email", address: {id: "id"}}}) do |s|
|
302
|
+
s.root :user
|
303
|
+
s.type :user, fields: [:id, :email] do |t|
|
304
|
+
t.many :address
|
305
|
+
end
|
107
306
|
s.type :address
|
108
307
|
end
|
109
308
|
|
110
|
-
|
111
|
-
select
|
112
|
-
to_json(x.*)
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
309
|
+
assert_equal token(<<-SQL
|
310
|
+
select 'user'::text as key,
|
311
|
+
(select to_json(x.*)
|
312
|
+
from (select id,
|
313
|
+
email,
|
314
|
+
(select to_json(coalesce(json_agg(x.*), '[]'::json))
|
315
|
+
from (select id
|
316
|
+
from addresses
|
317
|
+
where user_id = users.id) x) as address
|
318
|
+
from users
|
319
|
+
where id = 1 limit 1) x) as value
|
121
320
|
SQL
|
321
|
+
), token(res)
|
322
|
+
end
|
323
|
+
|
324
|
+
def test_link_many_invert
|
325
|
+
res = to_sql({address: {id: "id", users: {id: "id", email: "email"}}}) do |s|
|
326
|
+
s.root :address
|
327
|
+
s.type :address do |t|
|
328
|
+
t.many :users, invert: true
|
329
|
+
end
|
330
|
+
s.type :user, fields: [:id, :email]
|
331
|
+
end
|
122
332
|
|
123
|
-
assert_equal token(
|
333
|
+
assert_equal token(<<-SQL
|
334
|
+
select 'address'::text as key,
|
335
|
+
(select to_json(x.*)
|
336
|
+
from (select id,
|
337
|
+
(select to_json(coalesce(json_agg(x.*), '[]'::json))
|
338
|
+
from (select id,
|
339
|
+
email
|
340
|
+
from users
|
341
|
+
where id = addresses.user_id) x) as users
|
342
|
+
from addresses limit 1) x) as value
|
343
|
+
SQL
|
344
|
+
), token(res)
|
124
345
|
end
|
125
346
|
|
126
|
-
def
|
127
|
-
res =
|
347
|
+
def test_link_many_filter
|
348
|
+
res = to_sql({user: {id: 1, email: "email", address: {id: "id"}}}) do |s|
|
128
349
|
s.root :user
|
129
|
-
s.type :user do |t|
|
130
|
-
t.
|
350
|
+
s.type :user, fields: [:id, :email] do |t|
|
351
|
+
t.many :address, filter: "id % 2 = 0"
|
131
352
|
end
|
132
353
|
s.type :address
|
133
354
|
end
|
134
355
|
|
135
|
-
|
136
|
-
select
|
137
|
-
to_json(x.*)
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
356
|
+
assert_equal token(<<-SQL
|
357
|
+
select 'user'::text as key,
|
358
|
+
(select to_json(x.*)
|
359
|
+
from (select id,
|
360
|
+
email,
|
361
|
+
(select to_json(coalesce(json_agg(x.*), '[]'::json))
|
362
|
+
from (select id
|
363
|
+
from addresses
|
364
|
+
where user_id = users.id and (id % 2 = 0)) x) as address
|
365
|
+
from users
|
366
|
+
where id = 1 limit 1) x) as value
|
146
367
|
SQL
|
368
|
+
), token(res)
|
369
|
+
end
|
147
370
|
|
148
|
-
|
371
|
+
def test_link_many_order_by
|
372
|
+
res = to_sql({user: {id: 1, email: "email", address: {id: "id"}}}) do |s|
|
373
|
+
s.root :user
|
374
|
+
s.type :user, fields: [:id, :email] do |t|
|
375
|
+
t.many :address, order_by: "id desc"
|
376
|
+
end
|
377
|
+
s.type :address
|
378
|
+
end
|
379
|
+
|
380
|
+
assert_equal token(<<-SQL
|
381
|
+
select 'user'::text as key,
|
382
|
+
(select to_json(x.*)
|
383
|
+
from (select id,
|
384
|
+
email,
|
385
|
+
(select to_json(coalesce(json_agg(x.*), '[]'::json))
|
386
|
+
from (select id
|
387
|
+
from addresses
|
388
|
+
where user_id = users.id order by id desc) x) as address
|
389
|
+
from users
|
390
|
+
where id = 1 limit 1) x) as value
|
391
|
+
SQL
|
392
|
+
), token(res)
|
149
393
|
end
|
150
394
|
|
151
|
-
|
395
|
+
|
396
|
+
# # schema
|
397
|
+
|
398
|
+
# def test_schema_root_type
|
399
|
+
# assert_equal :sometype, Schema.new{|e| e.root(:sometype)}.roots[:sometype][:type]
|
400
|
+
# assert_equal :person, Schema.new{|e| e.root(:people)}.roots[:people][:type]
|
401
|
+
# end
|
402
|
+
|
403
|
+
# # type
|
404
|
+
|
405
|
+
# def test_pk_prefixed_by_as
|
406
|
+
# assert_equal :"sometypes.id", Type.new(:sometype).pk!
|
407
|
+
# end
|
408
|
+
|
409
|
+
# type - defaults
|
410
|
+
|
411
|
+
# def test_type_default_fields
|
412
|
+
# assert_equal [:id], Schema::Type.new(:sometype).fields
|
413
|
+
# end
|
414
|
+
|
415
|
+
# def test_type_default_as
|
416
|
+
# assert_equal :sometypes, Type.new(:sometype).as
|
417
|
+
# assert_equal :people, Type.new(:person).as
|
418
|
+
# end
|
419
|
+
|
420
|
+
# def test_type_default_sql
|
421
|
+
# assert_equal "select * from houses", Type.new(:house).sql
|
422
|
+
# assert_equal "select * from people", Type.new(:person).sql
|
423
|
+
# end
|
424
|
+
|
425
|
+
# def test_type_default_pk
|
426
|
+
# assert_equal :id, Type.new(:sometype).pk
|
427
|
+
# end
|
428
|
+
|
429
|
+
# def test_type_link_default_fk
|
430
|
+
# assert_equal :sometype_id, Type.new(:sometype).tap{|e| e.one(:somelink) }.links[:somelink][1][:fk]
|
431
|
+
# assert_equal :sometype_id, Type.new(:sometype).tap{|e| e.many(:somelink) }.links[:somelink][1][:fk]
|
432
|
+
# assert_equal :other_fk, Type.new(:sometype).tap{|e| e.one(:somelink, fk: :other_fk) }.links[:somelink][1][:fk]
|
433
|
+
# assert_equal :other_fk, Type.new(:sometype).tap{|e| e.many(:somelink, fk: :other_fk) }.links[:somelink][1][:fk]
|
434
|
+
# end
|
435
|
+
|
436
|
+
# def test_type_link_default_type
|
437
|
+
# assert_equal :somelink, Type.new(:sometype).tap{|e| e.one(:somelink) }.links[:somelink][1][:type]
|
438
|
+
# assert_equal :somelink, Type.new(:sometype).tap{|e| e.many(:somelink) }.links[:somelink][1][:type]
|
439
|
+
|
440
|
+
# assert_equal :person, Type.new(:sometype).tap{|e| e.one(:people) }.links[:people][1][:type]
|
441
|
+
# assert_equal :person, Type.new(:sometype).tap{|e| e.many(:people) }.links[:people][1][:type]
|
442
|
+
|
443
|
+
# assert_equal :other_type, Type.new(:sometype).tap{|e| e.one(:somelink, type: :other_type) }.links[:somelink][1][:type]
|
444
|
+
# assert_equal :other_type, Type.new(:sometype).tap{|e| e.many(:somelink, type: :other_type) }.links[:somelink][1][:type]
|
445
|
+
# end
|
446
|
+
|
447
|
+
# def test_unknown_field
|
448
|
+
# assert_raise_message "unknown field" do
|
449
|
+
# query([[:user, 1], :id, :name]) do |s|
|
450
|
+
# s.root :user
|
451
|
+
# s.type :user
|
452
|
+
# end
|
453
|
+
# end
|
454
|
+
# end
|
455
|
+
|
456
|
+
# def test_simple_object_query
|
457
|
+
# res = query({user: [[:user, 1], :id, :name]}) do |s|
|
458
|
+
# s.root :user
|
459
|
+
# s.type :user, fields: [:id, :name]
|
460
|
+
# end
|
461
|
+
|
462
|
+
# assert_equal token("select to_json(x.*) res from (select id, name from users where id in (1) limit 1) x"), token(res)
|
463
|
+
# end
|
464
|
+
|
465
|
+
# def test_simple
|
466
|
+
# res = query([[:user, 1], :id, :name]) do |s|
|
467
|
+
# s.root :user
|
468
|
+
# s.type :user, fields: [:id, :name]
|
469
|
+
# end
|
470
|
+
|
471
|
+
# assert_equal token("select to_json(x.*) res from (select id, name from users where id in (1) limit 1) x"), token(res)
|
472
|
+
# end
|
473
|
+
|
474
|
+
# def test_simple_filter
|
475
|
+
# res = query([[:user, 1], :id, :name]) do |s|
|
476
|
+
# s.root :user
|
477
|
+
# s.type :user, fields: [:id, :name], filter: "id % 2 = 0"
|
478
|
+
# end
|
479
|
+
|
480
|
+
# assert_equal token("select to_json(x.*) res from (select id, name from users where id in (1) and id % 2 = 0 limit 1) x"), token(res)
|
481
|
+
|
482
|
+
# res = query([[:user, 1], :id, :name]) do |s|
|
483
|
+
# s.root :user
|
484
|
+
# s.type :user, fields: [:id, :name], filter: "id % 2 = 0 and id > 10"
|
485
|
+
# end
|
486
|
+
|
487
|
+
# assert_equal token("select to_json(x.*) res from (select id, name from users where id in (1) and id % 2 = 0 and id > 10 limit 1) x"), token(res)
|
488
|
+
# end
|
489
|
+
|
490
|
+
# def test_one
|
152
491
|
# res = query([[:user, 1], :id, [[:address], :id]]) do |s|
|
153
492
|
# s.root :user
|
154
493
|
# s.type :user do |t|
|
155
|
-
# t.one :address
|
494
|
+
# t.one :address
|
156
495
|
# end
|
157
496
|
# s.type :address
|
158
|
-
# s.type :address_primary
|
159
|
-
# s.type :address_secondary
|
160
497
|
# end
|
161
498
|
|
162
499
|
# expected = <<-SQL
|
@@ -165,11 +502,7 @@ module PgGraphQl
|
|
165
502
|
# from (
|
166
503
|
# select
|
167
504
|
# id,
|
168
|
-
# (select to_json(x.*) res from (
|
169
|
-
# select 'primary' as type, id from addresses join addresses_primary using (id) where user_id = users.id and addresses.type = 'primary'
|
170
|
-
# union
|
171
|
-
# select 'secondary' as type, id from addresses join addresses_secondary using (id) where user_id = users.id and addresses.type = 'secondary'
|
172
|
-
# ) x) as address
|
505
|
+
# (select to_json(x.*) res from (select id from addresses where user_id = users.id limit 1) x) as address
|
173
506
|
# from users
|
174
507
|
# where id in (1)
|
175
508
|
# limit 1
|
@@ -179,168 +512,224 @@ module PgGraphQl
|
|
179
512
|
# assert_equal token(expected), token(res)
|
180
513
|
# end
|
181
514
|
|
182
|
-
def
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
515
|
+
# def test_one_fk_invert
|
516
|
+
# res = query([[:user, 1], :id, [[:address], :id]]) do |s|
|
517
|
+
# s.root :user
|
518
|
+
# s.type :user do |t|
|
519
|
+
# t.one :address, fk: :address_id, invert: true
|
520
|
+
# end
|
521
|
+
# s.type :address
|
522
|
+
# end
|
190
523
|
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
524
|
+
# expected = <<-SQL
|
525
|
+
# select
|
526
|
+
# to_json(x.*) res
|
527
|
+
# from (
|
528
|
+
# select
|
529
|
+
# id,
|
530
|
+
# (select to_json(x.*) res from (select id from addresses where id = users.address_id limit 1) x) as address
|
531
|
+
# from users
|
532
|
+
# where id in (1)
|
533
|
+
# limit 1
|
534
|
+
# ) x
|
535
|
+
# SQL
|
203
536
|
|
204
|
-
|
205
|
-
end
|
537
|
+
# assert_equal token(expected), token(res)
|
538
|
+
# end
|
206
539
|
|
207
|
-
def
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
540
|
+
# # def test_one_polymorph
|
541
|
+
# # res = query([[:user, 1], :id, [[:address], :id]]) do |s|
|
542
|
+
# # s.root :user
|
543
|
+
# # s.type :user do |t|
|
544
|
+
# # t.one :address, polymorph: [:primary, :secondary]
|
545
|
+
# # end
|
546
|
+
# # s.type :address
|
547
|
+
# # s.type :address_primary
|
548
|
+
# # s.type :address_secondary
|
549
|
+
# # end
|
550
|
+
|
551
|
+
# # expected = <<-SQL
|
552
|
+
# # select
|
553
|
+
# # to_json(x.*) res
|
554
|
+
# # from (
|
555
|
+
# # select
|
556
|
+
# # id,
|
557
|
+
# # (select to_json(x.*) res from (
|
558
|
+
# # select 'primary' as type, id from addresses join addresses_primary using (id) where user_id = users.id and addresses.type = 'primary'
|
559
|
+
# # union
|
560
|
+
# # select 'secondary' as type, id from addresses join addresses_secondary using (id) where user_id = users.id and addresses.type = 'secondary'
|
561
|
+
# # ) x) as address
|
562
|
+
# # from users
|
563
|
+
# # where id in (1)
|
564
|
+
# # limit 1
|
565
|
+
# # ) x
|
566
|
+
# # SQL
|
567
|
+
|
568
|
+
# # assert_equal token(expected), token(res)
|
569
|
+
# # end
|
570
|
+
|
571
|
+
# def test_one_filter
|
572
|
+
# res = query([[:user, 1], :id, [[:address], :id]]) do |s|
|
573
|
+
# s.root :user
|
574
|
+
# s.type :user do |t|
|
575
|
+
# t.one :address, filter: "is_default = true"
|
576
|
+
# end
|
577
|
+
# s.type :address
|
578
|
+
# end
|
217
579
|
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
580
|
+
# expected = <<-SQL
|
581
|
+
# select
|
582
|
+
# to_json(x.*) res
|
583
|
+
# from (
|
584
|
+
# select
|
585
|
+
# id,
|
586
|
+
# (select to_json(x.*) res from (select id from addresses where user_id = users.id and is_default = true limit 1) x) as address
|
587
|
+
# from users
|
588
|
+
# where id in (1)
|
589
|
+
# limit 1
|
590
|
+
# ) x
|
591
|
+
# SQL
|
230
592
|
|
231
|
-
|
232
|
-
end
|
593
|
+
# assert_equal token(expected), token(res)
|
594
|
+
# end
|
233
595
|
|
234
|
-
def
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
end
|
596
|
+
# def test_one_fk_through
|
597
|
+
# res = query([[:user, 1], :id, [[:address], :id]]) do |s|
|
598
|
+
# s.root :user
|
599
|
+
# s.type :user do |t|
|
600
|
+
# t.one :address, fk: ->(l) do
|
601
|
+
# "select id from some_address_table where user_id = #{t.pk!}"
|
602
|
+
# end
|
603
|
+
# end
|
604
|
+
# s.type :address
|
605
|
+
# end
|
245
606
|
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
from users
|
259
|
-
where id in (1)
|
260
|
-
limit 1
|
261
|
-
) x
|
262
|
-
SQL
|
607
|
+
# expected = <<-SQL
|
608
|
+
# select
|
609
|
+
# to_json(x.*) res
|
610
|
+
# from (
|
611
|
+
# select
|
612
|
+
# id,
|
613
|
+
# (select to_json(x.*) res from (select id from addresses where id in (select id from some_address_table where user_id = users.id) limit 1) x) as address
|
614
|
+
# from users
|
615
|
+
# where id in (1)
|
616
|
+
# limit 1
|
617
|
+
# ) x
|
618
|
+
# SQL
|
263
619
|
|
264
|
-
|
265
|
-
end
|
620
|
+
# assert_equal token(expected), token(res)
|
621
|
+
# end
|
266
622
|
|
267
|
-
def
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
623
|
+
# def test_one_in_one
|
624
|
+
# res = query([[:user, 1], :id, [[:address], :id, [[:person], :id]]]) do |s|
|
625
|
+
# s.root :user
|
626
|
+
# s.type :user do |t|
|
627
|
+
# t.one :address
|
628
|
+
# end
|
629
|
+
# s.type :address do |t|
|
630
|
+
# t.one :person
|
631
|
+
# end
|
632
|
+
# s.type :person
|
633
|
+
# end
|
275
634
|
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
635
|
+
# expected = <<-SQL
|
636
|
+
# select
|
637
|
+
# to_json(x.*) res
|
638
|
+
# from (
|
639
|
+
# select
|
640
|
+
# id,
|
641
|
+
# (select to_json(x.*) res from (
|
642
|
+
# select
|
643
|
+
# id,
|
644
|
+
# (select to_json(x.*) res from (select id from people where address_id = addresses.id limit 1) x) as person
|
645
|
+
# from addresses where user_id = users.id limit 1
|
646
|
+
# ) x) as address
|
647
|
+
# from users
|
648
|
+
# where id in (1)
|
649
|
+
# limit 1
|
650
|
+
# ) x
|
651
|
+
# SQL
|
286
652
|
|
287
|
-
|
288
|
-
end
|
653
|
+
# assert_equal token(expected), token(res)
|
654
|
+
# end
|
289
655
|
|
290
|
-
def
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
656
|
+
# def test_many
|
657
|
+
# res = query([[:user, 1], :id, [[:addresses], :id]]) do |s|
|
658
|
+
# s.root :user
|
659
|
+
# s.type :user do |t|
|
660
|
+
# t.many :addresses
|
661
|
+
# end
|
662
|
+
# s.type :address
|
663
|
+
# end
|
298
664
|
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
665
|
+
# expected = <<-SQL
|
666
|
+
# select
|
667
|
+
# to_json(x.*) res
|
668
|
+
# from (
|
669
|
+
# select id,
|
670
|
+
# (select to_json(coalesce(json_agg(x.*), '[]'::json)) res from (select id from addresses where user_id = users.id) x) as addresses
|
671
|
+
# from users
|
672
|
+
# where id in (1) limit 1
|
673
|
+
# ) x
|
674
|
+
# SQL
|
309
675
|
|
310
|
-
|
311
|
-
end
|
676
|
+
# assert_equal token(expected), token(res)
|
677
|
+
# end
|
312
678
|
|
313
|
-
def
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
end
|
322
|
-
s.type :person
|
323
|
-
end
|
679
|
+
# def test_many_with_ids
|
680
|
+
# res = query([[:user, 1], :id, [[:addresses, 100], :id]]) do |s|
|
681
|
+
# s.root :user
|
682
|
+
# s.type :user do |t|
|
683
|
+
# t.many :addresses
|
684
|
+
# end
|
685
|
+
# s.type :address
|
686
|
+
# end
|
324
687
|
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
from addresses where user_id = users.id
|
336
|
-
) x) as addresses
|
337
|
-
from users
|
338
|
-
where id in (1)
|
339
|
-
limit 1
|
340
|
-
) x
|
341
|
-
SQL
|
688
|
+
# expected = <<-SQL
|
689
|
+
# select
|
690
|
+
# to_json(x.*) res
|
691
|
+
# from (
|
692
|
+
# select id,
|
693
|
+
# (select to_json(coalesce(json_agg(x.*), '[]'::json)) res from (select id from addresses where user_id = users.id and id in (100)) x) as addresses
|
694
|
+
# from users
|
695
|
+
# where id in (1) limit 1
|
696
|
+
# ) x
|
697
|
+
# SQL
|
342
698
|
|
343
|
-
|
344
|
-
end
|
699
|
+
# assert_equal token(expected), token(res)
|
700
|
+
# end
|
701
|
+
|
702
|
+
# def test_one_in_many
|
703
|
+
# res = query([[:user, 1], :id, [[:addresses], :id, [[:person], :id]]]) do |s|
|
704
|
+
# s.root :user
|
705
|
+
# s.type :user do |t|
|
706
|
+
# t.many :addresses
|
707
|
+
# end
|
708
|
+
# s.type :address do |t|
|
709
|
+
# t.one :person
|
710
|
+
# end
|
711
|
+
# s.type :person
|
712
|
+
# end
|
713
|
+
|
714
|
+
# expected = <<-SQL
|
715
|
+
# select
|
716
|
+
# to_json(x.*) res
|
717
|
+
# from (
|
718
|
+
# select
|
719
|
+
# id,
|
720
|
+
# (select to_json(coalesce(json_agg(x.*), '[]'::json)) res from (
|
721
|
+
# select
|
722
|
+
# id,
|
723
|
+
# (select to_json(x.*) res from (select id from people where address_id = addresses.id limit 1) x) as person
|
724
|
+
# from addresses where user_id = users.id
|
725
|
+
# ) x) as addresses
|
726
|
+
# from users
|
727
|
+
# where id in (1)
|
728
|
+
# limit 1
|
729
|
+
# ) x
|
730
|
+
# SQL
|
731
|
+
|
732
|
+
# assert_equal token(expected), token(res)
|
733
|
+
# end
|
345
734
|
end
|
346
735
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pggraphql
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jan Zimmek
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-03-
|
11
|
+
date: 2015-03-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: json
|