graffiti 2.1
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.
- data/COPYING +676 -0
- data/ChangeLog.mtn +233 -0
- data/README.rdoc +129 -0
- data/TODO +30 -0
- data/doc/diagrams/graffiti-classes.svg +157 -0
- data/doc/diagrams/graffiti-deployment.svg +117 -0
- data/doc/diagrams/graffiti-store-sequence.svg +69 -0
- data/doc/diagrams/squish-select-sequence.svg +266 -0
- data/doc/examples/samizdat-rdf-config.yaml +77 -0
- data/doc/examples/samizdat-triggers-pgsql.sql +266 -0
- data/doc/papers/collreif.tex +462 -0
- data/doc/papers/rdf-to-relational-query-translation-icis2009.tex +936 -0
- data/doc/papers/rel-rdf.tex +545 -0
- data/doc/rdf-impl-report.txt +126 -0
- data/graffiti.gemspec +21 -0
- data/lib/graffiti.rb +15 -0
- data/lib/graffiti/debug.rb +34 -0
- data/lib/graffiti/exceptions.rb +20 -0
- data/lib/graffiti/rdf_config.rb +78 -0
- data/lib/graffiti/rdf_property_map.rb +92 -0
- data/lib/graffiti/sql_mapper.rb +916 -0
- data/lib/graffiti/squish.rb +568 -0
- data/lib/graffiti/store.rb +100 -0
- data/setup.rb +1360 -0
- data/test/ts_graffiti.rb +455 -0
- metadata +122 -0
data/test/ts_graffiti.rb
ADDED
@@ -0,0 +1,455 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# Graffiti RDF Store tests
|
4
|
+
#
|
5
|
+
# Copyright (c) 2002-2009 Dmitry Borodaenko <angdraug@debian.org>
|
6
|
+
#
|
7
|
+
# This program is free software.
|
8
|
+
# You can distribute/modify this program under the terms of
|
9
|
+
# the GNU General Public License version 3 or later.
|
10
|
+
#
|
11
|
+
# vim: et sw=2 sts=2 ts=8 tw=0
|
12
|
+
|
13
|
+
require 'test/unit'
|
14
|
+
require 'yaml'
|
15
|
+
require 'sequel'
|
16
|
+
require 'graffiti'
|
17
|
+
|
18
|
+
include Graffiti
|
19
|
+
|
20
|
+
class TC_Storage < Test::Unit::TestCase
|
21
|
+
|
22
|
+
def setup
|
23
|
+
config = File.open(
|
24
|
+
File.join(
|
25
|
+
File.dirname(File.dirname(__FILE__)),
|
26
|
+
'doc', 'examples', 'samizdat-rdf-config.yaml'
|
27
|
+
)
|
28
|
+
) {|f| YAML.load(f.read) }
|
29
|
+
@db = create_mock_db
|
30
|
+
@store = Store.new(@db, config)
|
31
|
+
@ns = @store.config.ns
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_query_select
|
35
|
+
squish = %{
|
36
|
+
SELECT ?msg, ?title, ?name, ?date, ?rating
|
37
|
+
WHERE (dc::title ?msg ?title)
|
38
|
+
(dc::creator ?msg ?creator)
|
39
|
+
(s::fullName ?creator ?name)
|
40
|
+
(dc::date ?msg ?date)
|
41
|
+
(rdf::subject ?stmt ?msg)
|
42
|
+
(rdf::predicate ?stmt dc::relation)
|
43
|
+
(rdf::object ?stmt s::Quality)
|
44
|
+
(s::rating ?stmt ?rating)
|
45
|
+
LITERAL ?rating >= -1
|
46
|
+
ORDER BY ?rating DESC
|
47
|
+
USING PRESET NS}
|
48
|
+
|
49
|
+
sql = "SELECT DISTINCT c.id AS msg, c.title AS title, b.full_name AS name, d.published_date AS date, a.rating AS rating
|
50
|
+
FROM statement AS a
|
51
|
+
INNER JOIN message AS c ON (c.id = a.subject)
|
52
|
+
INNER JOIN member AS b ON (c.creator = b.id)
|
53
|
+
INNER JOIN resource AS d ON (c.id = d.id)
|
54
|
+
INNER JOIN resource AS e ON (a.object = e.id) AND (e.uriref = 't' AND e.label = 'http://www.nongnu.org/samizdat/rdf/schema#Quality')
|
55
|
+
INNER JOIN resource AS f ON (a.predicate = f.id) AND (f.uriref = 't' AND f.label = 'http://purl.org/dc/elements/1.1/relation')
|
56
|
+
WHERE (a.id IS NOT NULL)
|
57
|
+
AND (d.published_date IS NOT NULL)
|
58
|
+
AND (b.full_name IS NOT NULL)
|
59
|
+
AND (c.title IS NOT NULL)
|
60
|
+
AND (a.rating >= -1)
|
61
|
+
ORDER BY a.rating DESC"
|
62
|
+
|
63
|
+
test_squish_select(squish, sql) do |query|
|
64
|
+
assert_equal %w[?msg ?title ?name ?date ?rating], query.nodes
|
65
|
+
assert query.pattern.include?(["#{@ns['dc']}title", "?msg", "?title", nil, false])
|
66
|
+
assert_equal '?rating >= -1', query.literal
|
67
|
+
assert_equal '?rating', query.order
|
68
|
+
assert_equal 'DESC', query.order_dir
|
69
|
+
assert_equal @ns['s'], query.ns['s']
|
70
|
+
end
|
71
|
+
|
72
|
+
assert_equal [], @store.select_all(squish)
|
73
|
+
end
|
74
|
+
|
75
|
+
def test_query_assert
|
76
|
+
# initialize
|
77
|
+
query_text = %{
|
78
|
+
INSERT ?msg
|
79
|
+
UPDATE ?title = 'Test Message', ?content = 'Some ''text''.'
|
80
|
+
WHERE (dc::creator ?msg 1)
|
81
|
+
(dc::title ?msg ?title)
|
82
|
+
(s::content ?msg ?content)
|
83
|
+
USING dc FOR #{@ns['dc']}
|
84
|
+
s FOR #{@ns['s']}}
|
85
|
+
begin
|
86
|
+
query = SquishAssert.new(@store.config, query_text)
|
87
|
+
rescue
|
88
|
+
assert false, "SquishAssert initialization raised #{$!.class}: #{$!}"
|
89
|
+
end
|
90
|
+
|
91
|
+
# query parser
|
92
|
+
assert_equal ['?msg'], query.insert
|
93
|
+
assert_equal({'?title' => "'0'", '?content' => "'1'"}, query.update)
|
94
|
+
assert query.pattern.include?(["#{@ns['dc']}title", "?msg", "?title", nil, false])
|
95
|
+
assert_equal @ns['s'], query.ns['s']
|
96
|
+
assert_equal "'Test Message'", query.substitute_literals("'0'")
|
97
|
+
assert_equal "'Some ''text''.'", query.substitute_literals("'1'")
|
98
|
+
|
99
|
+
# mock db
|
100
|
+
ids = @store.assert(query_text)
|
101
|
+
assert_equal [1], ids
|
102
|
+
assert_equal 'Test Message', @db[:Message][:id => 1][:title]
|
103
|
+
|
104
|
+
id2 = @store.assert(query_text)
|
105
|
+
query_text = %{
|
106
|
+
UPDATE ?rating = :rating
|
107
|
+
WHERE (rdf::subject ?stmt :related)
|
108
|
+
(rdf::predicate ?stmt dc::relation)
|
109
|
+
(rdf::object ?stmt 1)
|
110
|
+
(s::voteProposition ?vote ?stmt)
|
111
|
+
(s::voteMember ?vote :member)
|
112
|
+
(s::voteRating ?vote ?rating)}
|
113
|
+
params = {:rating => -1, :related => 2, :member => 3}
|
114
|
+
ids = @store.assert(query_text, params)
|
115
|
+
assert_equal [], ids
|
116
|
+
assert vote = @db[:vote].order(:id).last
|
117
|
+
assert_equal -1, vote[:rating].to_i
|
118
|
+
|
119
|
+
params[:rating] = -2
|
120
|
+
@store.assert(query_text, params)
|
121
|
+
assert vote2 = @db[:vote].order(:id).last
|
122
|
+
assert_equal -2, vote2[:rating].to_i
|
123
|
+
assert_equal vote[:id], vote2[:id]
|
124
|
+
end
|
125
|
+
|
126
|
+
def test_query_assert_expression
|
127
|
+
query_text = %{
|
128
|
+
UPDATE ?rating = 2 * :rating
|
129
|
+
WHERE (rdf::subject ?stmt :related)
|
130
|
+
(rdf::predicate ?stmt dc::relation)
|
131
|
+
(rdf::object ?stmt 1)
|
132
|
+
(s::voteProposition ?vote ?stmt)
|
133
|
+
(s::voteMember ?vote :member)
|
134
|
+
(s::voteRating ?vote ?rating)}
|
135
|
+
params = {:rating => -1, :related => 2, :member => 3}
|
136
|
+
@store.assert(query_text, params)
|
137
|
+
assert vote = @db[:vote].order(:id).last
|
138
|
+
assert_equal -2, vote[:rating].to_i
|
139
|
+
end
|
140
|
+
private :test_query_assert_expression
|
141
|
+
|
142
|
+
def test_dangling_blank_node
|
143
|
+
squish = %{
|
144
|
+
SELECT ?msg
|
145
|
+
WHERE (s::inReplyTo ?msg ?parent)
|
146
|
+
USING s FOR #{@ns['s']}}
|
147
|
+
|
148
|
+
sql = "SELECT DISTINCT a.id AS msg
|
149
|
+
FROM resource AS a
|
150
|
+
INNER JOIN resource AS b ON (a.part_of_subproperty = b.id) AND (b.uriref = 't' AND b.label = 'http://www.nongnu.org/samizdat/rdf/schema#inReplyTo')
|
151
|
+
WHERE (a.id IS NOT NULL)"
|
152
|
+
|
153
|
+
test_squish_select(squish, sql) do |query|
|
154
|
+
assert_equal %w[?msg], query.nodes
|
155
|
+
assert query.pattern.include?(["#{@ns['s']}inReplyTo", "?msg", "?parent", nil, false])
|
156
|
+
assert_equal @ns['s'], query.ns['s']
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
def test_external_resource_no_self_join
|
161
|
+
squish = %{SELECT ?id WHERE (s::id tag::Translation ?id)}
|
162
|
+
|
163
|
+
sql = "SELECT DISTINCT a.id AS id
|
164
|
+
FROM resource AS a
|
165
|
+
WHERE (a.id IS NOT NULL)
|
166
|
+
AND ((a.uriref = 't' AND a.label = 'http://www.nongnu.org/samizdat/rdf/tag#Translation'))"
|
167
|
+
|
168
|
+
test_squish_select(squish, sql) do |query|
|
169
|
+
assert_equal %w[?id], query.nodes
|
170
|
+
assert query.pattern.include?(["#{@ns['s']}id", "#{@ns['tag']}Translation", "?id", nil, false])
|
171
|
+
assert_equal @ns['s'], query.ns['s']
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
#def test_internal_resource
|
176
|
+
#end
|
177
|
+
|
178
|
+
#def test_external_subject_internal_property
|
179
|
+
#end
|
180
|
+
|
181
|
+
def test_except
|
182
|
+
squish = %{
|
183
|
+
SELECT ?msg
|
184
|
+
WHERE (dc::date ?msg ?date)
|
185
|
+
EXCEPT (s::inReplyTo ?msg ?parent)
|
186
|
+
(dct::isVersionOf ?msg ?version_of)
|
187
|
+
(dc::creator ?version_of 1)
|
188
|
+
ORDER BY ?date DESC}
|
189
|
+
|
190
|
+
sql = "SELECT DISTINCT b.id AS msg, b.published_date AS date
|
191
|
+
FROM resource AS b
|
192
|
+
LEFT JOIN (
|
193
|
+
SELECT b.id AS _field_f
|
194
|
+
FROM message AS a
|
195
|
+
INNER JOIN resource AS b ON (b.part_of = a.id)
|
196
|
+
INNER JOIN resource AS c ON (b.part_of_subproperty = c.id) AND (c.uriref = 't' AND c.label = 'http://purl.org/dc/terms/isVersionOf')
|
197
|
+
WHERE (a.creator = 1)
|
198
|
+
) AS _subquery_a ON (b.id = _subquery_a._field_f)
|
199
|
+
LEFT JOIN resource AS d ON (b.part_of_subproperty = d.id) AND (d.uriref = 't' AND d.label = 'http://www.nongnu.org/samizdat/rdf/schema#inReplyTo')
|
200
|
+
WHERE (b.published_date IS NOT NULL)
|
201
|
+
AND (b.id IS NOT NULL)
|
202
|
+
AND (_subquery_a._field_f IS NULL)
|
203
|
+
AND (d.id IS NULL)
|
204
|
+
ORDER BY b.published_date DESC"
|
205
|
+
|
206
|
+
test_squish_select(squish, sql)
|
207
|
+
end
|
208
|
+
|
209
|
+
def test_except_group_by
|
210
|
+
squish = %{
|
211
|
+
SELECT ?msg
|
212
|
+
WHERE (rdf::predicate ?stmt dc::relation)
|
213
|
+
(rdf::subject ?stmt ?msg)
|
214
|
+
(rdf::object ?stmt ?tag)
|
215
|
+
(dc::date ?stmt ?date)
|
216
|
+
(s::rating ?stmt ?rating FILTER ?rating >= 1.5)
|
217
|
+
(s::hidden ?msg ?hidden FILTER ?hidden = 'f')
|
218
|
+
EXCEPT (dct::isPartOf ?msg ?parent)
|
219
|
+
GROUP BY ?msg
|
220
|
+
ORDER BY max(?date) DESC}
|
221
|
+
|
222
|
+
sql = "SELECT DISTINCT a.subject AS msg, max(b.published_date)
|
223
|
+
FROM statement AS a
|
224
|
+
INNER JOIN resource AS b ON (a.id = b.id)
|
225
|
+
INNER JOIN message AS c ON (a.subject = c.id) AND (c.hidden = 'f')
|
226
|
+
INNER JOIN resource AS d ON (a.subject = d.id)
|
227
|
+
INNER JOIN resource AS e ON (a.predicate = e.id) AND (e.uriref = 't' AND e.label = 'http://purl.org/dc/elements/1.1/relation')
|
228
|
+
WHERE (c.hidden IS NOT NULL)
|
229
|
+
AND (b.published_date IS NOT NULL)
|
230
|
+
AND (a.object IS NOT NULL)
|
231
|
+
AND (a.rating IS NOT NULL)
|
232
|
+
AND (d.part_of IS NULL)
|
233
|
+
AND ((a.rating >= 1.5))
|
234
|
+
GROUP BY a.subject
|
235
|
+
ORDER BY max(b.published_date) DESC"
|
236
|
+
|
237
|
+
test_squish_select(squish, sql)
|
238
|
+
end
|
239
|
+
|
240
|
+
def test_optional
|
241
|
+
squish = %{
|
242
|
+
SELECT ?date, ?creator, ?lang, ?parent, ?version_of, ?hidden, ?open
|
243
|
+
WHERE (dc::date 1 ?date)
|
244
|
+
OPTIONAL (dc::creator 1 ?creator)
|
245
|
+
(dc::language 1 ?lang)
|
246
|
+
(s::inReplyTo 1 ?parent)
|
247
|
+
(dct::isVersionOf 1 ?version_of)
|
248
|
+
(s::hidden 1 ?hidden)
|
249
|
+
(s::openForAll 1 ?open)}
|
250
|
+
|
251
|
+
sql = "SELECT DISTINCT a.published_date AS date, b.creator AS creator, b.language AS lang, select_subproperty(a.part_of, d.id) AS parent, select_subproperty(a.part_of, c.id) AS version_of, b.hidden AS hidden, b.open AS open
|
252
|
+
FROM resource AS a
|
253
|
+
INNER JOIN message AS b ON (a.id = b.id)
|
254
|
+
LEFT JOIN resource AS c ON (a.part_of_subproperty = c.id) AND (c.uriref = 't' AND c.label = 'http://purl.org/dc/terms/isVersionOf')
|
255
|
+
LEFT JOIN resource AS d ON (a.part_of_subproperty = d.id) AND (d.uriref = 't' AND d.label = 'http://www.nongnu.org/samizdat/rdf/schema#inReplyTo')
|
256
|
+
WHERE (a.published_date IS NOT NULL)
|
257
|
+
AND ((a.id = 1))"
|
258
|
+
|
259
|
+
test_squish_select(squish, sql)
|
260
|
+
end
|
261
|
+
|
262
|
+
def test_except_optional_transitive
|
263
|
+
squish = %{
|
264
|
+
SELECT ?msg
|
265
|
+
WHERE (rdf::subject ?stmt ?msg)
|
266
|
+
(rdf::predicate ?stmt dc::relation)
|
267
|
+
(rdf::object ?stmt ?tag)
|
268
|
+
(s::rating ?stmt ?rating FILTER ?rating > 0)
|
269
|
+
(dc::date ?msg ?date)
|
270
|
+
EXCEPT (dct::isPartOf ?msg ?parent)
|
271
|
+
OPTIONAL (dct::isPartOf ?tag ?supertag TRANSITIVE)
|
272
|
+
LITERAL ?tag = 1 OR ?supertag = 1
|
273
|
+
ORDER BY ?date DESC}
|
274
|
+
|
275
|
+
sql = "SELECT DISTINCT a.subject AS msg, b.published_date AS date
|
276
|
+
FROM statement AS a
|
277
|
+
INNER JOIN resource AS b ON (a.subject = b.id)
|
278
|
+
INNER JOIN resource AS d ON (a.predicate = d.id) AND (d.uriref = 't' AND d.label = 'http://purl.org/dc/elements/1.1/relation')
|
279
|
+
LEFT JOIN part AS c ON (a.object = c.id)
|
280
|
+
WHERE (a.id IS NOT NULL)
|
281
|
+
AND (b.published_date IS NOT NULL)
|
282
|
+
AND (a.rating IS NOT NULL)
|
283
|
+
AND (b.part_of IS NULL)
|
284
|
+
AND ((a.rating > 0))
|
285
|
+
AND (a.object = 1 OR c.part_of = 1)
|
286
|
+
ORDER BY b.published_date DESC"
|
287
|
+
|
288
|
+
test_squish_select(squish, sql)
|
289
|
+
end
|
290
|
+
|
291
|
+
def test_optional_connect_by_object
|
292
|
+
squish = %{
|
293
|
+
SELECT ?event
|
294
|
+
WHERE (ical::dtstart ?event ?dtstart FILTER ?dtstart >= 'now')
|
295
|
+
(ical::dtend ?event ?dtend)
|
296
|
+
OPTIONAL (s::rruleEvent ?rrule ?event)
|
297
|
+
(ical::until ?rrule ?until FILTER ?until IS NULL OR ?until > 'now')
|
298
|
+
LITERAL ?dtend > 'now' OR ?rrule IS NOT NULL
|
299
|
+
ORDER BY ?event DESC}
|
300
|
+
|
301
|
+
sql = "SELECT DISTINCT b.id AS event
|
302
|
+
FROM event AS b
|
303
|
+
LEFT JOIN recurrence AS a ON (b.id = a.event) AND (a.until IS NULL OR a.until > 'now')
|
304
|
+
WHERE (b.dtstart IS NOT NULL)
|
305
|
+
AND ((b.dtstart >= 'now'))
|
306
|
+
AND (b.dtend > 'now' OR a.id IS NOT NULL)
|
307
|
+
ORDER BY b.id DESC"
|
308
|
+
|
309
|
+
test_squish_select(squish, sql)
|
310
|
+
end
|
311
|
+
private :test_optional_connect_by_object
|
312
|
+
|
313
|
+
def test_many_to_many
|
314
|
+
# pretend that Vote is a many-to-many relation table
|
315
|
+
squish = %{
|
316
|
+
SELECT ?p, ?date
|
317
|
+
WHERE (s::voteRating ?p ?vote1 FILTER ?vote1 > 0)
|
318
|
+
(s::voteRating ?p ?vote2 FILTER ?vote2 < 0)
|
319
|
+
(dc::date ?p ?date)
|
320
|
+
ORDER BY ?date DESC}
|
321
|
+
|
322
|
+
sql = "SELECT DISTINCT a.id AS p, c.published_date AS date
|
323
|
+
FROM vote AS a
|
324
|
+
INNER JOIN vote AS b ON (a.id = b.id) AND (b.rating < 0)
|
325
|
+
INNER JOIN resource AS c ON (a.id = c.id)
|
326
|
+
WHERE (c.published_date IS NOT NULL)
|
327
|
+
AND (a.rating IS NOT NULL)
|
328
|
+
AND (b.rating IS NOT NULL)
|
329
|
+
AND ((a.rating > 0))
|
330
|
+
ORDER BY c.published_date DESC"
|
331
|
+
|
332
|
+
test_squish_select(squish, sql)
|
333
|
+
end
|
334
|
+
|
335
|
+
def test_update_null_and_subproperty
|
336
|
+
query_text =
|
337
|
+
%{INSERT ?msg
|
338
|
+
UPDATE ?parent = :parent
|
339
|
+
WHERE (dct::isPartOf ?msg ?parent)}
|
340
|
+
@store.assert(query_text, :id => 1, :parent => 3)
|
341
|
+
assert_equal 3, @db[:resource].filter(:id => 1).get(:part_of)
|
342
|
+
|
343
|
+
# check that subproperty is set
|
344
|
+
query_text =
|
345
|
+
%{UPDATE ?parent = :parent
|
346
|
+
WHERE (s::subTagOf :id ?parent)}
|
347
|
+
@store.assert(query_text, :id => 1, :parent => 3)
|
348
|
+
assert_equal 3, @db[:resource].filter(:id => 1).get(:part_of)
|
349
|
+
assert_equal 2, @db[:resource].filter(:id => 1).get(:part_of_subproperty)
|
350
|
+
|
351
|
+
# check that NULL is handled correctly and that subproperty is unset
|
352
|
+
query_text =
|
353
|
+
%{UPDATE ?parent = NULL
|
354
|
+
WHERE (dct::isPartOf :id ?parent)}
|
355
|
+
@store.assert(query_text, :id => 1)
|
356
|
+
assert_equal nil, @db[:resource].filter(:id => 1).get(:part_of)
|
357
|
+
assert_equal nil, @db[:resource].filter(:id => 1).get(:part_of_subproperty)
|
358
|
+
end
|
359
|
+
|
360
|
+
private
|
361
|
+
|
362
|
+
def test_squish_select(squish, sql)
|
363
|
+
begin
|
364
|
+
query = SquishSelect.new(@store.config, squish)
|
365
|
+
rescue
|
366
|
+
assert false, "SquishSelect initialization raised #{$!.class}: #{$!}"
|
367
|
+
end
|
368
|
+
|
369
|
+
yield query if block_given?
|
370
|
+
|
371
|
+
# query result
|
372
|
+
begin
|
373
|
+
sql1 = @store.select(query)
|
374
|
+
rescue
|
375
|
+
assert false, "select with pre-parsed query raised #{$!.class}: #{$!}"
|
376
|
+
end
|
377
|
+
begin
|
378
|
+
sql2 = @store.select(squish)
|
379
|
+
rescue
|
380
|
+
assert false, "select with query text raised #{$!.class}: #{$!}"
|
381
|
+
end
|
382
|
+
assert sql1 == sql2
|
383
|
+
|
384
|
+
# transform result
|
385
|
+
assert_equal normalize(sql), normalize(sql1),
|
386
|
+
"Query doesn't match. Expected:\n#{sql}\nReceived:\n#{sql1}"
|
387
|
+
end
|
388
|
+
|
389
|
+
def normalize(sql)
|
390
|
+
# alias labels and where conditions may be reordered, but the query string
|
391
|
+
# length should remain the same
|
392
|
+
sql.size
|
393
|
+
end
|
394
|
+
|
395
|
+
def create_mock_db
|
396
|
+
db = Sequel.sqlite(:quote_identifiers => false)
|
397
|
+
|
398
|
+
db.create_table(:resource) do
|
399
|
+
primary_key :id
|
400
|
+
Time :published_date
|
401
|
+
Integer :part_of
|
402
|
+
Integer :part_of_subproperty
|
403
|
+
Integer :part_sequence_number
|
404
|
+
TrueClass :literal
|
405
|
+
TrueClass :uriref
|
406
|
+
String :label
|
407
|
+
end
|
408
|
+
|
409
|
+
db.create_table(:statement) do
|
410
|
+
primary_key :id
|
411
|
+
Integer :subject
|
412
|
+
Integer :predicate
|
413
|
+
Integer :object
|
414
|
+
BigDecimal :rating, :size => [4, 2]
|
415
|
+
end
|
416
|
+
|
417
|
+
db.create_table(:member) do
|
418
|
+
primary_key :id
|
419
|
+
String :login
|
420
|
+
String :full_name
|
421
|
+
String :email
|
422
|
+
end
|
423
|
+
|
424
|
+
db.create_table(:message) do
|
425
|
+
primary_key :id
|
426
|
+
String :title
|
427
|
+
Integer :creator
|
428
|
+
String :format
|
429
|
+
String :language
|
430
|
+
TrueClass :open
|
431
|
+
TrueClass :hidden
|
432
|
+
TrueClass :locked
|
433
|
+
String :content
|
434
|
+
String :html_full
|
435
|
+
String :html_short
|
436
|
+
end
|
437
|
+
|
438
|
+
db.create_table(:vote) do
|
439
|
+
primary_key :id
|
440
|
+
Integer :proposition
|
441
|
+
Integer :member
|
442
|
+
BigDecimal :rating, :size => 2
|
443
|
+
end
|
444
|
+
|
445
|
+
db
|
446
|
+
end
|
447
|
+
|
448
|
+
def create_mock_member(db)
|
449
|
+
db[:member].insert(
|
450
|
+
:login => 'test',
|
451
|
+
:full_name => 'test',
|
452
|
+
:email => 'test@localhost'
|
453
|
+
)
|
454
|
+
end
|
455
|
+
end
|