tripod 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,110 @@
1
+ # encoding: utf-8
2
+
3
+ # module for all domain objects that need to be persisted to the database
4
+ # as resources
5
+ module Tripod::Resource
6
+
7
+ extend ActiveSupport::Concern
8
+
9
+ include Tripod::Components
10
+
11
+ included do
12
+ # every resource needs a graph and a uri set.
13
+ validates_presence_of :uri, :graph_uri
14
+ end
15
+
16
+ attr_reader :new_record
17
+ attr_reader :graph_uri
18
+ attr_reader :uri
19
+
20
+ # Instantiate a +Resource+.
21
+ # Optionsally pass a uri
22
+ #
23
+ # @example Instantiate a new Resource
24
+ # Person.new('http://swirrl.com/ric.rdf#me')
25
+ #
26
+ # @param [ String, RDF::URI ] uri The uri of the resource.
27
+ #
28
+ # @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
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
44
+
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
52
+ end
53
+
54
+ # default comparison is via the uri
55
+ def <=>(other)
56
+ uri.to_s <=> uri.to_s
57
+ end
58
+
59
+ # performs equality checking on the uris
60
+ def ==(other)
61
+ self.class == other.class &&
62
+ uri.to_s == uri.to_s
63
+ end
64
+
65
+ # performs equality checking on the class
66
+ def ===(other)
67
+ other.class == Class ? self.class === other : self == other
68
+ end
69
+
70
+ # delegates to ==
71
+ def eql?()
72
+ self == (other)
73
+ end
74
+
75
+ def hash
76
+ identity.hash
77
+ end
78
+
79
+ # a resource is absolutely identified by it's class and id.
80
+ def identity
81
+ [ self.class, self.uri.to_s ]
82
+ end
83
+
84
+ # Return the key value for the resource.
85
+ #
86
+ # @example Return the key.
87
+ # resource.to_key
88
+ #
89
+ # @return [ Object ] The uri of the resource or nil if new.
90
+ def to_key
91
+ (persisted? || destroyed?) ? [ uri.to_s ] : nil
92
+ end
93
+
94
+ def to_a
95
+ [ self ]
96
+ end
97
+
98
+ module ClassMethods
99
+
100
+ # Performs class equality checking.
101
+ def ===(other)
102
+ other.class == Class ? self <= other : other.is_a?(self)
103
+ end
104
+
105
+ end
106
+
107
+ end
108
+
109
+ # causes any hooks to be fired, if they've been setup on_load of :tripod.
110
+ ActiveSupport.run_load_hooks(:triploid, Tripod::Resource)
@@ -0,0 +1,101 @@
1
+ # encoding: utf-8
2
+
3
+ # this module is responsible for connecting to an http sparql endpoint
4
+ module Tripod::SparqlClient
5
+
6
+ module Query
7
+
8
+
9
+ # Runs a +sparql+ query against the endpoint. Returns a RestClient response object.
10
+ #
11
+ # @example Run a query
12
+ # Tripload::Sparql.query('SELECT * WHERE {?s ?p ?o}')
13
+ #
14
+ # @return [ RestClient::Response ]
15
+
16
+ def self.query(sparql, format='json', headers = {})
17
+
18
+ begin
19
+ params = { :params => {:query => sparql, :output => format } }
20
+ hdrs = headers.merge(params)
21
+ RestClient::Request.execute(
22
+ :method => :get,
23
+ :url => Tripod.query_endpoint,
24
+ :headers => hdrs,
25
+ :timeout => Tripod.timeout_seconds,
26
+ )
27
+ rescue RestClient::BadRequest => e
28
+ body = e.http_body
29
+ if body.start_with?('Error 400: Parse error:')
30
+ # TODO: this is a SPARQL parsing exception. Do something different.
31
+ puts body.inspect
32
+ raise e
33
+ else
34
+ puts body.inspect
35
+ raise e
36
+ end
37
+ end
38
+ end
39
+
40
+ # Runs a SELECT +query+ against the endpoint. Returns a hash of the results.
41
+ # Specify +raw_format+ if you want the results raw, as returned from the SPARQL endpoint.
42
+ #
43
+ # @param [ String ] query The query to run
44
+ # @param [ String ] raw_format valid formats are: 'json', 'text', 'csv', 'xml'
45
+ #
46
+ # @example Run a SELECT query
47
+ #  Triploid::Sparql.select('SELECT * WHERE {?s ?p ?o}')
48
+ #
49
+ # @return [ Hash, String ]
50
+ def self.select(query, raw_format=nil)
51
+ query_response = self.query(query, (raw_format || 'json'))
52
+ if raw_format
53
+ query_response.body
54
+ else
55
+ JSON.parse(query_response.body)["results"]["bindings"]
56
+ end
57
+ end
58
+
59
+ # Executes a DESCRIBE +query+ against the SPARQL endpoint.
60
+ # Executes the +query+ and returns ntriples by default
61
+ #
62
+ # @example Run a DESCRIBE query
63
+ # Triploid::Sparql.select('DESCRIBE <http://foo>')
64
+ #
65
+ # @param [ String ] query The query to run
66
+ # @param [ String ] accept_header The header to pass to the database.
67
+
68
+ # @return [ String ] the raw response from the endpoint
69
+ def self.describe(query, accept_header='application/n-triples')
70
+ response = self.query(query, nil, {:accept=>accept_header})
71
+ return response.body
72
+ end
73
+ end
74
+
75
+ module Update
76
+
77
+ def self.update(sparql)
78
+
79
+ begin
80
+ RestClient::Request.execute(
81
+ :method => :post,
82
+ :url => Tripod.update_endpoint,
83
+ :timeout => Tripod.timeout_seconds,
84
+ :payload => {:update => sparql}
85
+ )
86
+ return true
87
+ rescue RestClient::BadRequest => e
88
+ body = e.http_body
89
+ if body.start_with?('Error 400: Parse error:')
90
+ # TODO: this is a SPARQL parsing exception. Do something different.
91
+ puts body.inspect
92
+ raise e
93
+ else
94
+ puts body.inspect
95
+ raise e
96
+ end
97
+ end
98
+ end
99
+
100
+ end
101
+ end
@@ -0,0 +1,47 @@
1
+ # encoding: utf-8
2
+
3
+ # This module contains the behaviour for getting the various states through which a
4
+ # resource can transition.
5
+ module Tripod::State
6
+
7
+ extend ActiveSupport::Concern
8
+
9
+ attr_writer :destroyed, :new_record
10
+
11
+ # Returns true if the +Resource+ has not been persisted to the database,
12
+ # false if it has. This is determined by the variable @new_record
13
+ # and NOT if the object has an id.
14
+ #
15
+ # @example Is the resource new?
16
+ # person.new_record?
17
+ #
18
+ # @return [ true, false ] True if new, false if not.
19
+ def new_record?
20
+ @new_record ||= false
21
+ end
22
+
23
+ # Checks if the resource has been saved to the database. Returns false
24
+ # if the document has been destroyed.
25
+ #
26
+ # @example Is the resource persisted?
27
+ # person.persisted?
28
+ #
29
+ # @return [ true, false ] True if persisted, false if not.
30
+ def persisted?
31
+ !new_record? && !destroyed?
32
+ end
33
+
34
+ # Returns true if the +Resource+ has been succesfully destroyed, and false
35
+ # if it hasn't. This is determined by the variable @destroyed and NOT
36
+ # by checking the database.
37
+ #
38
+ # @example Is the resource destroyed?
39
+ # person.destroyed?
40
+ #
41
+ # @return [ true, false ] True if destroyed, false if not.
42
+ def destroyed?
43
+ @destroyed ||= false
44
+ end
45
+ alias :deleted? :destroyed?
46
+
47
+ end
@@ -1,3 +1,3 @@
1
1
  module Tripod
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -0,0 +1,5 @@
1
+ class Person
2
+
3
+ include Tripod::Resource
4
+
5
+ end
@@ -0,0 +1,35 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
3
+
4
+ MODELS = File.join(File.dirname(__FILE__), "app/models")
5
+ $LOAD_PATH.unshift(MODELS)
6
+
7
+ require 'tripod'
8
+ require 'rspec'
9
+
10
+ RSpec.configure do |config|
11
+ config.mock_with :rspec
12
+
13
+ config.before(:each) do
14
+ # delete from all named graphs.
15
+ Tripod::SparqlClient::Update.update('
16
+ # delete from default graph:
17
+ DELETE {?s ?p ?o} WHERE {?s ?p ?o};
18
+ # delete from named graphs:
19
+ DELETE {graph ?g {?s ?p ?o}} WHERE {graph ?g {?s ?p ?o}};
20
+ ')
21
+ end
22
+
23
+ end
24
+
25
+ # configure any settings for testing...
26
+ Tripod.configure do |config|
27
+ config.update_endpoint = 'http://127.0.0.1:3030/tripod-test/update'
28
+ config.query_endpoint = 'http://127.0.0.1:3030/tripod-test/sparql'
29
+ end
30
+
31
+ # Autoload every model for the test suite that sits in spec/app/models.
32
+ Dir[ File.join(MODELS, "*.rb") ].sort.each do |file|
33
+ name = File.basename(file, ".rb")
34
+ autoload name.camelize.to_sym, name
35
+ end
@@ -0,0 +1,103 @@
1
+ require "spec_helper"
2
+
3
+ describe Tripod::Attributes do
4
+
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
27
+
28
+ let(:person) do
29
+ p = Person.new(@uri)
30
+ p.hydrate!(@graph)
31
+ p
32
+ end
33
+
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')
40
+ end
41
+ end
42
+
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')
49
+ end
50
+ end
51
+
52
+ describe '#[]=' do
53
+
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')]
58
+ end
59
+ end
60
+
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')]
65
+ end
66
+ end
67
+ end
68
+
69
+ describe '#write_attribute' do
70
+
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')]
75
+ end
76
+ end
77
+
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')]
82
+ end
83
+ end
84
+
85
+ end
86
+
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
+ end
92
+ end
93
+
94
+ describe "append_to_attribute" do
95
+
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')]
99
+ end
100
+
101
+ end
102
+
103
+ end
@@ -0,0 +1,71 @@
1
+ require "spec_helper"
2
+
3
+ describe Tripod::Finders do
4
+
5
+ let(:ric) do
6
+ @ric_uri = 'http://ric'
7
+ stmts = RDF::Graph.new
8
+
9
+ stmt = RDF::Statement.new
10
+ stmt.subject = RDF::URI.new(@ric_uri)
11
+ stmt.predicate = RDF::URI.new('http://name')
12
+ stmt.object = "ric"
13
+ stmts << stmt
14
+
15
+ stmt = RDF::Statement.new
16
+ stmt.subject = RDF::URI.new(@ric_uri)
17
+ stmt.predicate = RDF::URI.new('http://knows')
18
+ stmt.object = RDF::URI.new('http://bill')
19
+ stmts << stmt
20
+
21
+ r = Person.new(@ric_uri, 'http://people')
22
+ r.hydrate!(stmts)
23
+ r.save
24
+ r
25
+ end
26
+
27
+ let(:bill) do
28
+ @bill_uri = 'http://bill'
29
+ stmts = RDF::Graph.new
30
+ stmt = RDF::Statement.new
31
+ stmt.subject = RDF::URI.new(@bill_uri)
32
+ stmt.predicate = RDF::URI.new('http://name')
33
+ stmt.object = "bill"
34
+ stmts << stmt
35
+ b = Person.new(@bill_uri, 'http://people')
36
+ b.hydrate!(stmts)
37
+ b.save
38
+ b
39
+ end
40
+
41
+ describe '.find' do
42
+
43
+ context 'when record exists' do
44
+
45
+ it 'does not error' do
46
+ r = Person.find(ric.uri)
47
+ end
48
+
49
+ it 'hydrates and return an object' do
50
+ r = Person.find(ric.uri)
51
+ r['http://name'].should == [RDF::Literal.new("ric")]
52
+ r['http://knows'].should == [RDF::URI.new('http://bill')]
53
+ end
54
+
55
+ it 'sets the graph on the instantiated object' do
56
+ r = Person.find(ric.uri)
57
+ r.graph_uri.should_not be_nil
58
+ r.graph_uri.should == RDF::URI("http://people")
59
+ end
60
+
61
+ end
62
+
63
+ context 'when record does not exist' do
64
+ it 'raises not found' do
65
+ lambda { Person.find('http://nonexistant') }.should raise_error(Tripod::Errors::ResourceNotFound)
66
+ end
67
+ end
68
+
69
+ end
70
+
71
+ end