tripod 0.3.5 → 0.3.6

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -8,17 +8,13 @@ ActiveModel-style Ruby ORM for RDF Linked Data. Works with SPARQL 1.1 HTTP endpo
8
8
 
9
9
  ## Quick start, for using in a rails app.
10
10
 
11
- 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.
12
-
13
- 1. Install the gem:
14
-
15
- gem install tripod
16
-
17
- 2. Add it to your Gemfile
11
+ 1. Add it to your Gemfile and bundle
18
12
 
19
13
  gem tripod
20
14
 
21
- 3. Configure it (in application.rb, or development.rb/production.rb/test.rb)
15
+ $ bundle
16
+
17
+ 2. Configure it (in application.rb, or development.rb/production.rb/test.rb)
22
18
 
23
19
  # (values shown are the defaults)
24
20
  Tripod.configure do |config|
@@ -27,7 +23,7 @@ Note: Tripod doesn't supply a database. You need to install one. I recommend [Fu
27
23
  config.timeout_seconds = 30
28
24
  end
29
25
 
30
- 4. Include it in your model classes.
26
+ 3. Include it in your model classes.
31
27
 
32
28
  class Person
33
29
  include Tripod::Resource
@@ -45,7 +41,7 @@ Note: Tripod doesn't supply a database. You need to install one. I recommend [Fu
45
41
 
46
42
  # Note: Active Model validations are supported
47
43
 
48
- 5. Use it
44
+ 4. Use it
49
45
 
50
46
  uri = 'http://ric'
51
47
  p = Person.new(uri)
@@ -59,6 +55,10 @@ Note: Tripod doesn't supply a database. You need to install one. I recommend [Fu
59
55
 
60
56
  ric = Person.find('http://ric') #=> returns a single Person object.
61
57
 
58
+ ## Note:
59
+
60
+ 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.
61
+
62
62
 
63
63
  ## Some Other interesting features
64
64
 
@@ -18,22 +18,20 @@ module Tripod::Attributes
18
18
  # @param [ String ] name The name of the field for which to get the value.
19
19
  # @param [ Field ] field An optional Field object
20
20
  #
21
- # @return Either a string or an Array of strings, depending on whether the field is multivalued or not
21
+ # @return RDF:Term or array of them, depending on whether the field is multivalued or not
22
22
  def read_attribute(name, field=nil)
23
23
  field ||= self.fields[name]
24
24
  raise Tripod::Errors::FieldNotPresent.new unless field
25
25
 
26
26
  attr_values = read_predicate(field.predicate)
27
- # We always return strings on way out.
27
+
28
28
  # If the field is multivalued, return an array of the results
29
29
  # If it's not multivalued, return the first (should be only) result.
30
+
30
31
  if field.multivalued
31
- attr_values.map do |v|
32
- v.nil? ? nil : v.to_s
33
- end
32
+ attr_values
34
33
  else
35
- first_val = attr_values.first
36
- first_val.nil? ? nil : first_val.to_s
34
+ attr_values.first
37
35
  end
38
36
  end
39
37
  alias :[] :read_attribute
@@ -0,0 +1,35 @@
1
+ require 'dalli'
2
+
3
+ module Tripod
4
+ module CacheStores
5
+
6
+ # A Tripod::CacheStore that uses Memcached.
7
+ # Note: Make sure you set the memcached -I (slab size) to big enough to store each result,
8
+ # and set the -m (total size) to something quite big (or the cache will recycle too often).
9
+ class MemcachedCacheStore
10
+
11
+ # initialize a memcached cache store at the specified port (default 'localhost:11211')
12
+ def initialize(location)
13
+ @dalli = Dalli::Client.new(location)
14
+ end
15
+
16
+ # takes a block
17
+ def fetch(key)
18
+ raise ArgumentError.new("expected a block") unless block_given?
19
+
20
+ @dalli.fetch(key) do
21
+ yield
22
+ end
23
+ end
24
+
25
+ def write(key, data)
26
+ @dalli.set(key, data)
27
+ end
28
+
29
+ def read(key)
30
+ @dalli.get(key)
31
+ end
32
+
33
+ end
34
+ end
35
+ end
@@ -9,10 +9,13 @@ module Tripod
9
9
  # Execute the query and return a +ResourceCollection+ of all hydrated resources
10
10
  # +ResourceCollection+ is an +Enumerable+, Array-like object.
11
11
  def resources
12
- Tripod::ResourceCollection.new(resources_from_sparql(build_select_query))
12
+ Tripod::ResourceCollection.new(
13
+ resources_from_sparql(build_select_query)
14
+ )
13
15
  end
14
16
 
15
17
  # Execute the query and return the first result as a hydrated resource
18
+
16
19
  def first
17
20
  sq = Tripod::SparqlQuery.new(build_select_query)
18
21
  first_sparql = sq.as_first_query_str
@@ -26,8 +26,6 @@ module Tripod
26
26
 
27
27
  if resource_class._RDF_TYPE
28
28
  self.where("?uri a <#{resource_class._RDF_TYPE.to_s}>")
29
- else
30
- self.where("?uri ?p ?o")
31
29
  end
32
30
 
33
31
  self.graph_uri = resource_class._GRAPH_URI.to_s if resource_class._GRAPH_URI
@@ -86,6 +84,7 @@ module Tripod
86
84
  # @return [ Tripod::Criteria ] A criteria object
87
85
  def graph(graph_uri)
88
86
  self.graph_uri = graph_uri.to_s
87
+ self
89
88
  end
90
89
  end
91
90
  end
@@ -76,8 +76,7 @@ module Tripod::Finders
76
76
  # execute a query to return all objects (restricted by this class's rdf_type if specified)
77
77
  # returns a criteria object
78
78
  def all
79
- criteria = Tripod::Criteria.new(self)
80
- criteria
79
+ where('?uri ?p ?o')
81
80
  end
82
81
 
83
82
  def count
@@ -38,7 +38,7 @@ module Tripod::Resource
38
38
  @graph_uri = RDF::URI(graph_uri) if graph_uri
39
39
  @repository = RDF::Repository.new
40
40
  @new_record = true
41
- self.rdf_type = self.class._RDF_TYPE if respond_to?(:rdf_type=)
41
+ self.rdf_type = self.class._RDF_TYPE if respond_to?(:rdf_type=) && self.class._RDF_TYPE
42
42
  end
43
43
 
44
44
  # default comparison is via the uri
@@ -95,8 +95,8 @@ module Tripod::Resource
95
95
  # makes a "field" on this model called rdf_type
96
96
  # and sets a class level _RDF_TYPE variable with the rdf_type passed in.
97
97
  def rdf_type(new_rdf_type)
98
- field :rdf_type, RDF.type
99
98
  self._RDF_TYPE = RDF::URI.new(new_rdf_type.to_s)
99
+ field :rdf_type, RDF.type, :multivalued => true # things can have more than 1 type and often do
100
100
  end
101
101
 
102
102
  def graph_uri(new_graph_uri)
@@ -31,6 +31,10 @@ module Tripod
31
31
  resources[*args]
32
32
  end
33
33
 
34
+ def ==(other)
35
+ self.to_nt == other.to_nt
36
+ end
37
+
34
38
  # for n-triples we can just concatenate them
35
39
  def to_nt
36
40
  nt = ""
@@ -16,12 +16,24 @@ module Tripod::SparqlClient
16
16
  begin
17
17
  params = { :params => {:query => sparql, :output => format } }
18
18
  hdrs = headers.merge(params)
19
- RestClient::Request.execute(
20
- :method => :get,
21
- :url => Tripod.query_endpoint,
22
- :headers => hdrs,
23
- :timeout => Tripod.timeout_seconds,
24
- )
19
+
20
+ make_the_call =
21
+ -> { RestClient::Request.execute(
22
+ :method => :get,
23
+ :url => Tripod.query_endpoint,
24
+ :headers => hdrs,
25
+ :timeout => Tripod.timeout_seconds
26
+ ) }
27
+
28
+ if Tripod.cache_store # if a cache store is configured
29
+ # SHA-2 the key to keep the it within the small limit for many cache stores (e.g. Memcached is 250bytes)
30
+ # Note: SHA2's are pretty certain to be unique http://en.wikipedia.org/wiki/SHA-2.
31
+ key = 'SPARQL-QUERY-' + Digest::SHA2.hexdigest([format, headers, sparql].join(" "))
32
+ Tripod.cache_store.fetch(key, &make_the_call)
33
+ else
34
+ make_the_call.call()
35
+ end
36
+
25
37
  rescue RestClient::BadRequest => e
26
38
  # just re-raise as a BadSparqlRequest Exception
27
39
  raise Tripod::Errors::BadSparqlRequest.new(e.http_body, e)
@@ -1,3 +1,3 @@
1
1
  module Tripod
2
- VERSION = "0.3.5"
2
+ VERSION = "0.3.6"
3
3
  end
data/lib/tripod.rb CHANGED
@@ -51,12 +51,17 @@ module Tripod
51
51
  mattr_accessor :timeout_seconds
52
52
  @@timeout_seconds = 30
53
53
 
54
- # Use +configure+ to override configuration in an app, e.g.:
54
+ mattr_accessor :cache_store
55
+
56
+ # Use +configure+ to override configuration in an app, (defaults shown)
55
57
  #
56
58
  # Tripod.configure do |config|
57
59
  # config.update_endpoint = 'http://127.0.0.1:3030/tripod/update'
58
60
  # config.query_endpoint = 'http://127.0.0.1:3030/tripod/sparql'
59
- # config.timeout_seconds = 30
61
+ # config.timeout_seconds = 30#
62
+ # config.cache_store = nil #e.g Tripod::CacheStores::MemcachedCacheStore.new('localhost:11211')
63
+ # # note: if using memcached, make sure you set the -I (slab size) to big enough to store each result
64
+ # # and set the -m (total size) to something quite big (or the cache will recycle too often).
60
65
  # end
61
66
  #
62
67
  def self.configure
@@ -65,6 +70,8 @@ module Tripod
65
70
 
66
71
  end
67
72
 
73
+ require 'tripod/cache_stores/memcached_cache_store'
74
+
68
75
  require "tripod/extensions"
69
76
  require "tripod/sparql_client"
70
77
  require "tripod/sparql_query"
@@ -6,6 +6,7 @@ class Person
6
6
  graph_uri 'http://graph'
7
7
 
8
8
  field :name, 'http://name'
9
+ field :father, 'http://father'
9
10
  field :knows, 'http://knows', :multivalued => true
10
11
  field :aliases, 'http://alias', :multivalued => true
11
12
  field :age, 'http://age', :datatype => RDF::XSD.integer
@@ -2,14 +2,40 @@ require "spec_helper"
2
2
 
3
3
  describe Tripod::Attributes do
4
4
  describe ".read_attribute" do
5
+
6
+ let!(:other_person) do
7
+ p = Person.new('http://garry')
8
+ p.save!
9
+ p
10
+ end
11
+
5
12
  let(:person) do
6
13
  p = Person.new('http://barry')
7
14
  p.name = 'Barry'
15
+ p.father = other_person.uri
8
16
  p
9
17
  end
10
18
 
11
- it "should read the given attribute" do
12
- person[:name].should == 'Barry'
19
+ context "for a literal" do
20
+ it "should return an RDF::Literal" do
21
+ person[:name].class.should == RDF::Literal
22
+ end
23
+ it "should read the given attribute" do
24
+ person[:name].should == 'Barry'
25
+ end
26
+ end
27
+
28
+ context "for a uri" do
29
+
30
+ it "should return an RDF::URI" do
31
+ puts person[:father]
32
+
33
+ person[:father].class.should == RDF::URI
34
+ end
35
+
36
+ it "should read the given attribute" do
37
+ person[:father].should == other_person.uri
38
+ end
13
39
  end
14
40
 
15
41
  context "where the attribute is multi-valued" do
@@ -3,11 +3,11 @@ require "spec_helper"
3
3
  describe Tripod::Criteria do
4
4
 
5
5
  let(:person_criteria) do
6
- c = Tripod::Criteria.new(Person)
6
+ c = Person.all #Tripod::Criteria.new(Person)
7
7
  end
8
8
 
9
9
  let(:resource_criteria) do
10
- c = Tripod::Criteria.new(Resource)
10
+ c = Resource.all #Tripod::Criteria.new(Resource)
11
11
  end
12
12
 
13
13
  let!(:john) do
@@ -28,14 +28,14 @@ describe Tripod::Criteria do
28
28
 
29
29
  context "for a class with an rdf_type and graph" do
30
30
  it "should return a SELECT query based with an rdf type restriction" do
31
- person_criteria.send(:build_select_query).should == "SELECT DISTINCT ?uri (<http://graph> as ?graph) WHERE { GRAPH <http://graph> { ?uri a <http://person> } }"
31
+ person_criteria.send(:build_select_query).should == "SELECT DISTINCT ?uri (<http://graph> as ?graph) WHERE { GRAPH <http://graph> { ?uri a <http://person> . ?uri ?p ?o } }"
32
32
  end
33
33
 
34
34
  context "and extra restrictions" do
35
35
  before { person_criteria.where("[pattern]") }
36
36
 
37
37
  it "should return a SELECT query with the extra restriction" do
38
- person_criteria.send(:build_select_query).should == "SELECT DISTINCT ?uri (<http://graph> as ?graph) WHERE { GRAPH <http://graph> { ?uri a <http://person> . [pattern] } }"
38
+ person_criteria.send(:build_select_query).should == "SELECT DISTINCT ?uri (<http://graph> as ?graph) WHERE { GRAPH <http://graph> { ?uri a <http://person> . ?uri ?p ?o . [pattern] } }"
39
39
  end
40
40
  end
41
41
 
@@ -43,7 +43,7 @@ describe Tripod::Criteria do
43
43
  before { person_criteria.graph("http://anothergraph") }
44
44
 
45
45
  it "should override the graph in the query" do
46
- person_criteria.send(:build_select_query).should == "SELECT DISTINCT ?uri (<http://anothergraph> as ?graph) WHERE { GRAPH <http://anothergraph> { ?uri a <http://person> } }"
46
+ person_criteria.send(:build_select_query).should == "SELECT DISTINCT ?uri (<http://anothergraph> as ?graph) WHERE { GRAPH <http://anothergraph> { ?uri a <http://person> . ?uri ?p ?o } }"
47
47
  end
48
48
  end
49
49
  end
@@ -116,7 +116,6 @@ describe Tripod::Criteria do
116
116
 
117
117
  describe "#count" do
118
118
 
119
-
120
119
  it "should return a set of hydrated objects for the criteria" do
121
120
  person_criteria.count.should == 2
122
121
  person_criteria.where("?uri <http://name> 'John'").count.should ==1
@@ -129,7 +128,7 @@ describe Tripod::Criteria do
129
128
  end
130
129
 
131
130
  it "should execute the right Sparql" do
132
- sparql = "SELECT COUNT(*) { SELECT DISTINCT ?uri (<http://graph> as ?graph) WHERE { GRAPH <http://graph> { ?uri a <http://person> } } LIMIT 10 OFFSET 20 }"
131
+ sparql = "SELECT COUNT(*) { SELECT DISTINCT ?uri (<http://graph> as ?graph) WHERE { GRAPH <http://graph> { ?uri a <http://person> . ?uri ?p ?o } } LIMIT 10 OFFSET 20 }"
133
132
  Tripod::SparqlClient::Query.should_receive(:select).with(sparql).and_call_original
134
133
  Person.all.limit(10).offset(20).count
135
134
  end
@@ -2,9 +2,9 @@ require "spec_helper"
2
2
 
3
3
  describe Tripod::Criteria do
4
4
 
5
- let(:person_criteria) { Tripod::Criteria.new(Person) }
5
+ let(:person_criteria) { Person.all }
6
6
 
7
- let(:resource_criteria) { Tripod::Criteria.new(Resource) }
7
+ let(:resource_criteria) { Resource.all }
8
8
 
9
9
  describe "#initialize" do
10
10
 
@@ -18,7 +18,7 @@ describe Tripod::Criteria do
18
18
 
19
19
  context "with rdf_type set on the class" do
20
20
  it "should initialize the where clauses to include a type restriction" do
21
- person_criteria.where_clauses.should == ["?uri a <http://person>"]
21
+ person_criteria.where_clauses.should == ["?uri a <http://person>", "?uri ?p ?o"]
22
22
  end
23
23
  end
24
24
 
@@ -65,8 +65,8 @@ describe Tripod::Finders do
65
65
  end
66
66
 
67
67
  describe ".all" do
68
- it "should make and return a new criteria for the current class" do
69
- Person.all.should == Tripod::Criteria.new(Person)
68
+ it "should make and return a new criteria for the current class, with a where clause of ?uri ?p ?o already started" do
69
+ Person.all.should == Tripod::Criteria.new(Person).where("?uri ?p ?o")
70
70
  end
71
71
  end
72
72
 
@@ -23,7 +23,7 @@ describe Tripod::Resource do
23
23
 
24
24
  context "with rdf_type specified at class level" do
25
25
  it "sets the rdf type from the class" do
26
- person.rdf_type.to_s.should == 'http://person'
26
+ person.rdf_type.should == ['http://person']
27
27
  end
28
28
  end
29
29
 
data/tripod.gemspec CHANGED
@@ -27,4 +27,5 @@ Gem::Specification.new do |gem|
27
27
  gem.add_dependency "rdf-json"
28
28
  gem.add_dependency "json-ld"
29
29
  gem.add_dependency "guid"
30
+ gem.add_dependency "dalli", "~> 2.6"
30
31
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tripod
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.5
4
+ version: 0.3.6
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -11,11 +11,11 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2013-02-25 00:00:00.000000000 Z
14
+ date: 2013-03-13 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: rest-client
18
- requirement: &70333194336060 !ruby/object:Gem::Requirement
18
+ requirement: &70167526722900 !ruby/object:Gem::Requirement
19
19
  none: false
20
20
  requirements:
21
21
  - - ! '>='
@@ -23,10 +23,10 @@ dependencies:
23
23
  version: '0'
24
24
  type: :runtime
25
25
  prerelease: false
26
- version_requirements: *70333194336060
26
+ version_requirements: *70167526722900
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: activemodel
29
- requirement: &70333194335020 !ruby/object:Gem::Requirement
29
+ requirement: &70167526720820 !ruby/object:Gem::Requirement
30
30
  none: false
31
31
  requirements:
32
32
  - - ~>
@@ -34,10 +34,10 @@ dependencies:
34
34
  version: '3.1'
35
35
  type: :runtime
36
36
  prerelease: false
37
- version_requirements: *70333194335020
37
+ version_requirements: *70167526720820
38
38
  - !ruby/object:Gem::Dependency
39
39
  name: equivalent-xml
40
- requirement: &70333194334540 !ruby/object:Gem::Requirement
40
+ requirement: &70167518449700 !ruby/object:Gem::Requirement
41
41
  none: false
42
42
  requirements:
43
43
  - - ! '>='
@@ -45,10 +45,10 @@ dependencies:
45
45
  version: '0'
46
46
  type: :runtime
47
47
  prerelease: false
48
- version_requirements: *70333194334540
48
+ version_requirements: *70167518449700
49
49
  - !ruby/object:Gem::Dependency
50
50
  name: rdf
51
- requirement: &70333194333460 !ruby/object:Gem::Requirement
51
+ requirement: &70167518447940 !ruby/object:Gem::Requirement
52
52
  none: false
53
53
  requirements:
54
54
  - - ~>
@@ -56,10 +56,10 @@ dependencies:
56
56
  version: '1.0'
57
57
  type: :runtime
58
58
  prerelease: false
59
- version_requirements: *70333194333460
59
+ version_requirements: *70167518447940
60
60
  - !ruby/object:Gem::Dependency
61
61
  name: rdf-rdfxml
62
- requirement: &70333194332500 !ruby/object:Gem::Requirement
62
+ requirement: &70167518446320 !ruby/object:Gem::Requirement
63
63
  none: false
64
64
  requirements:
65
65
  - - ! '>='
@@ -67,10 +67,10 @@ dependencies:
67
67
  version: '0'
68
68
  type: :runtime
69
69
  prerelease: false
70
- version_requirements: *70333194332500
70
+ version_requirements: *70167518446320
71
71
  - !ruby/object:Gem::Dependency
72
72
  name: rdf-n3
73
- requirement: &70333194331640 !ruby/object:Gem::Requirement
73
+ requirement: &70167518445720 !ruby/object:Gem::Requirement
74
74
  none: false
75
75
  requirements:
76
76
  - - ! '>='
@@ -78,10 +78,10 @@ dependencies:
78
78
  version: '0'
79
79
  type: :runtime
80
80
  prerelease: false
81
- version_requirements: *70333194331640
81
+ version_requirements: *70167518445720
82
82
  - !ruby/object:Gem::Dependency
83
83
  name: rdf-json
84
- requirement: &70333194331040 !ruby/object:Gem::Requirement
84
+ requirement: &70167518444840 !ruby/object:Gem::Requirement
85
85
  none: false
86
86
  requirements:
87
87
  - - ! '>='
@@ -89,10 +89,10 @@ dependencies:
89
89
  version: '0'
90
90
  type: :runtime
91
91
  prerelease: false
92
- version_requirements: *70333194331040
92
+ version_requirements: *70167518444840
93
93
  - !ruby/object:Gem::Dependency
94
94
  name: json-ld
95
- requirement: &70333194330380 !ruby/object:Gem::Requirement
95
+ requirement: &70167518444020 !ruby/object:Gem::Requirement
96
96
  none: false
97
97
  requirements:
98
98
  - - ! '>='
@@ -100,10 +100,10 @@ dependencies:
100
100
  version: '0'
101
101
  type: :runtime
102
102
  prerelease: false
103
- version_requirements: *70333194330380
103
+ version_requirements: *70167518444020
104
104
  - !ruby/object:Gem::Dependency
105
105
  name: guid
106
- requirement: &70333194329820 !ruby/object:Gem::Requirement
106
+ requirement: &70167518443180 !ruby/object:Gem::Requirement
107
107
  none: false
108
108
  requirements:
109
109
  - - ! '>='
@@ -111,7 +111,18 @@ dependencies:
111
111
  version: '0'
112
112
  type: :runtime
113
113
  prerelease: false
114
- version_requirements: *70333194329820
114
+ version_requirements: *70167518443180
115
+ - !ruby/object:Gem::Dependency
116
+ name: dalli
117
+ requirement: &70167518442440 !ruby/object:Gem::Requirement
118
+ none: false
119
+ requirements:
120
+ - - ~>
121
+ - !ruby/object:Gem::Version
122
+ version: '2.6'
123
+ type: :runtime
124
+ prerelease: false
125
+ version_requirements: *70167518442440
115
126
  description: RDF ruby ORM
116
127
  email:
117
128
  - ric@swirrl.com
@@ -128,6 +139,7 @@ files:
128
139
  - Rakefile
129
140
  - lib/tripod.rb
130
141
  - lib/tripod/attributes.rb
142
+ - lib/tripod/cache_stores/memcached_cache_store.rb
131
143
  - lib/tripod/components.rb
132
144
  - lib/tripod/criteria.rb
133
145
  - lib/tripod/criteria/execution.rb