sasquatch 0.0.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/README +57 -0
- data/VERSION +1 -0
- data/lib/sasquatch.rb +16 -0
- data/lib/sasquatch/changeset.rb +66 -0
- data/lib/sasquatch/http_party.rb +12 -0
- data/lib/sasquatch/rdf.rb +31 -0
- data/lib/sasquatch/rss10.rb +18 -0
- data/lib/sasquatch/rss10/format.rb +12 -0
- data/lib/sasquatch/rss10/reader.rb +226 -0
- data/lib/sasquatch/search_result.rb +87 -0
- data/lib/sasquatch/sparql_builder.rb +120 -0
- data/lib/sasquatch/store.rb +302 -0
- metadata +159 -0
data/README
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
Sasquatch - A DSL for the Talis Platform
|
2
|
+
==============================
|
3
|
+
|
4
|
+
Sasquatch makes it easy to add/delete/replace resources and triples on the Talis Platform using RDF.rb.
|
5
|
+
|
6
|
+
Example:
|
7
|
+
|
8
|
+
store = Sasquatch::Store.new(storename, {:username=>u, :password=>p}) # authentication is optional
|
9
|
+
|
10
|
+
resource = store.describe("http://example.org/1")
|
11
|
+
=> #<RDF::Graph:0x81467e78(<>)>
|
12
|
+
|
13
|
+
resources = store.describe_multi(['http://example.org/1', 'http://example.org/2'])
|
14
|
+
=> #<RDF::Graph:0x8100fd58(<>)>
|
15
|
+
|
16
|
+
store.delete_uri('http://example.org/1')
|
17
|
+
|
18
|
+
=> true
|
19
|
+
|
20
|
+
store.delete_uris(['http://example.org/2', 'http://example.org/3'])
|
21
|
+
|
22
|
+
=> true
|
23
|
+
|
24
|
+
Because ChangeSets are somewhat cumbersome, there are methods to replace triples or resources wholesale:
|
25
|
+
|
26
|
+
store.replace(resources) # where 'resources' is an RDF::Graph, array of RDF::Statements or RDF::RDFObjects::Resource
|
27
|
+
|
28
|
+
This will replace all of the subjects with what is sent. Pass a boolean true to make it a versioned changeset.
|
29
|
+
|
30
|
+
There is also:
|
31
|
+
|
32
|
+
store.replace_triple(old_triple, new_triple)
|
33
|
+
|
34
|
+
where old_triple and new_triple are RDF::Statements
|
35
|
+
|
36
|
+
and
|
37
|
+
|
38
|
+
store.replace_triples
|
39
|
+
|
40
|
+
which takes a Hash where the keys are the old triples and the values are the new triples.
|
41
|
+
|
42
|
+
search = store.search("italy")
|
43
|
+
=> [#<RDF::URI:0x80f92484(http://id.loc.gov/authorities/sh89006665#concept)>, #<RDF::URI:0x80f90df0(http://lcsubjects.org/subjects/sh89006665#concept)>, #<RDF::URI:0x80f9042c(http://id.loc.gov/authorities/sh2004008861#concept)>, #<RDF::URI:0x80f8f5e0(http://id.loc.gov/authorities/sh2006002475#concept)>, #<RDF::URI:0x80f8e974(http://id.loc.gov/authorities/sh86005484#concept)>, #<RDF::URI:0x80f8d31c(http://lcsubjects.org/subjects/sh2004008861#concept)>, #<RDF::URI:0x80f8c548(http://lcsubjects.org/subjects/sh2006002475#concept)>, #<RDF::URI:0x80f8b29c(http://lcsubjects.org/subjects/sh86005484#concept)>, #<RDF::URI:0x80f8a48c(http://id.loc.gov/authorities/sh85069035#concept)>, #<RDF::URI:0x80f88f38(http://lcsubjects.org/subjects/sh85069035#concept)>]
|
44
|
+
|
45
|
+
#search returns a Sasquatch::SearchResult, which is sort of a glorified array.
|
46
|
+
|
47
|
+
To get the next page of search results, use SearchResult#next, to get the previous, use SearchResult#previous
|
48
|
+
|
49
|
+
You can access the search result graph at SearchResult#graph
|
50
|
+
|
51
|
+
Because it is really an array of URIs, you can chain it into other Store methods:
|
52
|
+
|
53
|
+
store.delete_uris(store.search('italy'))
|
54
|
+
|
55
|
+
=> true
|
56
|
+
|
57
|
+
etc.
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1
|
data/lib/sasquatch.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'httparty'
|
2
|
+
require 'cgi'
|
3
|
+
require 'rdf'
|
4
|
+
require 'rdf/ntriples'
|
5
|
+
require 'rdf/json'
|
6
|
+
require 'sparql/client'
|
7
|
+
require 'json'
|
8
|
+
module Sasquatch
|
9
|
+
require File.dirname(__FILE__) + '/sasquatch/http_party'
|
10
|
+
require File.dirname(__FILE__) + '/sasquatch/store'
|
11
|
+
require File.dirname(__FILE__) + '/sasquatch/rdf'
|
12
|
+
require File.dirname(__FILE__) + '/sasquatch/changeset'
|
13
|
+
require File.dirname(__FILE__) + '/sasquatch/rss10'
|
14
|
+
require File.dirname(__FILE__) + '/sasquatch/search_result'
|
15
|
+
require File.dirname(__FILE__) + '/sasquatch/sparql_builder'
|
16
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module RDF
|
2
|
+
module Talis
|
3
|
+
class Changeset < RDF::Vocabulary('http://purl.org/vocab/changeset/schema#')
|
4
|
+
property :removal
|
5
|
+
property :addition
|
6
|
+
property :creatorName
|
7
|
+
property :createdDate
|
8
|
+
property :subjectOfChange
|
9
|
+
property :changeReason
|
10
|
+
property :ChangeSet
|
11
|
+
property :precedingChangeSet
|
12
|
+
end
|
13
|
+
|
14
|
+
class Bigfoot < RDF::Vocabulary('http://schemas.talis.com/2006/bigfoot/configuration#')
|
15
|
+
property :jobType
|
16
|
+
property :ResetDataJob
|
17
|
+
property :startTime
|
18
|
+
property :JobRequest
|
19
|
+
end
|
20
|
+
|
21
|
+
class TalisDir < RDF::Vocabulary('http://schemas.talis.com/2005/dir/schema#')
|
22
|
+
property :etag
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
module Sasquatch
|
28
|
+
class Changeset
|
29
|
+
attr_reader :resource, :statements, :subject_of_change
|
30
|
+
def initialize(subject_of_change, resource=nil)
|
31
|
+
@resource = RDF::Node.new unless resource
|
32
|
+
@subject_of_change = subject_of_change
|
33
|
+
@statements = []
|
34
|
+
@statements.concat [RDF::Statement.new(@resource, RDF.type, RDF::Talis::Changeset.ChangeSet),
|
35
|
+
RDF::Statement.new(@resource, RDF::Talis::Changeset.changeReason, "Generated in Platform Party"),
|
36
|
+
RDF::Statement.new(@resource, RDF::Talis::Changeset.createdDate, Time.now),
|
37
|
+
RDF::Statement.new(@resource, RDF::Talis::Changeset.subjectOfChange, subject_of_change)]
|
38
|
+
end
|
39
|
+
|
40
|
+
def remove_statements(stmts)
|
41
|
+
stmts = [stmts] if stmts.is_a?(RDF::Statement)
|
42
|
+
stmts.each do |stmt|
|
43
|
+
raise ArgumentError unless stmt.subject == @subject_of_change
|
44
|
+
@statements.concat changeset_statement(stmt, :removal)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def add_statements(stmts)
|
49
|
+
stmts = [stmts] if stmts.is_a?(RDF::Statement)
|
50
|
+
stmts.each do |stmt|
|
51
|
+
next unless stmt
|
52
|
+
raise ArgumentError unless stmt.subject == @subject_of_change
|
53
|
+
@statements.concat changeset_statement(stmt, :addition)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def changeset_statement(stmt, action)
|
58
|
+
s = RDF::Node.new
|
59
|
+
[RDF::Statement.new(@resource, RDF::Talis::Changeset.send(action), s),
|
60
|
+
RDF::Statement.new(s, RDF.type, RDF.to_rdf+"Statement"),
|
61
|
+
RDF::Statement.new(s, RDF.subject, stmt.subject),
|
62
|
+
RDF::Statement.new(s, RDF.predicate, stmt.predicate),
|
63
|
+
RDF::Statement.new(s, RDF.object, stmt.object)]
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module RDF
|
2
|
+
class Statement
|
3
|
+
def to_ntriples
|
4
|
+
RDF::Writer.for(:ntriples).buffer do |writer|
|
5
|
+
writer << self
|
6
|
+
end
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class Graph
|
11
|
+
attr_reader :requested_resource
|
12
|
+
def to_ntriples
|
13
|
+
RDF::Writer.for(:ntriples).buffer do |writer|
|
14
|
+
self.statements.each do |statement|
|
15
|
+
writer << statement
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
def set_requested_resource(uri)
|
20
|
+
@requested_resource = uri
|
21
|
+
end
|
22
|
+
|
23
|
+
def requested_resource
|
24
|
+
if self.respond_to?(:"[]") # in case rdf-rdfobjects is available
|
25
|
+
self[@requested_resource]
|
26
|
+
else
|
27
|
+
RDF::URI.intern(@requested_resource)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
|
2
|
+
module RDF
|
3
|
+
module RSS10
|
4
|
+
require File.dirname(__FILE__) + '/rss10/format'
|
5
|
+
require File.dirname(__FILE__) + '/rss10/reader'
|
6
|
+
# require 'rdf/n3/vocab'
|
7
|
+
# require 'rdf/n3/patches/array_hacks'
|
8
|
+
# require 'rdf/n3/patches/graph_properties'
|
9
|
+
# autoload :Meta, 'rdf/n3/reader/meta'
|
10
|
+
# autoload :Parser, 'rdf/n3/reader/parser'
|
11
|
+
# autoload :Reader, 'rdf/n3/reader'
|
12
|
+
# autoload :VERSION, 'rdf/n3/version'
|
13
|
+
# autoload :Writer, 'rdf/n3/writer'
|
14
|
+
#
|
15
|
+
# def self.debug?; @debug; end
|
16
|
+
# def self.debug=(value); @debug = value; end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module RDF::RSS10
|
2
|
+
|
3
|
+
class Format < RDF::Format
|
4
|
+
#content_type 'application/xml', :extension => :rss
|
5
|
+
#content_type 'application/rdf+xml', :extension => :rss
|
6
|
+
content_encoding 'utf-8'
|
7
|
+
|
8
|
+
reader { RDF::RSS10::Reader }
|
9
|
+
writer { RDF::RSS10::Writer }
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
@@ -0,0 +1,226 @@
|
|
1
|
+
module RDF::RSS10
|
2
|
+
class Reader < RDF::Reader
|
3
|
+
require 'nokogiri'
|
4
|
+
# copied indiscriminately from gkellogg's rdf/xml parser
|
5
|
+
|
6
|
+
def initialize(input = $stdin, options = {}, &block)
|
7
|
+
super do
|
8
|
+
|
9
|
+
@base_uri = uri(options[:base_uri]) if options[:base_uri]
|
10
|
+
|
11
|
+
@doc = case input
|
12
|
+
when Nokogiri::XML::Document then input
|
13
|
+
else Nokogiri::XML.parse(input, @base_uri.to_s)
|
14
|
+
end
|
15
|
+
|
16
|
+
raise RDF::ReaderError, "Synax errors:\n#{@doc.errors}" if !@doc.errors.empty? && validate?
|
17
|
+
raise RDF::ReaderError, "Empty document" if (@doc.nil? || @doc.root.nil?) && validate?
|
18
|
+
|
19
|
+
block.call(self) if block_given?
|
20
|
+
end
|
21
|
+
end
|
22
|
+
##
|
23
|
+
# Iterates the given block for each RDF statement in the input.
|
24
|
+
#
|
25
|
+
# @yield [statement]
|
26
|
+
# @yieldparam [RDF::Statement] statement
|
27
|
+
# @return [void]
|
28
|
+
def each_statement(&block)
|
29
|
+
# Block called from add_statement
|
30
|
+
@callback = block
|
31
|
+
|
32
|
+
root = @doc.root
|
33
|
+
|
34
|
+
|
35
|
+
rdf_nodes = root.xpath("/rdf:RDF", "rdf" => RDF.to_uri.to_s)
|
36
|
+
statements = []
|
37
|
+
rdf_nodes.each do |node|
|
38
|
+
|
39
|
+
|
40
|
+
root.xpath("//rss:channel", "rss"=>RDF::RSS.to_s).each do |channel|
|
41
|
+
if channel.attribute('about')
|
42
|
+
channel_uri = RDF::URI.intern(channel.attribute('about').value)
|
43
|
+
else
|
44
|
+
channel_uri = RDF::Node.new
|
45
|
+
end
|
46
|
+
statements << RDF::Statement.new(channel_uri, RDF.type, RDF::RSS.channel)
|
47
|
+
channel.children.each do |elem|
|
48
|
+
unless elem.name == 'items'
|
49
|
+
if elem.children.length == 1 && elem.children.first.is_a?(Nokogiri::XML::Text)
|
50
|
+
statements << RDF::Statement.new(channel_uri, RDF::URI.intern(elem.namespace.href + elem.name), literal(elem.children.first))
|
51
|
+
elsif elem.attribute('resource')
|
52
|
+
statements << RDF::Statement.new(channel_uri, RDF::URI.intern(elem.namespace.href + elem.name), RDF::URI.intern(elem.attribute('resource').value))
|
53
|
+
end
|
54
|
+
else
|
55
|
+
stmt = RDF::Statement.new(:subject=>channel_uri, :predicate=>RDF::URI.intern(elem.namespace.href + elem.name))
|
56
|
+
elem.children.each do |list|
|
57
|
+
if list.attribute('about')
|
58
|
+
list_uri = RDF::URI.intern(list.attribute('about').value)
|
59
|
+
else
|
60
|
+
list_uri = RDF::Node.new
|
61
|
+
end
|
62
|
+
|
63
|
+
stmt.object = list_uri
|
64
|
+
statements << stmt
|
65
|
+
list_type = RDF::URI.intern(list.namespace.href + list.name)
|
66
|
+
unless list_type == RDF.Description
|
67
|
+
statements << RDF::Statement.new(:subject=>list_uri, :predicate=>RDF.type, :object=>list_type)
|
68
|
+
end
|
69
|
+
list.children.each do |li|
|
70
|
+
stmt = RDF::Statement.new(:subject=>list_uri, :predicate=>RDF::URI.intern(li.namespace.href + li.name))
|
71
|
+
if li.attribute('resource')
|
72
|
+
stmt.object = RDF::URI.intern(li.attribute('resource').value)
|
73
|
+
elsif li.children.length == 1 && li.children.first.is_a?(Nokogiri::XML::Text)
|
74
|
+
stmt.object = literal(li.children.first)
|
75
|
+
end
|
76
|
+
statements << stmt if stmt.object
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
root.xpath("/rdf:RDF/rss:item", "rdf"=>RDF.to_uri.to_s, "rss"=>RDF::RSS.to_s).each do |item|
|
83
|
+
if item.attribute('about')
|
84
|
+
item_uri = RDF::URI.intern(item.attribute('about').value)
|
85
|
+
else
|
86
|
+
item_uri = RDF::Node.new
|
87
|
+
end
|
88
|
+
statements.concat statements_from_element(item, item_uri)
|
89
|
+
end
|
90
|
+
|
91
|
+
|
92
|
+
end
|
93
|
+
statements.each do |stmt |
|
94
|
+
yield stmt
|
95
|
+
end
|
96
|
+
statements.to_enum
|
97
|
+
end
|
98
|
+
|
99
|
+
def statements_from_element(elem, resource)
|
100
|
+
child_elements = {}
|
101
|
+
statements = []
|
102
|
+
elem.children.each do |el|
|
103
|
+
if el.attribute_with_ns('resource', RDF.to_uri.to_s)
|
104
|
+
statements << RDF::Statement.new(:subject=>resource, :predicate=>RDF::URI.intern(el.namespace.href+el.name), :object=>RDF::URI.intern(el.attribute_with_ns('resource', RDF.to_uri.to_s).value))
|
105
|
+
elsif all_text_nodes?(el.children)
|
106
|
+
statements << RDF::Statement.new(:subject=>resource, :predicate=>RDF::URI.intern(el.namespace.href+el.name),:object=>literal(el.children.first))
|
107
|
+
else
|
108
|
+
el.children.each do |e|
|
109
|
+
if e.attribute_with_ns('about', RDF.to_uri.to_s)
|
110
|
+
c = RDF::URI.intern(e.attribute_with_ns('about', RDF.to_uri.to_s).value)
|
111
|
+
statements << RDF::Statement.new(:subject=>resource, :predicate=>RDF::URI.intern(el.namespace.href+el.name), :object=>c)
|
112
|
+
child_elements[c] = e
|
113
|
+
e_type = RDF::URI.intern(e.namespace.href + e.name)
|
114
|
+
unless e_type == RDF.Description || RDF::RSS.item
|
115
|
+
statements << RDF::Statement.new(:subject=>c, :predicate=>RDF.type, :object=>e_type)
|
116
|
+
end
|
117
|
+
elsif has_child_elements?(e)
|
118
|
+
c = RDF::Node.new
|
119
|
+
statements << RDF::Statement.new(:subject=>resource, :predicate=>RDF::URI.intern(el.namespace.href+el.name), :object=>c)
|
120
|
+
child_elements[c] = e
|
121
|
+
e_type = RDF::URI.intern(e.namespace.href + e.name)
|
122
|
+
unless e_type == RDF.Description || RDF::RSS.item
|
123
|
+
statements << RDF::Statement.new(:subject=>c, :predicate=>RDF.type, :object=>e_type)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
child_elements.each_pair do |r,e|
|
130
|
+
statements.concat statements_from_element(e, r)
|
131
|
+
end
|
132
|
+
statements
|
133
|
+
end
|
134
|
+
|
135
|
+
def all_text_nodes?(nodes)
|
136
|
+
all_text = true
|
137
|
+
nodes.each {|n| all_text = false unless n.is_a?(Nokogiri::XML::Text)}
|
138
|
+
all_text
|
139
|
+
end
|
140
|
+
|
141
|
+
def has_child_elements?(elem)
|
142
|
+
children = false
|
143
|
+
elem.each {|e| children = true if e.is_a?(Nokogiri::XML::Element)}
|
144
|
+
children
|
145
|
+
end
|
146
|
+
|
147
|
+
def literal(txt)
|
148
|
+
options = {}
|
149
|
+
if txt.attribute('lang')
|
150
|
+
options[:language] = txt.attribute('lang').value.to_sym
|
151
|
+
end
|
152
|
+
if txt.attribute_with_ns('datatype', RDF.to_uri.to_s)
|
153
|
+
options[:datatype] = RDF::URI.intern(txt.attribute_with_ns('datatype', RDF.to_uri.to_s).value)
|
154
|
+
end
|
155
|
+
RDF::Literal.new(txt.inner_text, options)
|
156
|
+
end
|
157
|
+
|
158
|
+
def parse_children(elem, stmt)
|
159
|
+
old_stmt = nil
|
160
|
+
statements = []
|
161
|
+
if elem.attribute_with_ns("about", RDF.to_uri.to_s)
|
162
|
+
old_stmt = stmt
|
163
|
+
stmt = RDF::Statement.new(:subject=>elem.attribute_with_ns("about", RDF.to_uri.to_s).value)
|
164
|
+
type_object = RDF::URI.intern(elem.namespace.href+elem.name)
|
165
|
+
unless type_object == RDF.Description || type_object == RDF::RSS.item
|
166
|
+
stmt.predicate = RDF.type
|
167
|
+
stmt.object = type_object
|
168
|
+
statements << stmt
|
169
|
+
stmt = RDF::Statement.new
|
170
|
+
stmt.subject = RDF::URI.intern(elem.attribute_with_ns('about', RDF.to_uri.to_s).value)
|
171
|
+
end
|
172
|
+
end
|
173
|
+
elem.children.each do |el|
|
174
|
+
next if el.is_a?(Nokogiri::XML::Text)
|
175
|
+
if el.attribute_with_ns('resource', RDF.to_uri.to_s) || el.attribute('resource')
|
176
|
+
if el.attribute_with_ns('resource', RDF.to_uri.to_s)
|
177
|
+
stmt.object = RDF::URI.intern(el.attribute_with_ns('resource', RDF.to_uri.to_s).value)
|
178
|
+
else
|
179
|
+
stmt.object = RDF::URI.intern(el.attribute('resource').value)
|
180
|
+
end
|
181
|
+
stmt.predicate = RDF::URI.intern(el.namespace.href+el.name)
|
182
|
+
statements << stmt.dup
|
183
|
+
stmt = RDF::Statement.new(:subject=>stmt.subject)
|
184
|
+
elsif el.children.length == 1 && el.children.first.is_a?(Nokogiri::XML::Text)
|
185
|
+
stmt.predicate = RDF::URI.intern(el.namespace.href+el.name)
|
186
|
+
options = {}
|
187
|
+
txt = el.children.first
|
188
|
+
if txt.attribute('lang')
|
189
|
+
options[:language] = txt.attribute('lang').value.to_sym
|
190
|
+
end
|
191
|
+
if txt.attribute_with_ns('datatype', RDF.to_uri.to_s)
|
192
|
+
options[:datatype] = RDF::URI.intern(txt.attribute_with_ns('datatype', RDF.to_uri.to_s).value)
|
193
|
+
end
|
194
|
+
stmt.object = RDF::Literal.new(txt.inner_text, options)
|
195
|
+
statements << stmt.dup
|
196
|
+
stmt = RDF::Statement.new(:subject=>stmt.subject)
|
197
|
+
else
|
198
|
+
stmt_found = false
|
199
|
+
el.children.each do |child|
|
200
|
+
next unless child.is_a?(Nokogiri::XML::Element)
|
201
|
+
stmt.predicate = RDF::URI.intern(el.namespace.href+el.name)
|
202
|
+
if child.attribute_with_ns("about", RDF.to_uri.to_s)
|
203
|
+
stmt.object = RDF::URI.intern(child.attribute_with_ns("about", RDF.to_uri.to_s).value)
|
204
|
+
statements << stmt.dup
|
205
|
+
stmt = RDF::Statement.new(:subject=>stmt.subject)
|
206
|
+
stmt_found = true
|
207
|
+
else
|
208
|
+
stmt.object = RDF::Node.new
|
209
|
+
statements << stmt.dup
|
210
|
+
stmt = RDF::Statement.new(:subject=>stmt.subject)
|
211
|
+
stmt_found = true
|
212
|
+
end
|
213
|
+
end
|
214
|
+
puts "#{el.name}: #{el.inspect}" unless stmt_found
|
215
|
+
end
|
216
|
+
|
217
|
+
#puts "#{el.name}: #{el.class.name}"
|
218
|
+
statements.concat parse_children(el, stmt) unless el.children.empty?
|
219
|
+
end
|
220
|
+
if old_stmt
|
221
|
+
stmt = old_stmt
|
222
|
+
end
|
223
|
+
statements
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module Sasquatch
|
2
|
+
class SearchResult < Array
|
3
|
+
attr_reader :graph, :total_results, :max_results, :start_index, :search_context
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@results = []
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.new_from_query(graph, options, store)
|
10
|
+
search_result = self.new
|
11
|
+
search_result.graph = graph
|
12
|
+
search_result.search_context={:store=>store, :options=>options}
|
13
|
+
search_result.parse_graph
|
14
|
+
search_result
|
15
|
+
end
|
16
|
+
|
17
|
+
def graph=(g)
|
18
|
+
@graph = g
|
19
|
+
end
|
20
|
+
|
21
|
+
def search_context=(sc)
|
22
|
+
@search_context=sc
|
23
|
+
end
|
24
|
+
|
25
|
+
def parse_graph
|
26
|
+
return unless @graph
|
27
|
+
@graph.query(:predicate=>RDF::URI.intern("http://a9.com/-/spec/opensearch/1.1/totalResults")).each do |stmt|
|
28
|
+
@total_results = stmt.object.value.to_i
|
29
|
+
end
|
30
|
+
@total_results ||= 0
|
31
|
+
@graph.query(:predicate=>RDF::URI.intern("http://a9.com/-/spec/opensearch/1.1/startIndex")).each do |stmt|
|
32
|
+
@start_index = stmt.object.value.to_i
|
33
|
+
end
|
34
|
+
@start_index ||= 0
|
35
|
+
@graph.query(:predicate=>RDF::URI.intern("http://a9.com/-/spec/opensearch/1.1/itemsPerPage")).each do |stmt|
|
36
|
+
@max_results = stmt.object.value.to_i
|
37
|
+
end
|
38
|
+
@max_results ||= 0
|
39
|
+
|
40
|
+
set_results
|
41
|
+
end
|
42
|
+
|
43
|
+
def set_results
|
44
|
+
channel_uri = nil
|
45
|
+
@graph.query(:predicate=>RDF.type, :object=>RDF::RSS.channel).each do |channel|
|
46
|
+
channel_uri = channel.subject
|
47
|
+
end
|
48
|
+
return unless channel_uri
|
49
|
+
seq_uri = nil
|
50
|
+
@graph.query(:subject=>channel_uri, :predicate=>RDF::RSS.items).each do |items|
|
51
|
+
seq_uri = items.object
|
52
|
+
end
|
53
|
+
return unless seq_uri
|
54
|
+
@graph.query(:subject=>seq_uri).each do |li|
|
55
|
+
if li.predicate.to_s =~ /http:\/\/www\.w3\.org\/1999\/02\/22-rdf-syntax-ns#_(\d)+$/
|
56
|
+
(ns,idx) = li.predicate.to_s.split("#_")
|
57
|
+
idx = (idx.to_i - 1)
|
58
|
+
self[idx] = li.object
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def previous
|
64
|
+
if @start_index == 0
|
65
|
+
nil
|
66
|
+
else
|
67
|
+
o = {}
|
68
|
+
o[:offset] = @start_index - @max_results
|
69
|
+
o[:offset] = 0 if o[:offset] < 0
|
70
|
+
o[:sort] = @search_context[:options][:sort]
|
71
|
+
o[:max] = @max_results
|
72
|
+
@search_context[:store].search(@search_context[:options][:query][:query],o)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
def next
|
76
|
+
if (@start_index + self.count) >= @total_results
|
77
|
+
nil
|
78
|
+
else
|
79
|
+
o = {}
|
80
|
+
o[:offset] = @start_index + @max_results
|
81
|
+
o[:sort] = @search_context[:options][:sort]
|
82
|
+
o[:max] = @max_results
|
83
|
+
@search_context[:store].search(@search_context[:options][:query][:query],o)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
module Sasquatch
|
2
|
+
class SparqlBuilder < SPARQL::Client::Query
|
3
|
+
attr_reader :store, :variables
|
4
|
+
|
5
|
+
def self.init(store, *variables)
|
6
|
+
options = variables.last.is_a?(Hash) ? variables.pop : {}
|
7
|
+
self.new(store, options).set_variables(*variables)
|
8
|
+
end
|
9
|
+
def initialize(store, options={}, &block)
|
10
|
+
@store = store
|
11
|
+
|
12
|
+
super(:sparql, options, &block)
|
13
|
+
end
|
14
|
+
|
15
|
+
def sparql
|
16
|
+
@form = "CHANGEME"
|
17
|
+
self
|
18
|
+
end
|
19
|
+
|
20
|
+
def set_variables(*vars)
|
21
|
+
@variables = vars
|
22
|
+
self
|
23
|
+
end
|
24
|
+
def describe!(graph=:default)
|
25
|
+
describe
|
26
|
+
execute(graph)
|
27
|
+
end
|
28
|
+
|
29
|
+
def describe
|
30
|
+
@form = :describe
|
31
|
+
@values = *@variables.flatten.map { |var|
|
32
|
+
[var, var.is_a?(RDF::URI) ? var : RDF::Query::Variable.new(var)]
|
33
|
+
}
|
34
|
+
self
|
35
|
+
end
|
36
|
+
|
37
|
+
def select!(graph=:default)
|
38
|
+
select
|
39
|
+
execute(graph)
|
40
|
+
end
|
41
|
+
|
42
|
+
def select
|
43
|
+
@form = :select
|
44
|
+
@values = @variables.flatten.map { |var| [var, RDF::Query::Variable.new(var)] }
|
45
|
+
self
|
46
|
+
end
|
47
|
+
|
48
|
+
def ask!(graph=:default)
|
49
|
+
ask
|
50
|
+
execute(graph)
|
51
|
+
end
|
52
|
+
|
53
|
+
def ask
|
54
|
+
@form = :ask
|
55
|
+
self
|
56
|
+
end
|
57
|
+
|
58
|
+
def construct!(graph=:default)
|
59
|
+
construct
|
60
|
+
execute(graph)
|
61
|
+
end
|
62
|
+
|
63
|
+
def construct
|
64
|
+
@form = :construct
|
65
|
+
options[:template] = build_patterns(*@variables)
|
66
|
+
self
|
67
|
+
end
|
68
|
+
|
69
|
+
def execute(graph)
|
70
|
+
@store.send(:"sparql_#{@form}", self.to_s, graph)
|
71
|
+
end
|
72
|
+
|
73
|
+
##
|
74
|
+
# Returns the string representation of this query.
|
75
|
+
#
|
76
|
+
# @return [String]
|
77
|
+
def to_s
|
78
|
+
buffer = [form.to_s.upcase]
|
79
|
+
case form
|
80
|
+
when :select, :describe
|
81
|
+
buffer << 'DISTINCT' if options[:distinct]
|
82
|
+
buffer << 'REDUCED' if options[:reduced]
|
83
|
+
buffer << (values.empty? ? '*' : values.map { |v| serialize_value(v[1]) }.join(' '))
|
84
|
+
when :construct
|
85
|
+
buffer << '{'
|
86
|
+
buffer += serialize_patterns(options[:template])
|
87
|
+
buffer << '}'
|
88
|
+
end
|
89
|
+
|
90
|
+
buffer << "FROM #{serialize_value(options[:from])}" if options[:from]
|
91
|
+
|
92
|
+
unless (patterns.empty? && (options[:optionals].nil? || options[:optionals].empty?)) && form == :describe
|
93
|
+
buffer << 'WHERE {'
|
94
|
+
buffer += serialize_patterns(patterns)
|
95
|
+
if options[:optionals]
|
96
|
+
options[:optionals].each do |patterns|
|
97
|
+
buffer << 'OPTIONAL {'
|
98
|
+
buffer += serialize_patterns(patterns)
|
99
|
+
buffer << '}'
|
100
|
+
end
|
101
|
+
end
|
102
|
+
if options[:filters]
|
103
|
+
buffer += options[:filters].map { |filter| "FILTER(#{filter})" }
|
104
|
+
end
|
105
|
+
buffer << '}'
|
106
|
+
end
|
107
|
+
|
108
|
+
if options[:order_by]
|
109
|
+
buffer << 'ORDER BY'
|
110
|
+
buffer += options[:order_by].map { |var| var.is_a?(String) ? var : "?#{var}" }
|
111
|
+
end
|
112
|
+
|
113
|
+
buffer << "OFFSET #{options[:offset]}" if options[:offset]
|
114
|
+
buffer << "LIMIT #{options[:limit]}" if options[:limit]
|
115
|
+
options[:prefixes].reverse.each {|e| buffer.unshift("PREFIX #{e}") } if options[:prefixes]
|
116
|
+
|
117
|
+
buffer.join(' ')
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,302 @@
|
|
1
|
+
module Sasquatch
|
2
|
+
class Store
|
3
|
+
include HTTParty
|
4
|
+
default_timeout 30
|
5
|
+
|
6
|
+
attr_reader :store_name, :last_response, :sparql_clients
|
7
|
+
def initialize(storename, options={})
|
8
|
+
self.class.base_uri "http://api.talis.com/stores/#{storename}"
|
9
|
+
@store_name = storename
|
10
|
+
if options[:username]
|
11
|
+
set_credentials(options[:username], options[:password])
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
def set_credentials(username, password)
|
17
|
+
@auth = {:username => username, :password => password}
|
18
|
+
@auth[:headers] = self.get("/snapshots", {}).headers['www-authenticate']
|
19
|
+
end
|
20
|
+
|
21
|
+
def describe(uri)
|
22
|
+
options = {:query=>{:about=>uri, :output=>"ntriples"}}
|
23
|
+
@last_response = get("/meta", options)
|
24
|
+
graph = parse_ntriples(@last_response.body)
|
25
|
+
graph.set_requested_resource(uri)
|
26
|
+
graph
|
27
|
+
end
|
28
|
+
|
29
|
+
def describe_multi(uris)
|
30
|
+
sparql = "DESCRIBE "
|
31
|
+
uris.each {|uri| sparql << "<#{uri}> "}
|
32
|
+
options = {:query=>{:query=>sparql, :output=>"ntriples"}}
|
33
|
+
@last_response = get("/services/sparql", options)
|
34
|
+
graph = parse_ntriples(@last_response.body)
|
35
|
+
graph
|
36
|
+
end
|
37
|
+
|
38
|
+
def augment(uri, pattern=:cbd)
|
39
|
+
graph = augment_multi([uri], pattern)
|
40
|
+
graph.set_requested_resource(uri)
|
41
|
+
graph
|
42
|
+
end
|
43
|
+
|
44
|
+
def augment_multi(uris, pattern=:cbd)
|
45
|
+
sparql = "DESCRIBE ?o "
|
46
|
+
i = 1
|
47
|
+
where = []
|
48
|
+
uris.each do |uri|
|
49
|
+
sparql << "<#{uri}> "
|
50
|
+
where << "{<#{uri}> ?p ?o . }"
|
51
|
+
i += 1
|
52
|
+
end
|
53
|
+
sparql << "\nWHERE\n{ #{where.join(" UNION ")} }"
|
54
|
+
options = {:body=>{:query=>sparql, :output=>"ntriples"}}
|
55
|
+
@last_response = post("/services/sparql", options)
|
56
|
+
graph = parse_ntriples(@last_response.body)
|
57
|
+
graph
|
58
|
+
end
|
59
|
+
|
60
|
+
def get(path, options)
|
61
|
+
self.class.get(path, options)
|
62
|
+
end
|
63
|
+
|
64
|
+
def post(path, options)
|
65
|
+
self.class.post(path, options)
|
66
|
+
end
|
67
|
+
|
68
|
+
def save(graph_statement_or_resource, graph_name=nil)
|
69
|
+
path = "/meta"
|
70
|
+
path << "/graphs/#{graph_name}" if graph_name
|
71
|
+
options = {:headers=>{"Content-Type"=> "text/turtle"}, :body=>graph_statement_or_resource.to_ntriples, :digest_auth=>@auth}
|
72
|
+
@last_response = post(path, options )
|
73
|
+
if @last_response.response.code == "204"
|
74
|
+
true
|
75
|
+
else
|
76
|
+
false
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def search(query, options={})
|
81
|
+
accept = self.class.headers['Accept']
|
82
|
+
self.class.headers 'Accept' => 'application/json'
|
83
|
+
path = "/items"
|
84
|
+
opts = {:query=>options}
|
85
|
+
opts[:query][:query] = query
|
86
|
+
@last_response = get(path, opts)
|
87
|
+
#graph = parse_rss10(@last_response.body)
|
88
|
+
graph = parse_json(@last_response.body)
|
89
|
+
self.class.headers 'Accept' => accept
|
90
|
+
SearchResult.new_from_query(graph, opts, self)
|
91
|
+
end
|
92
|
+
|
93
|
+
def remove_triple(stmt, versioned=false)
|
94
|
+
remove_triples([stmt], versioned, creator)
|
95
|
+
end
|
96
|
+
|
97
|
+
def remove_triples(stmts, versioned=false)
|
98
|
+
changesets = {}
|
99
|
+
stmts.each do |stmt|
|
100
|
+
changesets[stmt.subject] ||= Changeset.new(stmt.subject)
|
101
|
+
changesets[stmt.subject].remove_statements(stmt)
|
102
|
+
end
|
103
|
+
graph = RDF::Graph.new
|
104
|
+
changesets.each_pair do |uri, changeset|
|
105
|
+
changeset.statements.each do |stmt|
|
106
|
+
graph << stmt
|
107
|
+
end
|
108
|
+
end
|
109
|
+
send_changeset(graph, versioned, creator)
|
110
|
+
end
|
111
|
+
|
112
|
+
def replace_triple(old_stmt, new_stmt, versioned=false)
|
113
|
+
replace_triples({old_triple=>new_triple}, versioned, creator)
|
114
|
+
end
|
115
|
+
|
116
|
+
##
|
117
|
+
# takes a Hash in form of {old_statement=>replacement_statement}
|
118
|
+
#
|
119
|
+
def replace_triples(changes, versioned=false, creator="sasquatch.rb")
|
120
|
+
changesets = {}
|
121
|
+
changes.each_pair do |old_stmt, new_stmt|
|
122
|
+
changesets[old_stmt.subject] ||= Changeset.new(old_stmt.subject)
|
123
|
+
changesets[old_stmt.subject].remove_statements(*old_stmt)
|
124
|
+
changesets[new_stmt.subject] ||= Changeset.new(new_stmt.subject)
|
125
|
+
changesets[new_stmt.subject].remove_statements(*new_stmt)
|
126
|
+
end
|
127
|
+
graph = RDF::Graph.new
|
128
|
+
changesets.each_pair do |uri, changeset|
|
129
|
+
changeset.statements.each do |stmt|
|
130
|
+
graph << stmt
|
131
|
+
end
|
132
|
+
end
|
133
|
+
send_changeset(graph, versioned, creator)
|
134
|
+
end
|
135
|
+
|
136
|
+
def replace(graph_statements_or_resource, versioned=false, creator="sasquatch.rb")
|
137
|
+
uris = case graph_statements_or_resource.class.name
|
138
|
+
when "RDF::Graph"
|
139
|
+
subjects = []
|
140
|
+
graph_statements_or_resource.each_subject {|s| subjects << s}
|
141
|
+
subjects
|
142
|
+
when "Array"
|
143
|
+
subjects = []
|
144
|
+
graph_statements_or_resource.each_subject {|s| subjects << s}
|
145
|
+
subjects.uniq
|
146
|
+
else
|
147
|
+
# This should only work for rdf-rdfobjects Resources
|
148
|
+
if graph_statements_or_resource.respond_to?(:predicates)
|
149
|
+
[graph_statements_or_resource.to_s]
|
150
|
+
end
|
151
|
+
end
|
152
|
+
raise ArgumentError unless uris
|
153
|
+
current_graph = describe_multi(uris)
|
154
|
+
changesets = {}
|
155
|
+
current_graph.each_statement.each do |stmt|
|
156
|
+
changesets[stmt.subject] ||= Changeset.new(stmt.subject)
|
157
|
+
changesets[stmt.subject].remove_statements(stmt)
|
158
|
+
end
|
159
|
+
replacements = case graph_statements_or_resource.class.name
|
160
|
+
when "Array" then graph_statements_or_resource
|
161
|
+
else
|
162
|
+
graph_statements_or_resource.statements
|
163
|
+
end
|
164
|
+
replacements.each do |stmt|
|
165
|
+
changesets[stmt.subject] ||= Changeset.new(stmt.subject)
|
166
|
+
changesets[stmt.subject].add_statements(stmt)
|
167
|
+
end
|
168
|
+
graph = RDF::Graph.new
|
169
|
+
changesets.each do |uri, changeset|
|
170
|
+
changeset.statements.each do |stmt|
|
171
|
+
graph << stmt
|
172
|
+
end
|
173
|
+
end
|
174
|
+
send_changeset(graph, versioned, creator)
|
175
|
+
end
|
176
|
+
|
177
|
+
def send_changeset(graph, versioned=false, creator="sasquatch.rb")
|
178
|
+
path = "/meta"
|
179
|
+
path << "/changesets" if versioned
|
180
|
+
|
181
|
+
graph.query(:predicate=>RDF.type, :object=>RDF::Talis::Changeset.ChangeSet).each_subject do |cs|
|
182
|
+
graph << [cs, RDF::Talis::Changeset.creatorName, creator]
|
183
|
+
end
|
184
|
+
|
185
|
+
options = {:headers=>{"Content-Type"=> "application/vnd.talis.changeset+turtle"}, :body=>graph.to_ntriples, :digest_auth=>@auth}
|
186
|
+
@last_response = post(path, options )
|
187
|
+
if !versioned && @last_response.response.code =~ /^20[0-9]$/
|
188
|
+
true
|
189
|
+
elsif versioned && @last_response.response.code =~ /20[012]/
|
190
|
+
true
|
191
|
+
else
|
192
|
+
false
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
def delete_uri(uri, versioned=false, creator="sasquatch.rb")
|
197
|
+
delete_uris([uri], versioned, creator)
|
198
|
+
end
|
199
|
+
|
200
|
+
def delete_uris(uris, versioned=false, creator="sasquatch.rb")
|
201
|
+
current_graph = describe_multi(uris)
|
202
|
+
changesets = []
|
203
|
+
uris.each do |uri|
|
204
|
+
u = RDF::URI.intern(uri)
|
205
|
+
cs = Changeset.new(u)
|
206
|
+
cs.remove_statements(current_graph.query(:subject=>u))
|
207
|
+
changesets << cs
|
208
|
+
end
|
209
|
+
graph = RDF::Graph.new
|
210
|
+
changesets.each do |changeset|
|
211
|
+
changeset.statements.each do |stmt|
|
212
|
+
graph << stmt
|
213
|
+
end
|
214
|
+
end
|
215
|
+
send_changeset(graph, versioned, creator)
|
216
|
+
end
|
217
|
+
|
218
|
+
def sparql(*variables)
|
219
|
+
SparqlBuilder.init(self,variables)
|
220
|
+
end
|
221
|
+
|
222
|
+
def sparql_describe(query, graph=:default)
|
223
|
+
path = "/services/sparql"
|
224
|
+
unless graph == :default
|
225
|
+
path << "/graphs/#{graph}"
|
226
|
+
end
|
227
|
+
options = {:query=>{:query=>query, :output=>'ntriples'}}
|
228
|
+
@last_response = get(path, options)
|
229
|
+
graph = parse_ntriples(@last_response.body)
|
230
|
+
graph
|
231
|
+
end
|
232
|
+
|
233
|
+
alias :sparql_construct :sparql_describe
|
234
|
+
|
235
|
+
def sparql_select(query, graph=:default)
|
236
|
+
path = "/services/sparql"
|
237
|
+
unless graph == :default
|
238
|
+
path << "/graphs/#{graph}"
|
239
|
+
end
|
240
|
+
options = {:query=>{:query=>query, :output=>'json'}}
|
241
|
+
@last_response = get(path, options)
|
242
|
+
SPARQL::Client.parse_json_bindings(@last_response.body) || false
|
243
|
+
end
|
244
|
+
|
245
|
+
alias :sparql_ask :sparql_select
|
246
|
+
|
247
|
+
def access_status
|
248
|
+
accept = self.class.headers['Accept']
|
249
|
+
self.class.headers 'Accept' => 'application/json'
|
250
|
+
path = "/config/access-status"
|
251
|
+
@last_response = get(path, {})
|
252
|
+
graph = parse_json(@last_response.body)
|
253
|
+
self.class.headers 'Accept' => accept
|
254
|
+
graph
|
255
|
+
end
|
256
|
+
|
257
|
+
def read_only?
|
258
|
+
access_status.query(:predicate=>RDF::URI.intern('http://schemas.talis.com/2006/bigfoot/configuration#accessMode')).each do |stmt|
|
259
|
+
return true if stmt.object == RDF::URI.intern("http://schemas.talis.com/2006/bigfoot/statuses#read-only")
|
260
|
+
end
|
261
|
+
return false
|
262
|
+
end
|
263
|
+
|
264
|
+
def status_message
|
265
|
+
access_status.query(:predicate=>RDF::URI.intern('http://schemas.talis.com/2006/bigfoot/configuration#statusMessage')).each do |stmt|
|
266
|
+
return stmt.object.value
|
267
|
+
end
|
268
|
+
nil
|
269
|
+
end
|
270
|
+
|
271
|
+
def parse_ntriples(body)
|
272
|
+
read_graph(body, :ntriples)
|
273
|
+
end
|
274
|
+
|
275
|
+
def parse_json(body)
|
276
|
+
read_graph(body, :json)
|
277
|
+
end
|
278
|
+
|
279
|
+
def parse_rss10(body)
|
280
|
+
read_graph(body, :rss10)
|
281
|
+
end
|
282
|
+
|
283
|
+
def read_graph(data, format)
|
284
|
+
graph = RDF::Graph.new
|
285
|
+
RDF::Reader.for(format).new(data) do |reader|
|
286
|
+
reader.each_statement do |statement|
|
287
|
+
graph << statement
|
288
|
+
end
|
289
|
+
end
|
290
|
+
graph
|
291
|
+
end
|
292
|
+
|
293
|
+
# Parse the response body however you like
|
294
|
+
class Parser::Simple < HTTParty::Parser
|
295
|
+
def parse
|
296
|
+
body
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
parser Parser::Simple
|
301
|
+
end
|
302
|
+
end
|
metadata
ADDED
@@ -0,0 +1,159 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sasquatch
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Ross Singer
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-04-01 00:00:00 -04:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: httparty
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 11
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
- 7
|
33
|
+
- 4
|
34
|
+
version: 0.7.4
|
35
|
+
type: :runtime
|
36
|
+
version_requirements: *id001
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: rdf
|
39
|
+
prerelease: false
|
40
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
hash: 17
|
46
|
+
segments:
|
47
|
+
- 0
|
48
|
+
- 3
|
49
|
+
- 1
|
50
|
+
version: 0.3.1
|
51
|
+
type: :runtime
|
52
|
+
version_requirements: *id002
|
53
|
+
- !ruby/object:Gem::Dependency
|
54
|
+
name: rdf-json
|
55
|
+
prerelease: false
|
56
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
hash: 19
|
62
|
+
segments:
|
63
|
+
- 0
|
64
|
+
- 3
|
65
|
+
- 0
|
66
|
+
version: 0.3.0
|
67
|
+
type: :runtime
|
68
|
+
version_requirements: *id003
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: sparql-client
|
71
|
+
prerelease: false
|
72
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ">="
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
hash: 13
|
78
|
+
segments:
|
79
|
+
- 0
|
80
|
+
- 0
|
81
|
+
- 9
|
82
|
+
version: 0.0.9
|
83
|
+
type: :runtime
|
84
|
+
version_requirements: *id004
|
85
|
+
- !ruby/object:Gem::Dependency
|
86
|
+
name: rspec
|
87
|
+
prerelease: false
|
88
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ">="
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
hash: 11
|
94
|
+
segments:
|
95
|
+
- 2
|
96
|
+
- 1
|
97
|
+
- 0
|
98
|
+
version: 2.1.0
|
99
|
+
type: :development
|
100
|
+
version_requirements: *id005
|
101
|
+
description: A highly opinionated Ruby client for the Talis Platform using HTTParty and RDF.rb
|
102
|
+
email: ross.singer@talis.com
|
103
|
+
executables: []
|
104
|
+
|
105
|
+
extensions: []
|
106
|
+
|
107
|
+
extra_rdoc_files: []
|
108
|
+
|
109
|
+
files:
|
110
|
+
- README
|
111
|
+
- VERSION
|
112
|
+
- lib/sasquatch/changeset.rb
|
113
|
+
- lib/sasquatch/http_party.rb
|
114
|
+
- lib/sasquatch/rdf.rb
|
115
|
+
- lib/sasquatch/rss10/format.rb
|
116
|
+
- lib/sasquatch/rss10/reader.rb
|
117
|
+
- lib/sasquatch/rss10.rb
|
118
|
+
- lib/sasquatch/search_result.rb
|
119
|
+
- lib/sasquatch/sparql_builder.rb
|
120
|
+
- lib/sasquatch/store.rb
|
121
|
+
- lib/sasquatch.rb
|
122
|
+
has_rdoc: false
|
123
|
+
homepage: http://github.com/rsinger/sasquatch
|
124
|
+
licenses:
|
125
|
+
- MIT
|
126
|
+
post_install_message:
|
127
|
+
rdoc_options: []
|
128
|
+
|
129
|
+
require_paths:
|
130
|
+
- lib
|
131
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
132
|
+
none: false
|
133
|
+
requirements:
|
134
|
+
- - ">="
|
135
|
+
- !ruby/object:Gem::Version
|
136
|
+
hash: 57
|
137
|
+
segments:
|
138
|
+
- 1
|
139
|
+
- 8
|
140
|
+
- 7
|
141
|
+
version: 1.8.7
|
142
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
143
|
+
none: false
|
144
|
+
requirements:
|
145
|
+
- - ">="
|
146
|
+
- !ruby/object:Gem::Version
|
147
|
+
hash: 3
|
148
|
+
segments:
|
149
|
+
- 0
|
150
|
+
version: "0"
|
151
|
+
requirements: []
|
152
|
+
|
153
|
+
rubyforge_project:
|
154
|
+
rubygems_version: 1.3.7
|
155
|
+
signing_key:
|
156
|
+
specification_version: 3
|
157
|
+
summary: A highly opinionated Ruby client for the Talis Platform.
|
158
|
+
test_files: []
|
159
|
+
|