tripod 0.0.10 → 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.
@@ -13,10 +13,18 @@ module Tripod::Repository
13
13
  # person.hydrate!
14
14
  #
15
15
  # @example Hydrate the resource from a passed in graph
16
- # person.hydrate!(my_graph)
16
+ # person.hydrate!(:graph => my_graph)
17
+ #
18
+ # @example Only hydrate certain predicates (ignored if a graph is passde in)
19
+ # person.hydrate!(:only => ["http://foo", "http://bar"])
20
+ # person.hydrate!(:only => "http://foo")
21
+ #
17
22
  #
18
23
  # @return [ RDF::Repository ] A reference to the repository for this instance.
19
- def hydrate!(graph = nil)
24
+ def hydrate!(opts = {})
25
+
26
+ graph = opts[:graph]
27
+ only_hydrate_predicates = [opts[:only]].flatten # allow
20
28
 
21
29
  # we require that the uri is set.
22
30
  raise Tripod::Errors::UriNotSet.new() unless @uri
@@ -31,7 +39,16 @@ module Tripod::Repository
31
39
  end
32
40
  end
33
41
  else
34
- triples = Tripod::SparqlClient::Query::describe("DESCRIBE <#{uri}>")
42
+
43
+ unless only_hydrate_predicates && only_hydrate_predicates.any?
44
+ triples = Tripod::SparqlClient::Query::describe("DESCRIBE <#{uri}>")
45
+ else
46
+ query = "CONSTRUCT { <#{uri}> ?p ?o } WHERE { <#{uri}> ?p ?o . FILTER ("
47
+ query += only_hydrate_predicates.map { |p| "?p = <#{p.to_s}>" }.join(" || ")
48
+ query += ")}"
49
+ triples = Tripod::SparqlClient::Query::construct(query)
50
+ end
51
+
35
52
  @repository = RDF::Repository.new
36
53
  RDF::Reader.for(:ntriples).new(triples) do |reader|
37
54
  reader.each_statement do |statement|
@@ -9,8 +9,12 @@ module Tripod::Resource
9
9
  include Tripod::Components
10
10
 
11
11
  included do
12
- # every resource needs a graph and a uri set.
13
- validates_presence_of :uri, :graph_uri
12
+ # every resource needs a graph set.
13
+ validates_presence_of :graph_uri
14
+ # every instance of a resource has an rdf type field, which is set at the class level
15
+ class_attribute :_RDF_TYPE
16
+ # the Graph URI is set at the class level by default also, although this can be overridden in the constructor
17
+ class_attribute :_GRAPH_URI
14
18
  end
15
19
 
16
20
  attr_reader :new_record
@@ -18,7 +22,6 @@ module Tripod::Resource
18
22
  attr_reader :uri
19
23
 
20
24
  # Instantiate a +Resource+.
21
- # Optionsally pass a uri
22
25
  #
23
26
  # @example Instantiate a new Resource
24
27
  # Person.new('http://swirrl.com/ric.rdf#me')
@@ -26,29 +29,17 @@ module Tripod::Resource
26
29
  # @param [ String, RDF::URI ] uri The uri of the resource.
27
30
  #
28
31
  # @return [ Resource ] A new +Resource+
29
- def initialize(uri=nil, graph_uri=nil)
30
- @new_record = true
31
- @uri = RDF::URI(uri.to_s) if uri
32
- @graph_uri = RDF::URI(graph_uri.to_s) if graph_uri
33
- @repository = RDF::Repository.new
34
- end
32
+ def initialize(uri, graph_uri=nil)
33
+ raise Tripod::Errors::UriNotSet.new('uri missing') unless uri
34
+ @uri = RDF::URI(uri.to_s)
35
35
 
36
- # Set the uri for this resource
37
- def uri=(new_uri)
38
- if new_uri
39
- @uri = RDF::URI(new_uri.to_s)
40
- else
41
- @uri = nil
42
- end
43
- end
36
+ graph_uri ||= self.class._GRAPH_URI if self.class._GRAPH_URI
37
+ raise Tripod::Errors::UriNotSet.new('graph_uri missing') unless graph_uri
38
+ @graph_uri = RDF::URI(graph_uri)
44
39
 
45
- # Set the uri for this resource
46
- def graph_uri=(new_graph_uri)
47
- if new_graph_uri
48
- @graph_uri = RDF::URI(new_graph_uri.to_s)
49
- else
50
- @graph_uri = nil
51
- end
40
+ @repository = RDF::Repository.new
41
+ @new_record = true
42
+ self.rdf_type = self.class._RDF_TYPE if respond_to?(:rdf_type=)
52
43
  end
53
44
 
54
45
  # default comparison is via the uri
@@ -102,6 +93,14 @@ module Tripod::Resource
102
93
  other.class == Class ? self <= other : other.is_a?(self)
103
94
  end
104
95
 
96
+ def rdf_type(new_rdf_type)
97
+ field :rdf_type, RDF.type
98
+ self._RDF_TYPE = new_rdf_type
99
+ end
100
+
101
+ def graph_uri(new_graph_uri)
102
+ self._GRAPH_URI = new_graph_uri
103
+ end
105
104
  end
106
105
 
107
106
  end
@@ -0,0 +1,27 @@
1
+ # encoding: utf-8
2
+
3
+ # This module defines behaviour for finders.
4
+ module Tripod::Serialization
5
+ extend ActiveSupport::Concern
6
+
7
+ # Serialises this resource's triples to rdf/xml
8
+ def to_rdf
9
+ @repository.dump(:rdfxml)
10
+ end
11
+
12
+ # Serialises this resource's triples to turtle
13
+ def to_ttl
14
+ @repository.dump(:n3)
15
+ end
16
+
17
+ # Serialises this resource's triples to n-triples
18
+ def to_nt
19
+ @repository.dump(:ntriples)
20
+ end
21
+
22
+ # Serialises this resource's triples to JSON-LD
23
+ def to_json(opts={})
24
+ @repository.dump(:jsonld)
25
+ end
26
+
27
+ end
@@ -26,10 +26,8 @@ module Tripod::SparqlClient
26
26
  body = e.http_body
27
27
  if body.start_with?('Error 400: Parse error:')
28
28
  # TODO: this is a SPARQL parsing exception. Do something different.
29
- puts body.inspect
30
29
  raise e
31
30
  else
32
- puts body.inspect
33
31
  raise e
34
32
  end
35
33
  end
@@ -68,6 +66,21 @@ module Tripod::SparqlClient
68
66
  response = self.query(query, nil, {:accept=>accept_header})
69
67
  return response.body
70
68
  end
69
+
70
+ # Executes a CONSTRUCT +query+ against the SPARQL endpoint.
71
+ # Executes the +query+ and returns ntriples by default
72
+ #
73
+ # @example Run a CONSTRUCT query
74
+ # Tripod::SparqlClient::Query.select('CONSTRUCT <http://foo>')
75
+ #
76
+ # @param [ String ] query The query to run
77
+ # @param [ String ] accept_header The header to pass to the database.
78
+
79
+ # @return [ String ] the raw response from the endpoint
80
+ def self.construct(query, accept_header='application/n-triples')
81
+ response = self.query(query, nil, {:accept=>accept_header})
82
+ return response.body
83
+ end
71
84
  end
72
85
 
73
86
  module Update
@@ -92,10 +105,8 @@ module Tripod::SparqlClient
92
105
  body = e.http_body
93
106
  if body.start_with?('Error 400: Parse error:')
94
107
  # TODO: this is a SPARQL parsing exception. Do something different.
95
- puts body.inspect
96
108
  raise e
97
109
  else
98
- puts body.inspect
99
110
  raise e
100
111
  end
101
112
  end
@@ -1,3 +1,3 @@
1
1
  module Tripod
2
- VERSION = "0.0.10"
2
+ VERSION = "0.2"
3
3
  end
@@ -2,7 +2,11 @@ class Person
2
2
 
3
3
  include Tripod::Resource
4
4
 
5
+ rdf_type 'http://person'
6
+ graph_uri 'http://graph'
7
+
5
8
  field :name, 'http://name'
9
+ field :knows, 'http://knows', :multivalued => true
6
10
  field :aliases, 'http://alias', :multivalued => true
7
11
  field :age, 'http://age', :datatype => RDF::XSD.integer
8
12
  field :important_dates, 'http://importantdates', :datatype => RDF::XSD.date, :multivalued => true
@@ -1,103 +1,112 @@
1
1
  require "spec_helper"
2
2
 
3
3
  describe Tripod::Attributes do
4
+ describe ".read_attribute" do
5
+ let(:person) do
6
+ p = Person.new('http://barry')
7
+ p.name = 'Barry'
8
+ p
9
+ end
4
10
 
5
- before do
6
- @uri = 'http://ric'
7
- @graph = RDF::Graph.new
8
-
9
- stmt = RDF::Statement.new
10
- stmt.subject = RDF::URI.new(@uri)
11
- stmt.predicate = RDF::URI.new('http://blog')
12
- stmt.object = RDF::URI.new('http://blog1')
13
- @graph << stmt
14
-
15
- stmt2 = RDF::Statement.new
16
- stmt2.subject = RDF::URI.new(@uri)
17
- stmt2.predicate = RDF::URI.new('http://blog')
18
- stmt2.object = RDF::URI.new('http://blog2')
19
- @graph << stmt2
20
-
21
- stmt3 = RDF::Statement.new
22
- stmt3.subject = RDF::URI.new(@uri)
23
- stmt3.predicate = RDF::URI.new('http://name')
24
- stmt3.object = "ric"
25
- @graph << stmt3
26
- end
11
+ it "should read the given attribute" do
12
+ person[:name].should == 'Barry'
13
+ end
27
14
 
28
- let(:person) do
29
- p = Person.new(@uri)
30
- p.hydrate!(@graph)
31
- p
32
- end
15
+ context "where the attribute is multi-valued" do
16
+ before do
17
+ person.aliases = ['Boz', 'Baz', 'Bez']
18
+ end
33
19
 
34
- describe "#[]" do
35
- it 'returns the values where the predicate matches' do
36
- values = person['http://blog']
37
- values.length.should == 2
38
- values.first.should == RDF::URI('http://blog1')
39
- values[1].should == RDF::URI('http://blog2')
20
+ it "should return an array" do
21
+ person[:aliases].should == ['Boz', 'Baz', 'Bez']
22
+ end
40
23
  end
41
- end
42
24
 
43
- describe "#read_attribute" do
44
- it 'returns the values where the predicate matches' do
45
- values = person.read_attribute('http://blog')
46
- values.length.should == 2
47
- values.first.should == RDF::URI('http://blog1')
48
- values[1].should == RDF::URI('http://blog2')
25
+ context "where field is given and single-valued" do
26
+ let(:field) { Person.send(:field_for, :hat_type, 'http://hat', {}) }
27
+ before do
28
+ person.stub(:read_predicate).with('http://hat').and_return(['fez'])
29
+ end
30
+
31
+ it "should use the predicate name from the given field" do
32
+ person.should_receive(:read_predicate).with('http://hat').and_return(['fez'])
33
+ person.read_attribute(:hat_type, field)
34
+ end
35
+
36
+ it "should return a single value" do
37
+ person.read_attribute(:hat_type, field).should == 'fez'
38
+ end
49
39
  end
50
- end
51
40
 
52
- describe '#[]=' do
41
+ context "where field is given and is multi-valued" do
42
+ let(:field) { Person.send(:field_for, :hat_types, 'http://hat', {multivalued: true}) }
43
+ before do
44
+ person.stub(:read_predicate).with('http://hat').and_return(['fez', 'bowler'])
45
+ end
53
46
 
54
- context 'single term passed' do
55
- it 'replaces the values where the predicate matches' do
56
- person['http://name'] = 'richard'
57
- person['http://name'].should == [RDF::Literal.new('richard')]
47
+ it "should return an array of values" do
48
+ person.read_attribute(:hat_types, field).should == ['fez', 'bowler']
58
49
  end
59
50
  end
60
51
 
61
- context 'multiple terms passed' do
62
- it 'replaces the values where the predicate matches' do
63
- person['http://name'] = ['richard', 'ric', 'ricardo']
64
- person['http://name'].should == [RDF::Literal.new('richard'), RDF::Literal.new('ric'), RDF::Literal.new('ricardo')]
52
+ context "where there is no field with the given name" do
53
+ it "should raise a 'field not present' error" do
54
+ lambda { person.read_attribute(:hoof_size) }.should raise_error(Tripod::Errors::FieldNotPresent)
65
55
  end
66
56
  end
67
57
  end
68
58
 
69
- describe '#write_attribute' do
59
+ describe ".write_attribute" do
60
+ let(:person) { Person.new('http://barry') }
61
+
62
+ it "should write the given attribute" do
63
+ person[:name] = 'Barry'
64
+ person.name.should == 'Barry'
65
+ end
70
66
 
71
- context 'single term passed' do
72
- it 'replaces the values where the predicate matches' do
73
- person.write_attribute('http://name', 'richard')
74
- person['http://name'].should == [RDF::Literal.new('richard')]
67
+ it "should co-erce the value given to the correct datatype" do
68
+ person[:age] = 34
69
+ person.read_predicate('http://age').first.datatype.should == RDF::XSD.integer
70
+ end
71
+
72
+ context "where the attribute is multi-valued" do
73
+ it "should co-erce all the values to the correct datatype" do
74
+ person[:important_dates] = [Date.today]
75
+ person.read_predicate('http://importantdates').first.datatype.should == RDF::XSD.date
75
76
  end
76
77
  end
77
78
 
78
- context 'multiple terms passed' do
79
- it 'replaces the values where the predicate matches' do
80
- person.write_attribute('http://name', ['richard', 'ric', 'ricardo'])
81
- person['http://name'].should == [RDF::Literal.new('richard'), RDF::Literal.new('ric'), RDF::Literal.new('ricardo')]
79
+ context "where field is given" do
80
+ let(:field) { Person.send(:field_for, :hat_type, 'http://hat', {}) }
81
+
82
+ it "should derive the predicate name from the given field" do
83
+ person.write_attribute(:hat_type, 'http://bowlerhat', field)
84
+ person.read_predicate('http://hat').first.to_s.should == 'http://bowlerhat'
82
85
  end
83
86
  end
84
87
 
85
- end
88
+ context "where a field of a particular datatype is given" do
89
+ let(:field) { Person.send(:field_for, :hat_size, 'http://hatsize', {datatype: RDF::XSD.integer}) }
86
90
 
87
- describe '#remove_attribute' do
88
- it 'remnoves the values where the predicate matches' do
89
- person.remove_attribute('http://blog')
90
- person['http://blog'].should be_empty
91
+ it "should derive the datatype from the given field" do
92
+ person.write_attribute(:hat_size, 10, field)
93
+ person.read_predicate('http://hatsize').first.datatype.should == RDF::XSD.integer
94
+ end
91
95
  end
92
- end
93
96
 
94
- describe "append_to_attribute" do
97
+ context "where a multi-valued field of a given datatype is given" do
98
+ let(:field) { Person.send(:field_for, :hat_heights, 'http://hatheight', {datatype: RDF::XSD.integer, multivalued: true}) }
95
99
 
96
- it 'appends values to the existing values for the predicate' do
97
- person.append_to_attribute('http://name', 'rico')
98
- person['http://name'].should == [RDF::Literal.new('ric'), RDF::Literal.new('rico')]
100
+ it "should co-erce the values passed" do
101
+ person.write_attribute(:hat_heights, [5, 10, 15], field)
102
+ person.read_predicate('http://hatheight').first.datatype.should == RDF::XSD.integer
103
+ end
99
104
  end
100
105
 
106
+ context "where there is no field with the given name" do
107
+ it "should raise a 'field not present' error" do
108
+ lambda { person.write_attribute(:hoof_size, 'A1') }.should raise_error(Tripod::Errors::FieldNotPresent)
109
+ end
110
+ end
101
111
  end
102
-
103
112
  end
@@ -6,82 +6,32 @@ describe Tripod::Fields do
6
6
 
7
7
  let(:barry) do
8
8
  b = Person.new('http://barry')
9
- b['http://name'] = 'Barry'
10
- b['http://alias'] = ['Basildon', 'Baz']
11
- b['http://age'] = 54
12
- b['http://importantdates'] = [Date.new(2010,01,01), Date.new(2012,01,01)]
9
+ b.name = 'Barry'
10
+ b
13
11
  b
14
12
  end
15
13
 
16
- context "with no options" do
17
-
18
- it "creates a getter for the field, which accesses data for the predicate, returning a single String" do
19
- barry.name.should == "Barry"
20
- end
21
-
22
- it "creates a setter for the field, which sets data for the predicate" do
23
- barry.name = "Basildon"
24
- barry.name.should == "Basildon"
25
- barry['http://name'].should == [RDF::Literal.new("Basildon")]
26
- end
27
-
14
+ it "creates a getter for the field, which accesses data for the predicate, returning a single String" do
15
+ barry.name.should == "Barry"
28
16
  end
29
17
 
30
- context "with multivalued option" do
31
-
32
- it "creates a getter for the field, which accesses data for the predicate, returning an array of Strings" do
33
- barry.aliases.should == ['Basildon', 'Baz']
34
- end
35
-
36
- it "creates a setter for the field, which sets data for the predicate" do
37
- barry.aliases = ['Basildon', 'Baz', 'B-man']
38
- barry.aliases.should == ['Basildon', 'Baz', 'B-man']
39
- barry['http://alias'].should == [RDF::Literal.new("Basildon"),RDF::Literal.new("Baz"),RDF::Literal.new("B-man")]
40
- end
41
-
42
- context 'with data type' do
43
-
44
- it "creates a getter for the field, which accesses data for the predicate, returning an array of Strings" do
45
- barry.important_dates.class.should == Array
46
- barry.important_dates.should == ["2010-01-01Z", "2012-01-01Z"]
47
- end
48
-
49
- it "creates a setter for the field, which sets data for the predicate, using the right data type" do
50
- barry.important_dates = [Date.new(2010,01,02),Date.new(2010,01,03)]
51
- barry['http://importantdates'].should == [ RDF::Literal.new(Date.new(2010,01,02)), RDF::Literal.new(Date.new(2010,01,03)) ]
52
- barry['http://importantdates'].map(&:datatype).should == [RDF::XSD.date,RDF::XSD.date]
53
- end
54
-
55
- end
56
-
18
+ it "creates a setter for the field, which sets data for the predicate" do
19
+ barry.name = "Basildon"
20
+ barry.name.should == "Basildon"
57
21
  end
58
22
 
59
- context 'with data type' do
60
-
61
- it "creates a getter for the field, which accesses data for the predicate, returning a single String" do
62
- barry.age.should == "54"
63
- end
64
-
65
- it "creates a setter for the field, which sets data for the predicate, using the right data type" do
66
- barry.age = 57
67
- barry.age.should == '57'
68
- barry['http://age'] = [ RDF::Literal.new(57) ]
69
- barry['http://age'].first.datatype = RDF::XSD.integer
70
- end
71
-
23
+ it "creates a check? method, which returns true when the value is present" do
24
+ barry.name?.should == true
72
25
  end
73
26
 
74
- context 'with no data type specified' do
75
-
76
- it "creates the right kind of literals when setting values." do
77
- barry.name == 100 # set an integer
78
- barry['http://name'] = [ RDF::Literal.new(100) ]
79
- barry['http://name'].first.datatype = RDF::XSD.integer
27
+ context "when the value is not set" do
28
+ before do
29
+ barry.name = nil
80
30
  end
81
31
 
82
-
32
+ it "should have a check? method which returns false" do
33
+ barry.name?.should == false
34
+ end
83
35
  end
84
-
85
36
  end
86
-
87
37
  end