tripod 0.0.10 → 0.2

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -6,10 +6,12 @@ ActiveModel-style Ruby ORM for RDF Linked Data. Works with SPARQL 1.1 HTTP endpo
6
6
  * Inspired by [Durran Jordan's](https://github.com/durran) [Mongoid](http://mongoid.org/en/mongoid/) ORM for [MongoDB](http://www.mongodb.org/), and [Ben Lavender's](https://github.com/bhuga) RDF ORM, [Spira](https://github.com/ruby-rdf/spira).
7
7
  * Uses [Ruby-RDF](https://github.com/ruby-rdf/rdf) to manage the data internally.
8
8
 
9
- __Warning: Work still in progress / experimental. Not production ready!__
9
+ __Warning: Some features are still experimental.__
10
10
 
11
11
  ## Quick start, for using in a rails app.
12
12
 
13
+ Note: Tripod doesn't supply a database. You need to install one. I recommend [Fuseki](http://jena.apache.org/documentation/serving_data/index.html), which runs on port 3030 by default.
14
+
13
15
  1. Install the gem:
14
16
 
15
17
  gem install tripod
@@ -32,7 +34,11 @@ __Warning: Work still in progress / experimental. Not production ready!__
32
34
  class Person
33
35
  include Tripod::Resource
34
36
 
37
+ rdf_type 'http://person'
38
+ graph_uri 'http://people'
39
+
35
40
  field :name, 'http://name'
41
+ field :knows, 'http://knows', :multivalued => true
36
42
  field :aliases, 'http://alias', :multivalued => true
37
43
  field :age, 'http://age', :datatype => RDF::XSD.integer
38
44
  field :important_dates, 'http://importantdates', :datatype => RDF::XSD.date, :multivalued => true
@@ -43,15 +49,15 @@ __Warning: Work still in progress / experimental. Not production ready!__
43
49
  5. Use it
44
50
 
45
51
  uri = 'http://ric'
46
- graph = 'http://people'
47
- p = Person.new(uri, graph)
52
+ p = Person.new(uri)
48
53
  p.name = 'Ric'
49
54
  p.age = 31
50
55
  p.aliases = ['Rich', 'Richard']
51
56
  p.important_dates = [Date.new(2011,1,1)]
52
- p[RDF::type] = RDF::URI('http://person')
53
57
  p.save!
54
58
 
59
+ # Note: queries supplied to the where method should return the uris of the resource,
60
+ # and what graph they're in.
55
61
  people = Person.where("
56
62
  SELECT ?person ?graph
57
63
  WHERE {
@@ -60,7 +66,7 @@ __Warning: Work still in progress / experimental. Not production ready!__
60
66
  ?person a <http://person> .
61
67
  }
62
68
  }",
63
- :uri_variable => 'person' )
69
+ :uri_variable => 'person' ) # optionally, set a different name for the uri parameter (default: uri)
64
70
  # => returns an array of Person objects, containing all data we know about them.
65
71
 
66
72
  ric = Person.find('http://ric')
@@ -33,6 +33,7 @@ require 'rdf'
33
33
  require 'rdf/rdfxml'
34
34
  require 'rdf/n3'
35
35
  require 'rdf/json'
36
+ require 'json/ld'
36
37
 
37
38
  require 'rest_client'
38
39
 
@@ -64,12 +65,14 @@ end
64
65
  require "tripod/extensions"
65
66
  require "tripod/sparql_client"
66
67
 
68
+ require "tripod/predicates"
67
69
  require "tripod/attributes"
68
70
  require "tripod/errors"
69
71
  require "tripod/fields"
70
72
  require "tripod/finders"
71
73
  require "tripod/persistence"
72
74
  require "tripod/repository"
75
+ require "tripod/serialization"
73
76
  require "tripod/state"
74
77
  require "tripod/version"
75
78
 
@@ -5,77 +5,69 @@ module Tripod::Attributes
5
5
 
6
6
  extend ActiveSupport::Concern
7
7
 
8
- # Reads values from this respource's in-memory statement repository, where the predicate matches that of the uri passed in.
9
- # Returns an Array of RDF::Terms object.
8
+ # Reads an attribute from this resource, based on a defined field
9
+ # Returns the value(s) for the named (or given) field
10
10
  #
11
- # @example Read an attribute.
12
- # person.read_attribute('http://foo')
13
- # person.read_attribute(RDF::URI.new('http://foo'))
11
+ # @example Read the value associated with a predicate.
12
+ # class Person
13
+ # field :name, 'http://name'
14
+ # end
14
15
  #
15
- # @example Read an attribute (alternate syntax.)
16
- # person['http://foo']
17
- # person[RDF::URI.new('http://foo')]
16
+ # person.read_attribute(:name)
18
17
  #
19
- # @param [ String, RDF::URI ] uri The uri of the attribute to get.
18
+ # @param [ String ] name The name of the field for which to get the value.
19
+ # @param [ Field ] field An optional Field object
20
20
  #
21
- # @return [ Array ] An array of RDF::Terms.
22
- def read_attribute(predicate_uri)
23
- values = []
24
- @repository.query( [:subject, RDF::URI.new(predicate_uri.to_s), :object] ) do |statement|
25
- values << statement.object
21
+ # @return Either a string or an Array of strings, depending on whether the field is multivalued or not
22
+ def read_attribute(name, field=nil)
23
+ field ||= self.fields[name]
24
+ raise Tripod::Errors::FieldNotPresent.new unless field
25
+
26
+ attr_values = read_predicate(field.predicate)
27
+ # We always return strings on way out.
28
+ # If the field is multivalued, return an array of the results
29
+ # If it's not multivalued, return the first (should be only) result.
30
+ if field.multivalued
31
+ attr_values.map do |v|
32
+ v.nil? ? nil : v.to_s
33
+ end
34
+ else
35
+ first_val = attr_values.first
36
+ first_val.nil? ? nil : first_val.to_s
26
37
  end
27
- values
28
38
  end
29
39
  alias :[] :read_attribute
30
40
 
31
- # Replace the statement-values for a single predicate in this resource's in-memory repository.
41
+ # Writes an attribute to the resource, based on a defined field
32
42
  #
33
- # @example Write the attribute.
34
- # person.write_attribute('http://title', "Mr.")
35
- # person.write_attribute('http://title', ["Mrs.", "Ms."])
43
+ # @example Write the value associated with a predicate.
44
+ # class Person
45
+ # field :name, 'http://name'
46
+ # end
36
47
  #
37
- # @example Write the attribute (alternate syntax.)
38
- # person['http://title'] = "Mr."
39
- # person['http://title'] = ["Mrs.", "Ms."]
48
+ # person.write_attribute(:name, 'Bob')
40
49
  #
41
- # @param [ String, RDF::URI ] predicate_uri The name of the attribute to update.
42
- # @param [ Object, Array ] value The values to set for the attribute. Can be an array, or single item. They should compatible with RDF::Terms
43
- def write_attribute(predicate_uri, objects)
44
- raise Tripod::Errors::UriNotSet.new() unless @uri
45
-
46
- # remove existing
47
- remove_attribute(predicate_uri)
50
+ # @param [ String ] name The name of the field for which to set the value.
51
+ # @param [ String ] value The value to set it to
52
+ # @param [ Field ] field An optional Field object
53
+ def write_attribute(name, value, field=nil)
54
+ field ||= self.fields[name]
55
+ raise Tripod::Errors::FieldNotPresent.new unless field
48
56
 
49
- # ... and replace
50
- objects = [objects] unless objects.kind_of?(Array)
51
- objects.each do |object|
52
- @repository << RDF::Statement.new( @uri, RDF::URI.new(predicate_uri.to_s), object )
57
+ if value.kind_of?(Array)
58
+ if field.multivalued
59
+ new_val = []
60
+ value.each do |v|
61
+ new_val << self.class.new_value_for_field(v, field)
62
+ end
63
+ else
64
+ new_val = self.class.new_value_for_field(value.first, field)
65
+ end
66
+ else
67
+ new_val = self.class.new_value_for_field(value, field)
53
68
  end
54
69
 
55
- # returns the new values
56
- read_attribute(predicate_uri)
70
+ write_predicate(field.predicate, new_val)
57
71
  end
58
72
  alias :[]= :write_attribute
59
-
60
- # Append the statement-values for a single predicate in this resource's in-memory repository. Basically just adds a new statement for this ((resource's uri)+predicate)
61
- #
62
- # @example Write the attribute.
63
- # person.append_to_attribute('http://title', "Mrs.")
64
- # person.append_to_attribute('http://title', "Ms.")
65
- #
66
- # @param [ String, RDF::URI ] predicate_uri The uri of the attribute to update.
67
- # @param [ Object ] value The values to append for the attribute. Should compatible with RDF::Terms
68
- def append_to_attribute(predicate_uri, object )
69
- raise Tripod::Errors::UriNotSet.new() unless @uri
70
-
71
- @repository << RDF::Statement.new(@uri, RDF::URI.new(predicate_uri.to_s), object)
72
- end
73
-
74
- def remove_attribute(predicate_uri)
75
- @repository.query( [:subject, RDF::URI.new(predicate_uri.to_s), :object] ) do |statement|
76
- @repository.delete( statement )
77
- end
78
- end
79
- alias :delete :remove_attribute
80
-
81
73
  end
@@ -13,11 +13,13 @@ module Tripod::Components
13
13
  include ActiveModel::Naming
14
14
  include ActiveModel::Validations
15
15
 
16
+ include Tripod::Predicates
16
17
  include Tripod::Attributes
17
18
  include Tripod::Persistence
18
19
  include Tripod::Fields
19
20
  include Tripod::Finders
20
21
  include Tripod::Repository
22
+ include Tripod::Serialization
21
23
  include Tripod::State
22
24
 
23
25
  end
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ require 'tripod/errors/field_not_present'
2
3
  require 'tripod/errors/resource_not_found'
3
4
  require 'tripod/errors/uri_not_set'
4
5
  require 'tripod/errors/validations'
@@ -0,0 +1,8 @@
1
+ # encoding: utf-8
2
+ module Tripod::Errors
3
+
4
+ # field not present error.
5
+ class FieldNotPresent < StandardError
6
+ end
7
+
8
+ end
@@ -28,9 +28,8 @@ module Tripod::Fields
28
28
  #
29
29
  # @return [ Field ] The generated field
30
30
  def field(name, predicate, options = {})
31
- named = name.to_s
32
31
  # TODO: validate the field params/options here..
33
- add_field(named, predicate, options)
32
+ add_field(name, predicate, options)
34
33
  end
35
34
 
36
35
 
@@ -92,20 +91,7 @@ module Tripod::Fields
92
91
  def create_field_getter(name, meth, field)
93
92
  generated_methods.module_eval do
94
93
  re_define_method(meth) do
95
-
96
- attr_values = read_attribute(field.predicate)
97
-
98
- # We always return strings on way out.
99
- # If the field is multivalued, return an array of the results
100
- # If it's not multivalued, return the first (should be only) result.
101
- if field.multivalued
102
- attr_values.map do |v|
103
- v.nil? ? nil : v.to_s
104
- end
105
- else
106
- first_val = attr_values.first
107
- first_val.nil? ? nil : first_val.to_s
108
- end
94
+ read_attribute(name, field)
109
95
  end
110
96
  end
111
97
  end
@@ -121,20 +107,7 @@ module Tripod::Fields
121
107
  def create_field_setter(name, meth, field)
122
108
  generated_methods.module_eval do
123
109
  re_define_method("#{meth}=") do |value|
124
- if value.kind_of?(Array)
125
- if field.multivalued
126
- new_val = []
127
- value.each do |v|
128
- new_val << self.class.new_value_for_field(v, field)
129
- end
130
- else
131
- new_val = self.class.new_value_for_field(value.first, field)
132
- end
133
- else
134
- new_val = self.class.new_value_for_field(value, field)
135
- end
136
-
137
- write_attribute(field.predicate, new_val)
110
+ write_attribute(name, value, field)
138
111
  end
139
112
  end
140
113
  end
@@ -149,7 +122,7 @@ module Tripod::Fields
149
122
  def create_field_check(name, meth, field)
150
123
  generated_methods.module_eval do
151
124
  re_define_method("#{meth}?") do
152
- attr = read_attribute(field.predicate)
125
+ attr = read_attribute(name, field)
153
126
  attr == true || attr.present?
154
127
  end
155
128
  end
@@ -6,30 +6,37 @@ module Tripod::Finders
6
6
 
7
7
  module ClassMethods
8
8
 
9
- # Find a +Resource+ by its uri.
9
+ # Find a +Resource+ by its uri (and, optionally, by its graph if there are more than one).
10
10
  #
11
11
  # @example Find a single resource by a uri.
12
12
  # Person.find('http://ric')
13
13
  # Person.find(RDF::URI('http://ric'))
14
+ # @example Find a single resource by uri and graph
15
+ # Person.find('http://ric', 'http://example.com/people')
16
+ # Person.find(RDF::URI('http://ric'), Person.find(RDF::URI('http://example.com/people')))
14
17
  #
15
18
  # @param [ String, RDF::URI ] uri The uri of the resource to find
19
+ # @param [ String, RDF::URI ] graph_uri The uri of the graph from which to get the resource
16
20
  #
17
21
  # @raise [ Tripod::Errors::ResourceNotFound ] If no resource found.
18
22
  #
19
23
  # @return [ Resource ] A single resource
20
- def find(uri)
21
-
22
- # do a quick select to see what graph to use.
23
- select_query = "SELECT ?g WHERE { GRAPH ?g {<#{uri.to_s}> ?p ?o } } LIMIT 1"
24
- result = Tripod::SparqlClient::Query.select(select_query)
25
- if result.length > 0
26
- graph_uri_str = result[0]["g"]["value"]
27
- else
28
- raise Tripod::Errors::ResourceNotFound.new
24
+ def find(uri, graph_uri=nil)
25
+
26
+ unless graph_uri
27
+ # do a quick select to see what graph to use.
28
+ select_query = "SELECT ?g WHERE { GRAPH ?g {<#{uri.to_s}> ?p ?o } } LIMIT 1"
29
+ result = Tripod::SparqlClient::Query.select(select_query)
30
+ if result.length > 0
31
+ graph_uri = result[0]["g"]["value"]
32
+ else
33
+ raise Tripod::Errors::ResourceNotFound.new
34
+ end
29
35
  end
30
36
 
31
37
  # instantiate and hydrate the resource
32
- resource = self.new(uri, graph_uri_str)
38
+ resource = self.new(uri, graph_uri.to_s)
39
+
33
40
  resource.hydrate!
34
41
  resource.new_record = false
35
42
 
@@ -90,7 +97,7 @@ module Tripod::Finders
90
97
  triples_repository.query( [RDF::URI.new(u), :predicate, :object] ) do |statement|
91
98
  data_graph << statement
92
99
  end
93
- r.hydrate!(data_graph)
100
+ r.hydrate!(:graph => data_graph)
94
101
  r.new_record = false
95
102
  resources << r
96
103
  end
@@ -144,6 +144,18 @@ module Tripod::Persistence
144
144
  true
145
145
  end
146
146
 
147
+ def update_attribute(name, value)
148
+ write_attribute(name, value)
149
+ save
150
+ end
151
+
152
+ def update_attributes(attributes={})
153
+ attributes.each_pair do |name, value|
154
+ send "#{name}=", value
155
+ end
156
+ save
157
+ end
158
+
147
159
  module ClassMethods #:nodoc:
148
160
 
149
161
  # Raise an error if validation failed.
@@ -0,0 +1,78 @@
1
+ # encoding: utf-8
2
+
3
+ # This module defines behaviour for predicates.
4
+ module Tripod::Predicates
5
+
6
+ extend ActiveSupport::Concern
7
+
8
+ # returns a list of predicates (as RDF::URIs) for this resource
9
+ def predicates
10
+ pred_uris = []
11
+ @repository.statements.each do |s|
12
+ pred_uris << s.predicate unless pred_uris.include?(s.predicate)
13
+ end
14
+ pred_uris
15
+ end
16
+
17
+ # Reads values from this resource's in-memory statement repository, where the predicate matches that of the uri passed in.
18
+ # Returns an Array of RDF::Terms object.
19
+ #
20
+ # @example Read the value associated with a predicate.
21
+ # person.read_predicate('http://foo')
22
+ # person.read_predicate(RDF::URI.new('http://foo'))
23
+ #
24
+ # @param [ String, RDF::URI ] uri The uri of the predicate to get.
25
+ #
26
+ # @return [ Array ] An array of RDF::Terms.
27
+ def read_predicate(predicate_uri)
28
+ values = []
29
+ @repository.query( [:subject, RDF::URI.new(predicate_uri.to_s), :object] ) do |statement|
30
+ values << statement.object
31
+ end
32
+ values
33
+ end
34
+
35
+ # Replace the statement-values for a single predicate in this resource's in-memory repository.
36
+ #
37
+ # @example Write the predicate.
38
+ # person.write_predicate('http://title', "Mr.")
39
+ # person.write_predicate('http://title', ["Mrs.", "Ms."])
40
+ #
41
+ # @param [ String, RDF::URI ] predicate_uri The name of the attribute to update.
42
+ # @param [ Object, Array ] value The values to set for the attribute. Can be an array, or single item. They should be compatible with RDF::Terms
43
+ def write_predicate(predicate_uri, objects)
44
+ # remove existing
45
+ remove_predicate(predicate_uri)
46
+
47
+ # ... and replace
48
+ objects = [objects] unless objects.kind_of?(Array)
49
+ objects.each do |object|
50
+ @repository << RDF::Statement.new( @uri, RDF::URI.new(predicate_uri.to_s), object )
51
+ end
52
+
53
+ # returns the new values
54
+ read_predicate(predicate_uri)
55
+ end
56
+
57
+ # Append the statement-values for a single predicate in this resource's in-memory repository. Basically just adds a new statement for this ((resource's uri)+predicate)
58
+ #
59
+ # @example Write the attribute.
60
+ # person.append_to_predicate('http://title', "Mrs.")
61
+ # person.append_to_predicate('http://title', "Ms.")
62
+ #
63
+ # @param [ String, RDF::URI ] predicate_uri The uri of the attribute to update.
64
+ # @param [ Object ] value The values to append for the attribute. Should compatible with RDF::Terms
65
+ def append_to_predicate(predicate_uri, object )
66
+ raise Tripod::Errors::UriNotSet.new() unless @uri
67
+
68
+ @repository << RDF::Statement.new(@uri, RDF::URI.new(predicate_uri.to_s), object)
69
+ end
70
+
71
+ def remove_predicate(predicate_uri)
72
+ @repository.query( [:subject, RDF::URI.new(predicate_uri.to_s), :object] ) do |statement|
73
+ @repository.delete( statement )
74
+ end
75
+ end
76
+ alias :delete :remove_predicate
77
+
78
+ end