pho 0.4 → 0.4.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/CHANGES +12 -1
- data/Rakefile +2 -2
- data/doc/index.html +15 -1
- data/doc/rdoc/classes/Pho.html +14 -0
- data/doc/rdoc/classes/Pho/DatatypeProperty.html +12 -12
- data/doc/rdoc/classes/Pho/Etags.html +36 -36
- data/doc/rdoc/classes/Pho/Facet/Results.html +19 -19
- data/doc/rdoc/classes/Pho/Facet/Term.html +6 -6
- data/doc/rdoc/classes/Pho/FieldPredicateMap.html +90 -90
- data/doc/rdoc/classes/Pho/FieldWeighting.html +12 -12
- data/doc/rdoc/classes/Pho/Job.html +64 -64
- data/doc/rdoc/classes/Pho/Jobs.html +61 -61
- data/doc/rdoc/classes/Pho/Namespaces.html +6 -1
- data/doc/rdoc/classes/Pho/QueryProfile.html +61 -61
- data/doc/rdoc/classes/Pho/RDFCollection.html +71 -71
- data/doc/rdoc/classes/Pho/RDF_JSON.html +118 -0
- data/doc/rdoc/classes/Pho/RDF_JSON/SetAlgebra.html +240 -0
- data/doc/rdoc/classes/Pho/Snapshot.html +35 -35
- data/doc/rdoc/classes/Pho/Status.html +26 -26
- data/doc/rdoc/classes/Pho/Store.html +342 -264
- data/doc/rdoc/classes/Pho/Update.html +127 -0
- data/doc/rdoc/classes/Pho/Update/Changeset.html +520 -0
- data/doc/rdoc/classes/Pho/Update/ChangesetBuilder.html +330 -0
- data/doc/rdoc/classes/Pho/Update/Changesets.html +204 -0
- data/doc/rdoc/classes/Pho/Update/LiteralStatement.html +248 -0
- data/doc/rdoc/classes/Pho/Update/ResourceStatement.html +213 -0
- data/doc/rdoc/classes/Pho/Update/Statement.html +302 -0
- data/doc/rdoc/classes/String.html +146 -0
- data/doc/rdoc/created.rid +1 -1
- data/doc/rdoc/files/CHANGES.html +33 -2
- data/doc/rdoc/files/lib/pho/changeset_builder_rb.html +108 -0
- data/doc/rdoc/files/lib/pho/changeset_rb.html +108 -0
- data/doc/rdoc/files/lib/pho/job_rb.html +1 -1
- data/doc/rdoc/files/lib/pho/query_profile_rb.html +1 -1
- data/doc/rdoc/files/lib/pho/rdf_json_rb.html +101 -0
- data/doc/rdoc/files/lib/pho/store_rb.html +1 -1
- data/doc/rdoc/files/lib/pho_rb.html +4 -1
- data/doc/rdoc/fr_class_index.html +10 -0
- data/doc/rdoc/fr_file_index.html +3 -0
- data/doc/rdoc/fr_method_index.html +140 -110
- data/lib/pho.rb +14 -1
- data/lib/pho/changeset.rb +312 -0
- data/lib/pho/changeset_builder.rb +118 -0
- data/lib/pho/job.rb +1 -1
- data/lib/pho/query_profile.rb +1 -1
- data/lib/pho/rdf_json.rb +81 -0
- data/lib/pho/store.rb +63 -25
- data/tests/tc_changeset.rb +273 -0
- data/tests/tc_changeset_builder.rb +151 -0
- data/tests/tc_changesets.rb +96 -0
- data/tests/tc_metabox.rb +20 -2
- data/tests/tc_query_profile.rb +4 -2
- data/tests/tc_rdf_json.rb +273 -0
- data/tests/tc_search.rb +5 -5
- data/tests/tc_sparql.rb +12 -0
- data/tests/ts_pho.rb +5 -1
- metadata +25 -3
@@ -0,0 +1,118 @@
|
|
1
|
+
module Pho
|
2
|
+
|
3
|
+
require 'uri'
|
4
|
+
|
5
|
+
#Module organizing classes related to Changeset handling.
|
6
|
+
module Update
|
7
|
+
|
8
|
+
#Utility class providing methods for building Changesets from triple hashes
|
9
|
+
class ChangesetBuilder
|
10
|
+
|
11
|
+
#Build a batch changeset
|
12
|
+
#
|
13
|
+
#This method is suitable for building an array of changesets that describe changes made to a
|
14
|
+
#number of different resources.
|
15
|
+
#
|
16
|
+
#Returns an array of Changeset objects
|
17
|
+
#
|
18
|
+
# before:: triple hash describing current state of the resource
|
19
|
+
# after:: triple hash describing updated state of the resource
|
20
|
+
# creator_name:: name of the creator of the changes
|
21
|
+
# change_reason:: description of why the changes are being made
|
22
|
+
def ChangesetBuilder.build_batch(before, after, creator_name=nil, change_reason=nil)
|
23
|
+
|
24
|
+
removals = Pho::RDF_JSON::SetAlgebra.minus(before, after)
|
25
|
+
additions = Pho::RDF_JSON::SetAlgebra.minus(after, before)
|
26
|
+
|
27
|
+
batch = Array.new
|
28
|
+
|
29
|
+
removals.each do |uri, properties|
|
30
|
+
cs = Pho::Update::Changeset.new(uri, creator_name, change_reason) do |cs|
|
31
|
+
cs.add_removals( create_statements_for_uri(uri, properties) )
|
32
|
+
if additions.has_key?(uri)
|
33
|
+
cs.add_additions( create_statements_for_uri(uri, additions[uri] ) )
|
34
|
+
additions.delete(uri)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
batch << cs
|
38
|
+
end
|
39
|
+
|
40
|
+
if !additions.empty?
|
41
|
+
additions.each do |uri, properties|
|
42
|
+
cs = Pho::Update::Changeset.new(uri, creator_name, change_reason) do |cs|
|
43
|
+
cs.add_additions( create_statements_for_uri(uri, properties) )
|
44
|
+
end
|
45
|
+
batch << cs
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
return batch
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
#Build a single changeset
|
54
|
+
#
|
55
|
+
#This method is suitable for building changesets that describe changes made to a single resource
|
56
|
+
#If the before/after hashes contain data for other subjects, then an error will be thrown.
|
57
|
+
#
|
58
|
+
#The method will return a single Changeset object.
|
59
|
+
#
|
60
|
+
# subject_of_change:: uri of the resource being updated
|
61
|
+
# before:: triple hash describing current state of the resource
|
62
|
+
# after:: triple hash describing updated state of the resource
|
63
|
+
# creator_name:: name of the creator of the changes
|
64
|
+
# change_reason:: description of why the changes are being made
|
65
|
+
def ChangesetBuilder.build(subject_of_change, before, after, creator_name=nil, change_reason=nil)
|
66
|
+
removals = Pho::RDF_JSON::SetAlgebra.minus(before, after)
|
67
|
+
additions = Pho::RDF_JSON::SetAlgebra.minus(after, before)
|
68
|
+
|
69
|
+
cs = Pho::Update::Changeset.new(subject_of_change, creator_name, change_reason) do |cs|
|
70
|
+
cs.add_removals( create_statements(removals) )
|
71
|
+
cs.add_additions( create_statements(additions) )
|
72
|
+
end
|
73
|
+
|
74
|
+
return cs
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
#Takes an triple hash and serializes it as an array of Pho::Update::Statement objects
|
79
|
+
#
|
80
|
+
# triples:: a hash of triples, conforming to RDF-in-JSON structure
|
81
|
+
def ChangesetBuilder.create_statements(triples)
|
82
|
+
statements = Array.new
|
83
|
+
triples.each do |uri, properties|
|
84
|
+
statements += create_statements_for_uri(uri, properties)
|
85
|
+
end
|
86
|
+
return statements
|
87
|
+
end
|
88
|
+
|
89
|
+
#Create statements for a specific uri, using predicate-object values in
|
90
|
+
#the provided properties hash
|
91
|
+
#
|
92
|
+
# uri:: subject of change
|
93
|
+
# properties:: hash of predicate-object values
|
94
|
+
def ChangesetBuilder.create_statements_for_uri(uri, properties)
|
95
|
+
statements = Array.new
|
96
|
+
properties.each do |predicate, val_array|
|
97
|
+
val_array.each do |value|
|
98
|
+
s = nil
|
99
|
+
if value["type"] == "literal"
|
100
|
+
s = Pho::Update::Statement.create_literal(uri, predicate, value["value"], value["lang"], value["datatype"])
|
101
|
+
else
|
102
|
+
#TODO bnodes?
|
103
|
+
s = Pho::Update::Statement.create_resource(uri, predicate, value["value"])
|
104
|
+
end
|
105
|
+
if s != nil
|
106
|
+
statements << s
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
return statements
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
118
|
+
|
data/lib/pho/job.rb
CHANGED
@@ -21,7 +21,7 @@ module Pho
|
|
21
21
|
jobs = Array.new
|
22
22
|
|
23
23
|
doc = REXML::Document.new(resp.content)
|
24
|
-
REXML::XPath.each(doc.root, "//bf:job", Pho::Namespaces::MAPPING)
|
24
|
+
REXML::XPath.each(doc.root, "//bf:job", Pho::Namespaces::MAPPING) do |el|
|
25
25
|
jobs << el.attributes["rdf:resource"]
|
26
26
|
end
|
27
27
|
return jobs
|
data/lib/pho/query_profile.rb
CHANGED
@@ -137,7 +137,7 @@ module Pho
|
|
137
137
|
rdf << " <rdfs:label>#{@label}</rdfs:label> "
|
138
138
|
|
139
139
|
@field_weights.each do |property|
|
140
|
-
rdf << " <
|
140
|
+
rdf << " <bf:fieldWeight rdf:resource=\"#{property.uri}\"/> "
|
141
141
|
end
|
142
142
|
|
143
143
|
rdf << " </rdf:Description>"
|
data/lib/pho/rdf_json.rb
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
module Pho
|
2
|
+
|
3
|
+
#TODO
|
4
|
+
#blank nodes
|
5
|
+
|
6
|
+
#Module providing code for manipulating triple hashes structured according
|
7
|
+
#to the RDF in JSON spec
|
8
|
+
module RDF_JSON
|
9
|
+
|
10
|
+
#Class providing set algebra methods over triple hashes
|
11
|
+
class SetAlgebra
|
12
|
+
|
13
|
+
#Accepts two triple hashes, expressed as RDF-in-JSON and returns a new
|
14
|
+
#Ruby data structure that constitutes the different between the two graphs
|
15
|
+
#
|
16
|
+
#i.e. the return value will be a hash containing the triples that are in the
|
17
|
+
#first graph but which are not present in the second.
|
18
|
+
#
|
19
|
+
# first:: the first graph
|
20
|
+
# second:: the second graph.
|
21
|
+
def SetAlgebra.minus(first, second)
|
22
|
+
|
23
|
+
difference = Hash.new
|
24
|
+
first.each do |uri,properties|
|
25
|
+
if second.has_key?(uri)
|
26
|
+
properties.each do |predicate,value|
|
27
|
+
if second[uri].has_key?(predicate)
|
28
|
+
#second hash has same uri and predicate, so check value arrays
|
29
|
+
second_value = second[uri][predicate]
|
30
|
+
value.each do |val|
|
31
|
+
|
32
|
+
if !object_in_array?(second_value, val)
|
33
|
+
|
34
|
+
difference[uri] ||= Hash.new
|
35
|
+
difference[uri][predicate] ||= Array.new
|
36
|
+
difference[uri][predicate] << val
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
else
|
41
|
+
#uri is in second, but not this property and value
|
42
|
+
difference[uri] ||= Hash.new
|
43
|
+
difference[uri][predicate] = value
|
44
|
+
end
|
45
|
+
end
|
46
|
+
else
|
47
|
+
#uri not in second, so pass all straight-through
|
48
|
+
difference[uri] = properties
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
return difference
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
#Is there an object in the specified array, that matches the provided description
|
58
|
+
def SetAlgebra.object_in_array?(array, val)
|
59
|
+
array.each do |entry|
|
60
|
+
|
61
|
+
if entry["type"] == val["type"]
|
62
|
+
if entry["value"] == val["value"]
|
63
|
+
|
64
|
+
if entry["type"] == "literal"
|
65
|
+
if entry["datatype"] == val["datatype"] &&
|
66
|
+
entry["lang"] = val["lang"]
|
67
|
+
return true
|
68
|
+
end
|
69
|
+
end
|
70
|
+
return true
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
end
|
75
|
+
return false
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
data/lib/pho/store.rb
CHANGED
@@ -2,14 +2,10 @@ module Pho
|
|
2
2
|
|
3
3
|
#TODO:
|
4
4
|
#
|
5
|
-
# Make it work with Ruby < 1.8.7
|
6
|
-
# Changesets
|
7
|
-
# Multisparql
|
8
|
-
#
|
9
5
|
# Conditional deletions
|
10
6
|
# If-Modified-Since support
|
11
7
|
# Robustness in uri fetching
|
12
|
-
|
8
|
+
# Etag Testing
|
13
9
|
|
14
10
|
# The Store class acts as a lightweight client interface to the Talis Platform API
|
15
11
|
# (http://n2.talis.com/wiki/Platform_API). The class provides methods for interacting
|
@@ -75,10 +71,19 @@ module Pho
|
|
75
71
|
# METABOX
|
76
72
|
#############
|
77
73
|
|
78
|
-
# Store some RDF in the Metabox associated with this store
|
74
|
+
# Store some RDF in the Metabox associated with this store. Default is to store the
|
75
|
+
# data in the metabox, but a private graph name can also be specified.
|
76
|
+
|
79
77
|
# data:: a String containing the data to store
|
80
|
-
|
81
|
-
|
78
|
+
# graph_name:: name of a private graph in which to store the data. E.g. "1" or "private". Resolves to /meta/graphs/graph_name
|
79
|
+
def store_data(data, graph_name=nil)
|
80
|
+
u = nil
|
81
|
+
if graph_name == nil
|
82
|
+
u = build_uri("/meta")
|
83
|
+
else
|
84
|
+
u = build_uri("/meta/graphs/#{graph_name}")
|
85
|
+
end
|
86
|
+
|
82
87
|
response = @client.post(u, data, RDF_XML )
|
83
88
|
return response
|
84
89
|
end
|
@@ -97,9 +102,14 @@ module Pho
|
|
97
102
|
# An Accept header of "application/rdf+xml" will be sent in the request to support retrieval of RDF from
|
98
103
|
# URLs that support Content Negotiation.
|
99
104
|
#
|
105
|
+
# NOTE: Currently this method doesn't properly handle base uris of retrieved data. i.e. the data isn't parsed
|
106
|
+
# and there is no way to pass a base uri to the Platform. Be warned!
|
107
|
+
#
|
108
|
+
# The default is to store the data in the Metabox. But a private graph name can also be specified
|
100
109
|
# u:: the url of the data
|
101
110
|
# parameters:: a Hash of url parameters to pass when requesting data from the specified URL
|
102
|
-
|
111
|
+
# graph_name:: name of a private graph in which to store the data. E.g. "1" or "private". Resolves to /meta/graphs/graph_name
|
112
|
+
def store_url(u, parameters=nil, graph_name=nil)
|
103
113
|
|
104
114
|
headers = ACCEPT_RDF.clone()
|
105
115
|
dataresp = @client.get(u, parameters, headers )
|
@@ -109,7 +119,7 @@ module Pho
|
|
109
119
|
throw
|
110
120
|
end
|
111
121
|
|
112
|
-
return store_data(dataresp.content)
|
122
|
+
return store_data(dataresp.content, graph_name)
|
113
123
|
|
114
124
|
end
|
115
125
|
|
@@ -133,7 +143,29 @@ module Pho
|
|
133
143
|
record_etags("#{u}?about=#{uri}", etags, response)
|
134
144
|
return response
|
135
145
|
end
|
136
|
-
|
146
|
+
|
147
|
+
# Submit a Changeset to the Platform to update the metabox
|
148
|
+
#
|
149
|
+
# Default behaviour is to update the metabox with an unversioned name
|
150
|
+
# However using the optional parameters, changes can be made versioned, and
|
151
|
+
# can also be submitted to private graphs.
|
152
|
+
#
|
153
|
+
# rdf:: the RDF/XML describing the changes
|
154
|
+
# versioned:: true or false to indicate this is a versioned change
|
155
|
+
# graph_name:: name of private graph to update instead of metabox
|
156
|
+
def submit_changeset(rdf, versioned=false, graph_name=nil)
|
157
|
+
uri = "/meta"
|
158
|
+
if graph_name != nil
|
159
|
+
uri = uri + "/graphs/#{graph_name}"
|
160
|
+
end
|
161
|
+
uri = uri + "/changesets" if versioned
|
162
|
+
|
163
|
+
u = self.build_uri( uri )
|
164
|
+
headers = {"Content-Type" => "application/vnd.talis.changeset+xml"}
|
165
|
+
response = @client.post(u, rdf, headers)
|
166
|
+
return response
|
167
|
+
end
|
168
|
+
|
137
169
|
#############
|
138
170
|
# SERVICES
|
139
171
|
#############
|
@@ -142,40 +174,46 @@ module Pho
|
|
142
174
|
#
|
143
175
|
# query:: the SPARQL query
|
144
176
|
# format:: the preferred response format
|
145
|
-
def sparql_describe(query, format="application/rdf+xml")
|
146
|
-
return sparql(query, format)
|
177
|
+
def sparql_describe(query, format="application/rdf+xml", multisparql=false)
|
178
|
+
return sparql(query, format, multisparql)
|
147
179
|
end
|
148
180
|
|
149
181
|
#Perform a SPARQL CONSTRUCT query.
|
150
182
|
#
|
151
183
|
# query:: the SPARQL query
|
152
184
|
# format:: the preferred response format
|
153
|
-
def sparql_construct(query, format="application/rdf+xml")
|
154
|
-
return sparql(query, format)
|
185
|
+
def sparql_construct(query, format="application/rdf+xml", multisparql=false)
|
186
|
+
return sparql(query, format, multisparql)
|
155
187
|
end
|
156
188
|
|
157
189
|
#Perform a SPARQL ASK query.
|
158
190
|
#
|
159
191
|
# query:: the SPARQL query
|
160
192
|
# format:: the preferred response format
|
161
|
-
def sparql_ask(query, format="application/sparql-results+xml")
|
162
|
-
return sparql(query, format)
|
193
|
+
def sparql_ask(query, format="application/sparql-results+xml", multisparql=false)
|
194
|
+
return sparql(query, format, multisparql)
|
163
195
|
end
|
164
196
|
|
165
197
|
#Perform a SPARQL SELECT query.
|
166
198
|
#
|
167
199
|
# query:: the SPARQL query
|
168
200
|
# format:: the preferred response format
|
169
|
-
def sparql_select(query, format="application/sparql-results+xml")
|
170
|
-
return sparql(query, format)
|
201
|
+
def sparql_select(query, format="application/sparql-results+xml", multisparql=false)
|
202
|
+
return sparql(query, format, multisparql)
|
171
203
|
end
|
172
204
|
|
173
205
|
#Perform a SPARQL query
|
174
206
|
#
|
175
207
|
# query:: the SPARQL query
|
176
|
-
# format:: the preferred response format
|
177
|
-
|
178
|
-
|
208
|
+
# format:: the preferred response format
|
209
|
+
# multisparql:: use default sparql service or multisparql service
|
210
|
+
def sparql(query, format=nil, multisparql=false)
|
211
|
+
if multisparql
|
212
|
+
u = self.build_uri("/services/multisparql")
|
213
|
+
else
|
214
|
+
u = self.build_uri("/services/sparql")
|
215
|
+
end
|
216
|
+
|
179
217
|
params = {}
|
180
218
|
params["query"] = query
|
181
219
|
headers = {}
|
@@ -203,7 +241,7 @@ module Pho
|
|
203
241
|
def search(query, params=nil)
|
204
242
|
u = self.build_uri("/items")
|
205
243
|
search_params = get_search_params(u, query, params)
|
206
|
-
response = @client.get(u, search_params
|
244
|
+
response = @client.get(u, search_params)
|
207
245
|
return response
|
208
246
|
|
209
247
|
end
|
@@ -225,7 +263,7 @@ module Pho
|
|
225
263
|
u = self.build_uri("/services/facet")
|
226
264
|
search_params = get_search_params(u, query, params)
|
227
265
|
search_params["fields"] = facets.join(",")
|
228
|
-
response = @client.get(u, search_params
|
266
|
+
response = @client.get(u, search_params)
|
229
267
|
return response
|
230
268
|
end
|
231
269
|
|
@@ -244,7 +282,7 @@ module Pho
|
|
244
282
|
# uri:: the URL for the RSS 1.0 feed
|
245
283
|
def augment_uri(uri)
|
246
284
|
u = self.build_uri("/services/augment")
|
247
|
-
response = @client.get(u, {"data-uri" => uri}
|
285
|
+
response = @client.get(u, {"data-uri" => uri})
|
248
286
|
return response
|
249
287
|
end
|
250
288
|
|
@@ -0,0 +1,273 @@
|
|
1
|
+
$:.unshift File.join(File.dirname(__FILE__), "..", "lib")
|
2
|
+
require 'pho'
|
3
|
+
require 'test/unit'
|
4
|
+
require 'mocha'
|
5
|
+
require 'rexml/document'
|
6
|
+
require 'uri'
|
7
|
+
|
8
|
+
class ChangesetTest < Test::Unit::TestCase
|
9
|
+
|
10
|
+
def test_equality_resources
|
11
|
+
one = Pho::Update::Statement.create_resource("http://www.example.org/my-resource", "http://xmlns.com/foaf/0.1/homePage", "http://www.example.org/page1")
|
12
|
+
two = Pho::Update::Statement.create_resource("http://www.example.org/my-resource", "http://xmlns.com/foaf/0.1/homePage", "http://www.example.org/page2")
|
13
|
+
assert_equal(false, one == two)
|
14
|
+
assert_equal(true, one != two)
|
15
|
+
|
16
|
+
two = Pho::Update::Statement.create_resource("http://www.example.org/my-resource", "http://xmlns.com/foaf/0.1/homePage", "http://www.example.org/page1")
|
17
|
+
assert_are_equal(one, two)
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_equality_literals
|
21
|
+
one = Pho::Update::Statement.create_literal("http://www.example.org/my-resource", "http://xmlns.com/foaf/0.1/homePage", "one")
|
22
|
+
two = Pho::Update::Statement.create_literal("http://www.example.org/my-resource", "http://xmlns.com/foaf/0.1/homePage", "two")
|
23
|
+
assert_equal(false, one == two)
|
24
|
+
assert_equal(true, one != two)
|
25
|
+
|
26
|
+
two = Pho::Update::Statement.create_literal("http://www.example.org/my-resource", "http://xmlns.com/foaf/0.1/homePage", "one")
|
27
|
+
assert_are_equal(one, two)
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_statement_constructor
|
32
|
+
s = Pho::Update::Statement.create_literal("http://www.example.org/my-resource", "http://xmlns.com/foaf/0.1/homePage", "one", "en")
|
33
|
+
s = Pho::Update::Statement.create_literal("http://www.example.org/my-resource", "http://xmlns.com/foaf/0.1/homePage", "one", nil, "http://www.example.org/datatype")
|
34
|
+
assert_raise RuntimeError do
|
35
|
+
s = Pho::Update::Statement.create_literal("http://www.example.org/my-resource", "http://xmlns.com/foaf/0.1/homePage", "one", "en", "http://www.example.org/datatype")
|
36
|
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_statement_constructor_visibility
|
40
|
+
assert_raise NoMethodError do
|
41
|
+
s = Pho::Update::Statement.new("http://www.example.org/my-resource", "http://xmlns.com/foaf/0.1/homePage", "one", "en")
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
#Test validation code in constructor
|
47
|
+
def test_must_pass_uri_to_constructor
|
48
|
+
|
49
|
+
assert_raise URI::InvalidURIError do
|
50
|
+
cs = Pho::Update::Changeset.new(nil)
|
51
|
+
end
|
52
|
+
|
53
|
+
assert_raise URI::InvalidURIError do
|
54
|
+
cs = Pho::Update::Changeset.new("literal")
|
55
|
+
end
|
56
|
+
cs = Pho::Update::Changeset.new("http://www.example.org/my-resource")
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
def assert_are_equal(one, two)
|
61
|
+
assert_equal(true, one == two)
|
62
|
+
assert_equal(false, one != two)
|
63
|
+
|
64
|
+
assert_equal(true, two == one)
|
65
|
+
assert_equal(false, two != one)
|
66
|
+
end
|
67
|
+
|
68
|
+
def test_cannot_add_with_wrong_subject()
|
69
|
+
cs = Pho::Update::Changeset.new("http://www.example.org")
|
70
|
+
assert_raise RuntimeError do
|
71
|
+
cs.add_addition( Pho::Update::Statement.create_literal("http://example.net", "http://example.net/predicate", "foo") )
|
1
72
|
end
|
73
|
+
assert_raise RuntimeError do
|
74
|
+
cs.add_removal( Pho::Update::Statement.create_literal("http://example.net", "http://example.net/predicate", "foo") )
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def test_to_rdf_empty_changeset
|
79
|
+
cs = Pho::Update::Changeset.new("http://www.example.org/my-resource")
|
80
|
+
|
81
|
+
rdf = cs.to_rdf()
|
82
|
+
assert_not_nil(rdf)
|
83
|
+
|
84
|
+
cs_el = get_changeset(rdf)
|
85
|
+
soc = REXML::XPath.first(cs_el, "cs:subjectOfChange", Pho::Namespaces::MAPPING)
|
86
|
+
assert_equal("http://www.example.org/my-resource", soc.attributes["rdf:resource"] )
|
87
|
+
|
88
|
+
el = REXML::XPath.first(cs_el, "cs:creatorName", Pho::Namespaces::MAPPING)
|
89
|
+
assert_nil(el)
|
90
|
+
el = REXML::XPath.first(cs_el, "cs:changeReason", Pho::Namespaces::MAPPING)
|
91
|
+
assert_nil(el)
|
92
|
+
|
93
|
+
end
|
94
|
+
|
95
|
+
def test_to_rdf_empty_changeset_with_creator_and_reason
|
96
|
+
cs = Pho::Update::Changeset.new("http://www.example.org/my-resource", "creator", "reason")
|
97
|
+
|
98
|
+
cs_el = get_changeset(cs.to_rdf)
|
99
|
+
soc = REXML::XPath.first(cs_el, "cs:subjectOfChange", Pho::Namespaces::MAPPING)
|
100
|
+
assert_equal("http://www.example.org/my-resource", soc.attributes["rdf:resource"] )
|
101
|
+
|
102
|
+
el = REXML::XPath.first(cs_el, "cs:creatorName", Pho::Namespaces::MAPPING)
|
103
|
+
assert_equal("creator", el.text)
|
104
|
+
el = REXML::XPath.first(cs_el, "cs:changeReason", Pho::Namespaces::MAPPING)
|
105
|
+
assert_equal("reason", el.text)
|
106
|
+
|
107
|
+
end
|
108
|
+
|
109
|
+
def test_to_rdf_empty_changeset_with_block
|
110
|
+
cs = Pho::Update::Changeset.new("http://www.example.org/my-resource") do |obj|
|
111
|
+
obj.creator_name = "creator"
|
112
|
+
obj.change_reason = "reason"
|
113
|
+
end
|
114
|
+
|
115
|
+
root = parse(cs.to_rdf)
|
116
|
+
cs_el = REXML::XPath.first(root, "cs:ChangeSet", Pho::Namespaces::MAPPING)
|
117
|
+
soc = REXML::XPath.first(cs_el, "cs:subjectOfChange", Pho::Namespaces::MAPPING)
|
118
|
+
assert_equal("http://www.example.org/my-resource", soc.attributes["rdf:resource"] )
|
119
|
+
|
120
|
+
el = REXML::XPath.first(cs_el, "cs:creatorName", Pho::Namespaces::MAPPING)
|
121
|
+
assert_equal("creator", el.text)
|
122
|
+
el = REXML::XPath.first(cs_el, "cs:changeReason", Pho::Namespaces::MAPPING)
|
123
|
+
assert_equal("reason", el.text)
|
124
|
+
|
125
|
+
end
|
126
|
+
|
127
|
+
def test_to_rdf_with_resource_addition
|
128
|
+
cs = Pho::Update::Changeset.new("http://www.example.org/my-resource") do |c|
|
129
|
+
c.add_addition( Pho::Update::Statement.create_resource("http://www.example.org/my-resource", "http://xmlns.com/foaf/0.1/homePage", "http://www.example.org/page") )
|
130
|
+
end
|
131
|
+
assert_equal(1, cs.additions.length)
|
132
|
+
assert_equal(0, cs.removals.length)
|
133
|
+
|
134
|
+
cs_el = get_changeset(cs.to_rdf)
|
135
|
+
|
136
|
+
addition = REXML::XPath.first(cs_el, "cs:addition", Pho::Namespaces::MAPPING)
|
137
|
+
assert_not_nil(addition)
|
138
|
+
statement = REXML::XPath.first(addition, "rdf:Statement", Pho::Namespaces::MAPPING)
|
139
|
+
assert_not_nil(statement)
|
140
|
+
el = REXML::XPath.first(statement, "rdf:subject", Pho::Namespaces::MAPPING)
|
141
|
+
assert_equal("http://www.example.org/my-resource", el.attributes["rdf:resource"])
|
142
|
+
el = REXML::XPath.first(statement, "rdf:predicate", Pho::Namespaces::MAPPING)
|
143
|
+
assert_equal("http://xmlns.com/foaf/0.1/homePage", el.attributes["rdf:resource"])
|
144
|
+
el = REXML::XPath.first(statement, "rdf:object", Pho::Namespaces::MAPPING)
|
145
|
+
assert_equal("http://www.example.org/page", el.attributes["rdf:resource"])
|
146
|
+
assert_equal(nil, el.text)
|
147
|
+
|
148
|
+
end
|
149
|
+
|
150
|
+
def test_to_rdf_with_literal_addition
|
151
|
+
cs = Pho::Update::Changeset.new("http://www.example.org/my-resource") do |c|
|
152
|
+
c.add_addition( Pho::Update::Statement.create_literal("http://www.example.org/my-resource", "http://xmlns.com/foaf/0.1/homePage", "literal") )
|
153
|
+
end
|
154
|
+
assert_equal(1, cs.additions.length)
|
155
|
+
|
156
|
+
cs_el = get_changeset(cs.to_rdf)
|
157
|
+
|
158
|
+
addition = REXML::XPath.first(cs_el, "cs:addition", Pho::Namespaces::MAPPING)
|
159
|
+
assert_not_nil(addition)
|
160
|
+
statement = REXML::XPath.first(addition, "rdf:Statement", Pho::Namespaces::MAPPING)
|
161
|
+
assert_not_nil(statement)
|
162
|
+
el = REXML::XPath.first(statement, "rdf:subject", Pho::Namespaces::MAPPING)
|
163
|
+
assert_equal("http://www.example.org/my-resource", el.attributes["rdf:resource"])
|
164
|
+
el = REXML::XPath.first(statement, "rdf:predicate", Pho::Namespaces::MAPPING)
|
165
|
+
assert_equal("http://xmlns.com/foaf/0.1/homePage", el.attributes["rdf:resource"])
|
166
|
+
el = REXML::XPath.first(statement, "rdf:object", Pho::Namespaces::MAPPING)
|
167
|
+
assert_equal(nil, el.attributes["rdf:resource"])
|
168
|
+
assert_equal("literal", el.text)
|
169
|
+
|
170
|
+
end
|
171
|
+
|
172
|
+
def test_to_rdf_with_resource_removal
|
173
|
+
cs = Pho::Update::Changeset.new("http://www.example.org/my-resource") do |c|
|
174
|
+
c.add_removal( Pho::Update::Statement.create_resource("http://www.example.org/my-resource", "http://xmlns.com/foaf/0.1/homePage", "http://www.example.org/page") )
|
175
|
+
end
|
176
|
+
assert_equal(0, cs.additions.length)
|
177
|
+
assert_equal(1, cs.removals.length)
|
178
|
+
|
179
|
+
cs_el = get_changeset(cs.to_rdf)
|
180
|
+
|
181
|
+
removal = REXML::XPath.first(cs_el, "cs:removal", Pho::Namespaces::MAPPING)
|
182
|
+
assert_not_nil(removal)
|
183
|
+
|
184
|
+
statement = REXML::XPath.first(removal, "rdf:Statement", Pho::Namespaces::MAPPING)
|
185
|
+
assert_not_nil(statement)
|
186
|
+
el = REXML::XPath.first(statement, "rdf:subject", Pho::Namespaces::MAPPING)
|
187
|
+
assert_equal("http://www.example.org/my-resource", el.attributes["rdf:resource"])
|
188
|
+
el = REXML::XPath.first(statement, "rdf:predicate", Pho::Namespaces::MAPPING)
|
189
|
+
assert_equal("http://xmlns.com/foaf/0.1/homePage", el.attributes["rdf:resource"])
|
190
|
+
el = REXML::XPath.first(statement, "rdf:object", Pho::Namespaces::MAPPING)
|
191
|
+
assert_equal("http://www.example.org/page", el.attributes["rdf:resource"])
|
192
|
+
assert_equal(nil, el.text)
|
193
|
+
|
194
|
+
end
|
195
|
+
|
196
|
+
def test_submit_changeset
|
197
|
+
mc = mock()
|
198
|
+
mc.expects(:set_auth)
|
199
|
+
mc.expects(:post).with("http://api.talis.com/stores/testing/meta", "data", {"Content-Type" => "application/vnd.talis.changeset+xml"} )
|
200
|
+
|
201
|
+
store = Pho::Store.new("http://api.talis.com/stores/testing", "user", "pass", mc)
|
202
|
+
resp = store.submit_changeset("data")
|
203
|
+
end
|
204
|
+
|
205
|
+
def test_submit_changeset_to_graph
|
206
|
+
mc = mock()
|
207
|
+
mc.expects(:set_auth)
|
208
|
+
mc.expects(:post).with("http://api.talis.com/stores/testing/meta/graphs/1", "data", {"Content-Type" => "application/vnd.talis.changeset+xml"} )
|
209
|
+
|
210
|
+
store = Pho::Store.new("http://api.talis.com/stores/testing", "user", "pass", mc)
|
211
|
+
resp = store.submit_changeset("data", false, "1")
|
212
|
+
|
213
|
+
end
|
214
|
+
|
215
|
+
def test_submit_versioned_changeset
|
216
|
+
mc = mock()
|
217
|
+
mc.expects(:set_auth)
|
218
|
+
mc.expects(:post).with("http://api.talis.com/stores/testing/meta/changesets", "data", {"Content-Type" => "application/vnd.talis.changeset+xml"} )
|
219
|
+
|
220
|
+
store = Pho::Store.new("http://api.talis.com/stores/testing", "user", "pass", mc)
|
221
|
+
resp = store.submit_changeset("data", true)
|
222
|
+
end
|
223
|
+
|
224
|
+
def test_submit_versioned_changeset_to_graph
|
225
|
+
mc = mock()
|
226
|
+
mc.expects(:set_auth)
|
227
|
+
mc.expects(:post).with("http://api.talis.com/stores/testing/meta/graphs/1/changesets", "data", {"Content-Type" => "application/vnd.talis.changeset+xml"} )
|
228
|
+
|
229
|
+
store = Pho::Store.new("http://api.talis.com/stores/testing", "user", "pass", mc)
|
230
|
+
resp = store.submit_changeset("data", true, "1")
|
231
|
+
end
|
232
|
+
|
233
|
+
|
234
|
+
def test_submit
|
235
|
+
mc = mock()
|
236
|
+
mc.expects(:set_auth)
|
237
|
+
mc.expects(:post).with("http://api.talis.com/stores/testing/meta", anything, {"Content-Type" => "application/vnd.talis.changeset+xml"} )
|
238
|
+
|
239
|
+
store = Pho::Store.new("http://api.talis.com/stores/testing", "user", "pass", mc)
|
240
|
+
|
241
|
+
cs = Pho::Update::Changeset.new("http://www.example.org/my-resource") do |c|
|
242
|
+
c.add_removal( Pho::Update::Statement.create_resource("http://www.example.org/my-resource", "http://xmlns.com/foaf/0.1/homePage", "http://www.example.org/page") )
|
243
|
+
end
|
244
|
+
|
245
|
+
resp = cs.submit(store)
|
246
|
+
|
247
|
+
end
|
248
|
+
|
249
|
+
def test_submit_versioned
|
250
|
+
mc = mock()
|
251
|
+
mc.expects(:set_auth)
|
252
|
+
mc.expects(:post).with("http://api.talis.com/stores/testing/meta/changesets", anything, {"Content-Type" => "application/vnd.talis.changeset+xml"} )
|
253
|
+
|
254
|
+
store = Pho::Store.new("http://api.talis.com/stores/testing", "user", "pass", mc)
|
255
|
+
|
256
|
+
cs = Pho::Update::Changeset.new("http://www.example.org/my-resource") do |c|
|
257
|
+
c.add_removal( Pho::Update::Statement.create_resource("http://www.example.org/my-resource", "http://xmlns.com/foaf/0.1/homePage", "http://www.example.org/page") )
|
258
|
+
end
|
259
|
+
|
260
|
+
resp = cs.submit(store, true)
|
261
|
+
|
262
|
+
end
|
263
|
+
|
264
|
+
#parse rdf, return root element
|
265
|
+
def parse(rdf)
|
266
|
+
doc = REXML::Document.new(rdf)
|
267
|
+
return doc.root()
|
268
|
+
end
|
269
|
+
|
270
|
+
#parse rdf/xml and retrieve the changeset element
|
271
|
+
def get_changeset(rdf)
|
272
|
+
root = parse(rdf)
|
273
|
+
return REXML::XPath.first(root, "cs:ChangeSet", Pho::Namespaces::MAPPING)
|
274
|
+
end
|
275
|
+
end
|