ldp 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 63183c29706de20d216c7ac0701f1c487ac198a1
4
- data.tar.gz: e08f80b008698907385b8060d4d2dcaed42ede20
3
+ metadata.gz: 85a6fa5082ce00c5b29780ec58bdc216286a5cfe
4
+ data.tar.gz: 884e4e0ea864ddf64270eff48c9ae1fca7d7d70b
5
5
  SHA512:
6
- metadata.gz: c7ff75b126b3fcf65c11c3a2a5265551f6bb9ea579c9775038a6b5a2decf64799ff8a898473c96be70232671526ffcabf278538dca1b49a98b5246c31ac4c44e
7
- data.tar.gz: ecd3068a5a86397768f428e142be9832b587f5474629fb2c0c68a3364128ea1a9b67fb15471323f0ddaba2e0c09ada6f00cc898670481f8ab6a393d914902a41
6
+ metadata.gz: 7061275707496fd834cc225c3fe6ff1ac6eaf7bd8d01ef63677b8fbd30c24c1038a16ee0646c35b59e452f2c988d2c8d908c1c1011c200812dc3b379cbcc7212
7
+ data.tar.gz: 40a58400110d941f10212a0b56bfa4cca92d0244372a4e7769a9fdda746c7444de04eefac95cf3f04863d4d31b52ab980b25ed5a2c1562b2424113951a1a4c05
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
5
+ - 2.1.0
6
+ - jruby-19mode
data/Gemfile CHANGED
@@ -3,4 +3,5 @@ source 'https://rubygems.org'
3
3
  # Specify your gem's dependencies in ldp-client.gemspec
4
4
  gemspec
5
5
 
6
- gem "byebug"
6
+ gem 'byebug', :platforms => [:mri_20]
7
+
data/README.md CHANGED
@@ -1,12 +1,14 @@
1
- # Ldp::Client
1
+ # Ldp.rb
2
2
 
3
- TODO: Write a gem description
3
+ [![Build Status](https://travis-ci.org/cbeer/ldp.png?branch=master)](https://travis-ci.org/cbeer/ldp)
4
+
5
+ Linked Data Platform client library for Ruby
4
6
 
5
7
  ## Installation
6
8
 
7
9
  Add this line to your application's Gemfile:
8
10
 
9
- gem 'ldp-client'
11
+ gem 'ldp'
10
12
 
11
13
  And then execute:
12
14
 
@@ -14,11 +16,31 @@ And then execute:
14
16
 
15
17
  Or install it yourself as:
16
18
 
17
- $ gem install ldp-client
19
+ $ gem install ldp
18
20
 
19
21
  ## Usage
20
22
 
21
- TODO: Write usage instructions here
23
+ ```ruby
24
+ host = 'http://localhost:8080'
25
+ client = Ldp::Client.new(host)
26
+ resource = Ldp::Resource.new(client, host + '/rest/node/to/update')
27
+ orm = Ldp::Orm.new(resource)
28
+
29
+ # view the current title(s)
30
+ orm.orm.value(RDF::DC11.title)
31
+
32
+ # update the title
33
+ orm.graph.delete([orm.resource.subject_uri, RDF::DC11.title, nil])
34
+ orm.graph.insert([orm.resource.subject_uri, RDF::DC11.title, 'a new title'])
35
+
36
+ # save changes
37
+ orm.save
38
+ ```
39
+
40
+ ### Fedora Commons notes
41
+ Due to some discrepancies with alpha version of Fedora Commons, you may need to do some things differently:
42
+ * [Can't load resources from Fedora 4](https://github.com/cbeer/ldp/issues/1)
43
+ * [orm.save with an rdf:type doesn't work with Fedora 4.0.0-alpha-3](https://github.com/cbeer/ldp/issues/2)
22
44
 
23
45
  ## Contributing
24
46
 
data/Rakefile CHANGED
@@ -1 +1,6 @@
1
1
  require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/ldp.gemspec CHANGED
@@ -19,7 +19,7 @@ Gem::Specification.new do |spec|
19
19
  spec.require_paths = ["lib"]
20
20
 
21
21
  spec.add_dependency "faraday"
22
- spec.add_dependency "linkeddata"
22
+ spec.add_dependency "linkeddata", ">= 1.1"
23
23
  spec.add_development_dependency "bundler", "~> 1.3"
24
24
  spec.add_development_dependency "rake"
25
25
  spec.add_development_dependency "rspec"
data/lib/ldp.rb CHANGED
@@ -1,21 +1,20 @@
1
1
  require 'ldp/version'
2
+ require 'linkeddata'
2
3
 
3
4
  module Ldp
4
- require 'ldp/client'
5
-
6
- def self.resource
7
- RDF::URI.new("http://www.w3.org/ns/ldp/Resource")
8
- end
5
+ RDF::Graph.send(:include, RDF::Isomorphic)
9
6
 
10
- def self.container
11
- RDF::URI.new("http://www.w3.org/ns/ldp/Container")
12
- end
7
+ require 'ldp/client'
8
+ require 'ldp/uri'
13
9
 
14
- def self.inlinedResource
15
- RDF::URI.new("http://www.w3.org/ns/ldp/inlinedResource")
16
- end
10
+ extend Uri
17
11
 
18
12
  autoload :Response, 'ldp/response'
19
13
  autoload :Resource, 'ldp/resource'
20
14
  autoload :Container, 'ldp/container'
21
- end
15
+
16
+ autoload :Orm, 'ldp/orm'
17
+
18
+ class NotFound < StandardError; end
19
+
20
+ end
data/lib/ldp/client.rb CHANGED
@@ -1,8 +1,11 @@
1
1
  require 'faraday'
2
- require 'linkeddata'
3
2
 
4
3
  module Ldp
5
4
  class Client
5
+
6
+ require 'ldp/client/methods'
7
+
8
+ include Ldp::Client::Methods
6
9
 
7
10
  attr_reader :http
8
11
 
@@ -14,10 +17,11 @@ module Ldp
14
17
  end
15
18
  end
16
19
 
20
+ # Find or initialize a new LDP resource by URI
17
21
  def find_or_initialize subject
18
22
  data = get(subject)
19
23
 
20
- if !data.is_a? Response
24
+ unless data.is_a? Response
21
25
  raise "#{subject} is not an LDP Resource"
22
26
  end
23
27
 
@@ -28,43 +32,5 @@ module Ldp
28
32
  end
29
33
  end
30
34
 
31
- def get url
32
- resp = http.get do |req|
33
- req.url url
34
- yield req if block_given?
35
- end
36
-
37
- if Response.resource? resp
38
- Response.wrap self, resp
39
- else
40
- resp
41
- end
42
- end
43
-
44
- def delete url
45
- http.delete do |req|
46
- req.url url
47
- yield req if block_given?
48
- end
49
- end
50
-
51
- def post url, body
52
- http.post do |req|
53
- req.url url
54
- req.headers['Content-Type'] = 'text/turtle'
55
- req.body = body
56
- yield req if block_given?
57
- end
58
- end
59
-
60
- def put url, body
61
- http.put do |req|
62
- req.url url
63
- req.headers['Content-Type'] = 'text/turtle'
64
- req.body = body
65
- yield req if block_given?
66
- end
67
- end
68
-
69
35
  end
70
36
  end
@@ -0,0 +1,44 @@
1
+ module Ldp::Client::Methods
2
+
3
+ # Get a LDP Resource by URI
4
+ def get url
5
+ resp = http.get do |req|
6
+ req.url url
7
+ yield req if block_given?
8
+ end
9
+
10
+ if Ldp::Response.resource? resp
11
+ Ldp::Response.wrap self, resp
12
+ else
13
+ resp
14
+ end
15
+ end
16
+
17
+ # Delete a LDP Resource by URI
18
+ def delete url
19
+ http.delete do |req|
20
+ req.url url
21
+ yield req if block_given?
22
+ end
23
+ end
24
+
25
+ # Post TTL to an LDP Resource
26
+ def post url, body = nil, headers = {}
27
+ http.post do |req|
28
+ req.url url
29
+ req.headers = {"Content-Type"=>"text/turtle"}.merge headers
30
+ req.body = body
31
+ yield req if block_given?
32
+ end
33
+ end
34
+
35
+ # Update an LDP resource with TTL by URI
36
+ def put url, body
37
+ http.put do |req|
38
+ req.url url
39
+ req.headers['Content-Type'] = 'text/turtle'
40
+ req.body = body
41
+ yield req if block_given?
42
+ end
43
+ end
44
+ end
data/lib/ldp/container.rb CHANGED
@@ -1,7 +1,31 @@
1
1
  module Ldp
2
2
  class Container < Resource
3
- def add graph
4
- client.post subject, graph.dump(:ttl)
3
+ ##
4
+ # Add a resource to the LDP container
5
+ def add *args
6
+ # slug, graph
7
+ # graph
8
+ # slug
9
+
10
+ case
11
+ when (args.length > 2 || args.length == 0)
12
+
13
+ when (args.length == 2)
14
+ slug, graph = args
15
+ when (args.first.is_a? RDF::Graph)
16
+ slug = nil
17
+ graph = args.first
18
+ else
19
+ slug = args.first
20
+ graph = RDF::Graph.new
21
+ end
22
+
23
+ resp = client.post subject, graph.dump(:ttl) do |req|
24
+ req.headers['Slug'] = slug
25
+ end
26
+
27
+ subject = resp.headers['Location']
28
+ return client.find_or_initialize subject
5
29
  end
6
30
  end
7
31
  end
data/lib/ldp/orm.rb ADDED
@@ -0,0 +1,87 @@
1
+ module Ldp
2
+ class Orm
3
+
4
+ attr_reader :resource
5
+ attr_reader :last_response
6
+
7
+ def initialize resource
8
+ @resource = resource
9
+ end
10
+
11
+ def graph
12
+ resource.graph
13
+ end
14
+
15
+ def value predicate
16
+ graph.query(:subject => resource.subject_uri, :predicate => predicate).map do |stmt|
17
+ stmt.object
18
+ end
19
+ end
20
+
21
+ def query *args, &block
22
+ graph.query *args, &block
23
+ end
24
+
25
+ def reload
26
+ Ldp::Orm.new resource.reload
27
+ end
28
+
29
+ def create
30
+ nil
31
+ end
32
+
33
+ def save
34
+ @last_response = resource.update
35
+
36
+ diff = Ldp::Resource.check_for_differences_and_reload_resource self
37
+
38
+ if diff.any?
39
+ diff
40
+ else
41
+ @last_response.success?
42
+ end
43
+ end
44
+
45
+ def save!
46
+ result = save
47
+
48
+ if result.is_a? RDF::Graph
49
+ raise GraphDifferenceException.new "", result
50
+ elsif !result
51
+ raise SaveException.new "", @last_response
52
+ end
53
+
54
+ result
55
+ end
56
+
57
+ def delete
58
+ resource.delete
59
+ end
60
+
61
+ def method_missing meth, *args, &block
62
+ super
63
+ end
64
+
65
+ def respond_to?(meth)
66
+ super
67
+ end
68
+
69
+ end
70
+
71
+ class GraphDifferenceException < Exception
72
+ attr_reader :diff
73
+ def initialize message, diff
74
+ super(message)
75
+ @diff = diff
76
+ end
77
+ end
78
+
79
+ class SaveException < Exception
80
+ attr_reader :response
81
+ def initialize message, response
82
+ super(message)
83
+ @response = response
84
+ end
85
+ end
86
+ end
87
+
data/lib/ldp/resource.rb CHANGED
@@ -1,11 +1,81 @@
1
1
  module Ldp
2
2
  class Resource
3
+
3
4
  attr_reader :client, :subject
4
5
 
5
- def initialize client, subject, data = nil
6
+ ##
7
+ # Create a new LDP resource with a blank RDF graph
8
+ def self.create client, subject
9
+ self.new client, subject, RDF::Graph.new
10
+ end
11
+
12
+ def initialize client, subject, graph_or_response = nil
6
13
  @client = client
7
14
  @subject = subject
8
- @get = data
15
+
16
+ @graph = graph_or_response if graph_or_response.is_a? RDF::Graph
17
+ @get = graph_or_response if graph_or_response.is_a? Ldp::Response
18
+ end
19
+
20
+ ##
21
+ # Get the graph subject as a URI
22
+ def subject_uri
23
+ @subject_uri ||= RDF::URI.new subject
24
+ end
25
+
26
+ ##
27
+ # Reload the LDP resource
28
+ def reload
29
+ Ldp::Resource.new client, subject
30
+ end
31
+
32
+ ##
33
+ # Is the resource new, or does it exist in the LDP server?
34
+ def new?
35
+ get
36
+ end
37
+
38
+ ##
39
+ # Have we retrieved the content already?
40
+ def retrieved_content?
41
+ @get
42
+ end
43
+
44
+ ##
45
+ # Get the resource
46
+ def get
47
+ @get ||= client.get(subject).tap do |result|
48
+ raise NotFound if result.status == 404
49
+ end
50
+ end
51
+
52
+ ##
53
+ # Delete the resource
54
+ def delete
55
+ client.delete subject do |req|
56
+ req.headers['If-Match'] = get.etag if retrieved_content?
57
+ end
58
+ end
59
+
60
+ ##
61
+ # Create a new resource at the URI
62
+ def create
63
+ raise "" if new?
64
+ resp = client.post '', graph.dump(:ttl) do |req|
65
+ req.headers['Slug'] = subject
66
+ end
67
+
68
+ @subject = resp.headers['Location']
69
+ @subject_uri = nil
70
+ end
71
+
72
+ ##
73
+ # Update the stored graph
74
+ def update new_graph = nil
75
+ new_graph ||= graph
76
+ client.put subject, new_graph.dump(:ttl) do |req|
77
+ req.headers['If-Match'] = get.etag if retrieved_content?
78
+ end
9
79
  end
10
80
 
11
81
  def graph
@@ -14,11 +84,11 @@ module Ldp
14
84
 
15
85
  inlinedResources = get.graph.query(:predicate => Ldp.inlinedResource).map { |x| x.object }
16
86
 
17
- unless inlinedResource.empty?
87
+ unless inlinedResources.empty?
18
88
  new_graph = RDF::Graph.new
19
89
 
20
90
  original_graph.each_statement do |s|
21
- unless inlinedResource.include? s.subject
91
+ unless inlinedResources.include? s.subject
22
92
  new_graph << s
23
93
  end
24
94
  end
@@ -30,29 +100,31 @@ module Ldp
30
100
  end
31
101
  end
32
102
 
33
- def get
34
- @get ||= client.get subject
35
- end
103
+ def self.check_for_differences_and_reload_resource old_object
104
+ new_object = old_object.reload
36
105
 
37
- def delete
38
- client.delete subject do |req|
39
- if @get
40
- req.headers['If-Match'] << get.headers['ETag']
106
+ bijection = new_object.graph.bijection_to(old_object.graph)
107
+ diff = RDF::Graph.new
108
+
109
+ old_object.graph.each do |statement|
110
+ if statement.has_blank_nodes?
111
+ subject = bijection.fetch(statement.subject, false) if statement.subject.node?
112
+ object = bijection.fetch(statement.object, false) if statement.object.node?
113
+ bijection_statement = RDF::Statement.new :subject => subject || statemnet.subject, :predicate => statement.predicate, :object => object || statement.object
114
+
115
+ diff << statement if subject === false or object === false or new_object.graph.has_statement?(bijection_statement)
116
+ elsif !new_object.graph.has_statement? statement
117
+ diff << statement
41
118
  end
42
119
  end
43
120
 
44
- @get = nil
45
- @graph = nil
121
+ diff
46
122
  end
47
123
 
48
- def update
49
- client.put subject, graph.dump(:ttl) do |req|
50
- if @get
51
- req.headers['If-Match'] << get.headers['ETag']
52
- end
53
- end
54
- @get = nil
55
- @graph = nil
124
+ ##
125
+ # Reload this resource as an LDP container
126
+ def as_container
127
+ Ldp::Container.new client, subject, @graph || @get
56
128
  end
57
129
  end
58
- end
130
+ end
data/lib/ldp/response.rb CHANGED
@@ -1,17 +1,22 @@
1
1
  module Ldp
2
2
  module Response
3
+
4
+ ##
5
+ # Wrap the raw Faraday respone with our LDP extensions
3
6
  def self.wrap client, raw_resp
4
7
  raw_resp.send(:extend, Ldp::Response)
5
8
  raw_resp.ldp_client = client
6
9
  raw_resp
7
10
  end
8
11
 
12
+ ##
13
+ # Extract the Link: headers from the HTTP resource
9
14
  def self.links raw_resp
10
- Array(raw_resp.headers["Link"]).inject({}) do |memo, header|
15
+ h = Hash.new { |hash, key| hash[key] = [] }
16
+ Array(raw_resp.headers["Link"]).map { |x| x.split(", ") }.flatten.inject(h) do |memo, header|
11
17
  v = header.scan(/(.*);\s?rel="([^"]+)"/)
12
18
 
13
19
  if v.length == 1
14
- memo[v.first.last] ||= []
15
20
  memo[v.first.last] << v.first.first
16
21
  end
17
22
 
@@ -19,25 +24,63 @@ module Ldp
19
24
  end
20
25
  end
21
26
 
27
+ ##
28
+ # Is the response an LDP resource?
22
29
  def self.resource? raw_resp
23
- links(raw_resp).fetch("type", []).include? Ldp.resource
30
+ links(raw_resp).fetch("type", []).include? Ldp.resource.to_s
31
+ end
32
+
33
+ ##
34
+ # Is the response an LDP resource?
35
+ def resource?
36
+ Ldp::Response.resource?(self)
37
+ end
38
+
39
+ ##
40
+ # Is the response an LDP container
41
+ def container?
42
+ graph.has_statement? RDF::Statement.new(subject, RDF.type, Ldp.container)
24
43
  end
25
44
 
45
+ ##
46
+ # Get the subject for the response
47
+ def subject
48
+ @subject ||= if has_page?
49
+ graph.first_object [page_subject, Ldp.page_of, nil]
50
+ else
51
+ page_subject
52
+ end
53
+ end
54
+
55
+ ##
56
+ # Get the URI to the response
57
+ def page_subject
58
+ @page_subject ||= RDF::URI.new env[:url]
59
+ end
60
+
61
+ ##
62
+ # Set the LDP client for this resource
26
63
  def ldp_client= client
27
64
  @ldp_client = client
28
65
  end
29
66
 
67
+ ##
68
+ # Get the LDP client
30
69
  def ldp_client
31
70
  @ldp_client
32
71
  end
33
72
 
73
+ ##
74
+ # Get the graph for the resource (or a blank graph if there is no metadata for the resource)
34
75
  def graph
35
76
  @graph ||= begin
36
77
  graph = RDF::Graph.new
37
78
 
38
- RDF::Reader.for(:ttl).new(StringIO.new(body), :base_uri => subject) do |reader|
39
- reader.each_statement do |s|
40
- graph << s
79
+ if resource?
80
+ RDF::Reader.for(:ttl).new(StringIO.new(body), :base_uri => page_subject) do |reader|
81
+ reader.each_statement do |s|
82
+ graph << s
83
+ end
41
84
  end
42
85
  end
43
86
 
@@ -45,52 +88,90 @@ module Ldp
45
88
  end
46
89
  end
47
90
 
91
+ ##
92
+ # Extract the ETag for the resource
48
93
  def etag
49
-
94
+ headers['ETag']
50
95
  end
51
96
 
97
+ ##
98
+ # Extract the last modified header for the resource
52
99
  def last_modified
53
-
100
+ headers['Last-Modified']
54
101
  end
55
102
 
103
+ ##
104
+ # Statements about the page
56
105
  def page
106
+ @page_graph ||= begin
107
+ g = RDF::Graph.new
108
+
109
+ if resource?
110
+ res = graph.query RDF::Statement.new(page_subject, nil, nil)
57
111
 
112
+ res.each_statement do |s|
113
+ g << s
114
+ end
115
+ end
116
+
117
+ g
118
+ end
58
119
  end
59
120
 
60
- def paginated?
121
+ ##
122
+ # Is the response paginated?
123
+ def has_page?
124
+ graph.has_statement? RDF::Statement.new(page_subject, RDF.type, Ldp.page)
125
+ end
61
126
 
127
+ ##
128
+ # Is there a next page?
129
+ def has_next?
130
+ next_page != nil
62
131
  end
63
132
 
133
+ ##
134
+ # Get the URI for the next page
64
135
  def next_page
65
-
136
+ graph.first_object [page_subject, Ldp.nextPage, nil]
66
137
  end
67
138
 
139
+ ##
140
+ # Get the URI to the first page
68
141
  def first_page
69
-
142
+ if links['first']
143
+ RDF::URI.new links['first']
144
+ elsif graph.has_statement? RDf::Statement.new(page_subject, Ldp.nextPage, nil)
145
+ subject
146
+ end
70
147
  end
71
148
 
149
+ ##
150
+ # Get a list of inlined resources
72
151
  def resources
73
-
152
+ graph.query RDF::Statement.new(page_subject, Ldp.inlinedResource, nil)
74
153
  end
75
154
 
155
+ ##
156
+ # Get a list of member resources
76
157
  def members
77
-
78
- end
79
-
80
- def resource?
81
- Ldp::Response.resource?(self)
158
+ graph.query RDF::Statement.new(page_subject, membership_predicate, nil)
82
159
  end
83
160
 
84
- def container?
85
- graph.has_statement? RDF::Statement.new(subject, RDF.type, Ldp.container)
161
+ ##
162
+ # Predicate to use to determine container membership
163
+ def membership_predicate
164
+ graph.first_object [page_subject, Ldp.membership_predicate, nil]
86
165
  end
87
166
 
88
167
  def sort
89
168
 
90
169
  end
91
170
 
92
- def subject
93
- RDF::URI.new env[:url]
171
+ ##
172
+ # Link: headers from the HTTP response
173
+ def links
174
+ Ldp::Response.links(self)
94
175
  end
95
176
  end
96
177
  end
data/lib/ldp/uri.rb ADDED
@@ -0,0 +1,35 @@
1
+ module Ldp::Uri
2
+
3
+ def uri str
4
+ RDF::URI.new("http://www.w3.org/ns/ldp#") + str
5
+ end
6
+
7
+ def resource
8
+ uri("Resource")
9
+ end
10
+
11
+ def container
12
+ uri("Container")
13
+ end
14
+
15
+ def page
16
+ uri("Page")
17
+ end
18
+
19
+ def page_of
20
+ uri("pageOf")
21
+ end
22
+
23
+ def next_page
24
+ uri("nextPage")
25
+ end
26
+
27
+ def inlinedResource
28
+ uri("inlinedResource")
29
+ end
30
+
31
+ def membership_predicate
32
+ uri("membershipPredicate")
33
+ end
34
+
35
+ end
data/lib/ldp/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Ldp
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -0,0 +1,5 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Integration tests" do
4
+
5
+ end
@@ -6,6 +6,14 @@ describe "Ldp::Client" do
6
6
  graph.dump(:ttl)
7
7
  end
8
8
 
9
+
10
+ let(:paginatedGraph) do
11
+ graph = RDF::Graph.new << [RDF::URI.new(""), RDF::DC.title, "Hello, world!"]
12
+ graph << [RDF::URI.new("?firstPage"), RDF.type, Ldp.page]
13
+ graph << [RDF::URI.new("?firstPage"), Ldp.page_of, RDF::URI.new("")]
14
+ graph.dump(:ttl)
15
+ end
16
+
9
17
  let(:simple_container_graph) do
10
18
  graph = RDF::Graph.new << [RDF::URI.new(""), RDF.type, Ldp.container]
11
19
  graph.dump(:ttl)
@@ -13,9 +21,11 @@ describe "Ldp::Client" do
13
21
 
14
22
  let(:conn_stubs) do
15
23
  stubs = Faraday::Adapter::Test::Stubs.new do |stub|
16
- stub.get('/a_resource') {[ 200, {"Link" => "http://www.w3.org/ns/ldp/Resource;rel=\"type\""}, simple_graph ]}
17
- stub.get('/a_container') {[ 200, {"Link" => "http://www.w3.org/ns/ldp/Resource;rel=\"type\""}, simple_container_graph ]}
18
-
24
+ stub.get('/a_resource') {[ 200, {"Link" => "http://www.w3.org/ns/ldp#Resource;rel=\"type\""}, simple_graph ]}
25
+ stub.get('/a_container') {[ 200, {"Link" => "http://www.w3.org/ns/ldp#Resource;rel=\"type\""}, simple_container_graph ]}
26
+ stub.put("/a_resource") { [204]}
27
+ stub.delete("/a_resource") { [204]}
28
+ stub.post("/a_container") { [201, {"Location" => "http://example.com/a_container/subresource"}]}
19
29
  end
20
30
  end
21
31
 
@@ -53,11 +63,69 @@ describe "Ldp::Client" do
53
63
  expect(resp.resource?).to be_true
54
64
  end
55
65
 
56
- describe "response" do
66
+ it "should accept a block to change the HTTP request" do
67
+ expect { |b| subject.get "a_resource", &b }.to yield_control
68
+ end
69
+ end
70
+
71
+ describe "delete" do
72
+ it "should DELETE the subject from the HTTP endpoint" do
73
+ resp = subject.delete "a_resource"
74
+ expect(resp.status).to eq(204)
75
+ end
76
+
77
+ it "should accept a block to change the HTTP request" do
78
+ expect { |b| subject.delete "a_resource", &b }.to yield_control
79
+ end
80
+ end
81
+
82
+ describe "post" do
83
+ it "should POST to the subject at the HTTP endpoint" do
84
+ resp = subject.post "a_container"
85
+ expect(resp.status).to eq(201)
86
+ expect(resp.headers[:Location]).to eq("http://example.com/a_container/subresource")
87
+ end
88
+
89
+ it "should set content" do
90
+ subject.post "a_container", 'foo' do |req|
91
+ expect(req.body).to eq 'foo'
92
+ end
93
+ end
94
+
95
+ it "should set default Content-type" do
96
+ subject.post "a_container", 'foo' do |req|
97
+ expect(req.headers).to eq({ "Content-Type" => "text/turtle" })
98
+ end
99
+ end
100
+
101
+ it "should set headers" do
102
+ subject.post "a_container", 'foo', {'Content-Type' => 'application/pdf'} do |req|
103
+ expect(req.headers).to eq({ "Content-Type" => "application/pdf" })
104
+ end
105
+ end
106
+
107
+ it "should set headers passed as arguments" do
108
+ resp = subject.post "a_container"
109
+ end
110
+
111
+ it "should accept a block to change the HTTP request" do
112
+ expect { |b| subject.post "a_container", &b }.to yield_control
113
+ end
114
+
115
+ end
116
+
117
+ describe "put" do
118
+ it "should PUT content to the subject at the HTTP endpoint" do
119
+ resp = subject.put "a_resource", "some-payload"
120
+ expect(resp.status).to eq(204)
121
+ end
57
122
 
123
+ it "should accept a block to change the HTTP request" do
124
+ expect { |b| subject.put "a_resource", "some-payload", &b }.to yield_control
58
125
  end
59
126
  end
60
127
 
128
+
61
129
  describe "find_or_initialize" do
62
130
  it "should be a resource" do
63
131
  resource = subject.find_or_initialize "a_resource"
@@ -71,4 +139,4 @@ describe "Ldp::Client" do
71
139
  end
72
140
 
73
141
  end
74
- end
142
+ end
@@ -0,0 +1,102 @@
1
+ require 'spec_helper'
2
+
3
+ describe Ldp::Orm do
4
+ subject { Ldp::Orm.new test_resource }
5
+
6
+ let(:simple_graph) do
7
+ graph = RDF::Graph.new << [RDF::URI.new(""), RDF::DC.title, "Hello, world!"]
8
+ graph.dump(:ttl)
9
+ end
10
+
11
+ let(:conn_stubs) do
12
+ stubs = Faraday::Adapter::Test::Stubs.new do |stub|
13
+ stub.get('/a_resource') {[ 200, {"Link" => "http://www.w3.org/ns/ldp#Resource;rel=\"type\""}, simple_graph ]}
14
+ stub.put("/a_resource") { [204]}
15
+ end
16
+ end
17
+
18
+ let(:mock_conn) do
19
+ test = Faraday.new do |builder|
20
+ builder.adapter :test, conn_stubs do |stub|
21
+ end
22
+ end
23
+ end
24
+
25
+ let :mock_client do
26
+ Ldp::Client.new mock_conn
27
+ end
28
+
29
+ let :test_resource do
30
+ Ldp::Resource.new mock_client, "http://example.com/a_resource"
31
+ end
32
+
33
+ describe "#delete" do
34
+ it "should delete the LDP resource" do
35
+ test_resource.should_receive(:delete)
36
+ subject.delete
37
+ end
38
+ end
39
+
40
+ describe "#create" do
41
+
42
+ end
43
+
44
+ describe "#save" do
45
+ it "should update the resource from the graph" do
46
+ expect(subject.save).to be_true
47
+ end
48
+
49
+ it "should provide a graph of differences if the post-save graph doesn't match our graph" do
50
+ subject.graph << RDF::Statement.new(:subject => subject.resource.subject_uri, :predicate => RDF::URI.new("info:some-predicate"), :object => RDF::Literal.new("xyz"))
51
+ result = subject.save
52
+ expect(result).to_not be_empty
53
+ end
54
+
55
+ it "should return false if the response was not successful" do
56
+ conn_stubs.instance_variable_get(:@stack)[:put] = [] # erases the stubs for :put
57
+ conn_stubs.put('/a_resource') {[412]}
58
+ expect(subject.save).to be_false
59
+ end
60
+ end
61
+
62
+ describe "#save!" do
63
+ it "should raise an exception if there are differences after saving the graph" do
64
+ subject.graph << RDF::Statement.new(:subject => subject.resource.subject_uri, :predicate => RDF::URI.new("info:some-predicate"), :object => RDF::Literal.new("xyz"))
65
+ expect { subject.save! }.to raise_exception(Ldp::GraphDifferenceException)
66
+ end
67
+
68
+ it "should raise an exception if the ETag didn't match" do
69
+ conn_stubs.instance_variable_get(:@stack)[:put] = [] # erases the stubs for :put
70
+ conn_stubs.put('/a_resource') {[412, {}, "Bad If-Match header value: 'ae43aa934dc4f4e15ea1b4dd1ca7a56791972836'"]}
71
+ expect { subject.save! }.to raise_exception(Ldp::SaveException)
72
+ end
73
+ end
74
+
75
+ describe "#value" do
76
+ it "should provide a convenience method for retrieving values" do
77
+ expect(subject.value(RDF::DC.title).first.to_s).to eq "Hello, world!"
78
+ end
79
+ end
80
+
81
+ describe "#reload" do
82
+ before do
83
+ updated_graph = RDF::Graph.new << [RDF::URI.new(""), RDF::DC.title, "Hello again, world!"]
84
+ conn_stubs.get('/a_resource') {[200,
85
+ {"Link" => "http://www.w3.org/ns/ldp#Resource;rel=\"type\"",
86
+ "ETag" => "new-tag"},
87
+ updated_graph.dump(:ttl)]}
88
+ end
89
+
90
+ it "loads the new values" do
91
+ old_value = subject.value(RDF::DC.title).first.to_s
92
+ reloaded = subject.reload
93
+ expect(reloaded.value(RDF::DC.title).first.to_s).not_to eq old_value
94
+ end
95
+
96
+ it "uses the new ETag" do
97
+ old_tag = subject.resource.get.etag
98
+ reloaded = subject.reload
99
+ expect(reloaded.resource.get.etag).not_to eq old_tag
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,40 @@
1
+ require 'spec_helper'
2
+
3
+ describe Ldp::Resource do
4
+ subject { Ldp::Resource.new(mock_client, path) }
5
+
6
+ let(:conn_stubs) do
7
+ stubs = Faraday::Adapter::Test::Stubs.new do |stub|
8
+ stub.get('/not_found_resource') { [404] }
9
+ stub.get('/a_resource') { [200] }
10
+ end
11
+ end
12
+
13
+ let(:mock_conn) do
14
+ test = Faraday.new do |builder|
15
+ builder.adapter :test, conn_stubs do |stub|
16
+ end
17
+ end
18
+ end
19
+
20
+ let :mock_client do
21
+ Ldp::Client.new mock_conn
22
+ end
23
+
24
+ describe "#get" do
25
+ context "when the resource is not in repository" do
26
+ let(:path) { '/not_found_resource' }
27
+ it "should raise an error" do
28
+ expect{ subject.get }.to raise_error Ldp::NotFound
29
+ end
30
+ end
31
+
32
+ context "when the resource is in the repository" do
33
+ let(:path) { '/a_resource' }
34
+ it "should get the response" do
35
+ expect(subject.get).to be_kind_of Faraday::Response
36
+ expect(subject.get.status).to eq 200
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,128 @@
1
+ require 'spec_helper'
2
+
3
+ describe Ldp::Response do
4
+ LDP_RESOURCE_HEADERS = { "Link" => Ldp.resource.to_s + ";rel=\"type\""}
5
+
6
+ let(:mock_response) { double() }
7
+ let(:mock_client) { double(Ldp::Client) }
8
+
9
+ subject do
10
+ Ldp::Response.wrap mock_client, mock_response
11
+ end
12
+
13
+ describe ".wrap" do
14
+ it "should mixin Ldp::Response into the raw response" do
15
+ Ldp::Response.wrap(mock_client, mock_response)
16
+ expect(mock_response).to be_a_kind_of(Ldp::Response)
17
+ expect(mock_response.ldp_client).to eq(mock_client)
18
+ end
19
+ end
20
+
21
+ describe ".links" do
22
+ it "should extract link headers with relations as a hash" do
23
+ mock_response.stub(:headers => {
24
+ "Link" => [
25
+ "xyz;rel=\"some-rel\"",
26
+ "abc;rel=\"some-multi-rel\"",
27
+ "123;rel=\"some-multi-rel\"",
28
+ "vanilla-link"
29
+ ]
30
+ })
31
+ h = Ldp::Response.links mock_response
32
+
33
+ expect(h['some-rel']).to include("xyz")
34
+ expect(h['some-multi-rel']).to include("abc", "123")
35
+ expect(h['doesnt-exist']).to be_empty
36
+ end
37
+
38
+ it "should return an empty hash if no link headers are availabe" do
39
+ mock_response.stub(:headers => {})
40
+ h = Ldp::Response.links mock_response
41
+
42
+ expect(h).to be_empty
43
+ end
44
+
45
+ end
46
+
47
+ describe ".resource?" do
48
+ it "should be a resource if a Link[rel=type] header asserts it is an ldp:resource" do
49
+ mock_response.stub(:headers => {
50
+ "Link" => [
51
+ "#{Ldp.resource};rel=\"type\""
52
+ ]
53
+ })
54
+ expect(Ldp::Response.resource? mock_response).to be_true
55
+ end
56
+ end
57
+
58
+ describe "#graph" do
59
+ it "should parse the response body for an RDF graph" do
60
+ mock_response.stub :body => "<> <info:b> <info:c> .", :headers => LDP_RESOURCE_HEADERS
61
+ subject.stub :page_subject => RDF::URI.new('info:a')
62
+ graph = subject.graph
63
+
64
+ expect(graph).to have_subject(RDF::URI.new("info:a"))
65
+ expect(graph).to have_statement RDF::Statement.new(RDF::URI.new("info:a"), RDF::URI.new("info:b"), RDF::URI.new("info:c"))
66
+
67
+ end
68
+ end
69
+
70
+ describe "#etag" do
71
+ it "should pass through the response's ETag" do
72
+ mock_response.stub :headers => { 'ETag' => 'xyz'}
73
+
74
+ expect(subject.etag).to eq('xyz')
75
+ end
76
+ end
77
+
78
+ describe "#last_modified" do
79
+ it "should pass through the response's Last-Modified" do
80
+ mock_response.stub :headers => { 'Last-Modified' => 'some-date'}
81
+ expect(subject.last_modified).to eq('some-date')
82
+ end
83
+ end
84
+
85
+ describe "#has_page" do
86
+ it "should see if the response has an ldp:Page statement" do
87
+ graph = RDF::Graph.new
88
+
89
+ subject.stub :page_subject => RDF::URI.new('info:a')
90
+
91
+ graph << [RDF::URI.new('info:a'), RDF.type, Ldp.page]
92
+
93
+ mock_response.stub :body => graph.dump(:ttl), :headers => LDP_RESOURCE_HEADERS
94
+
95
+ expect(subject).to have_page
96
+ end
97
+
98
+ it "should be false otherwise" do
99
+ subject.stub :page_subject => RDF::URI.new('info:a')
100
+ mock_response.stub :body => '', :headers => LDP_RESOURCE_HEADERS
101
+ expect(subject).not_to have_page
102
+ end
103
+ end
104
+
105
+ describe "#page" do
106
+ it "should get the ldp:Page data from the query" do
107
+ graph = RDF::Graph.new
108
+
109
+ subject.stub :page_subject => RDF::URI.new('info:a')
110
+
111
+ graph << [RDF::URI.new('info:a'), RDF.type, Ldp.page]
112
+ graph << [RDF::URI.new('info:b'), RDF.type, Ldp.page]
113
+
114
+ mock_response.stub :body => graph.dump(:ttl), :headers => LDP_RESOURCE_HEADERS
115
+
116
+ expect(subject.page.count).to eq(1)
117
+
118
+ end
119
+ end
120
+
121
+ describe "#subject" do
122
+ it "should extract the HTTP request URI as an RDF URI" do
123
+ mock_response.stub :body => '', :headers => LDP_RESOURCE_HEADERS
124
+ mock_response.stub :env => { :url => 'http://xyz/a'}
125
+ expect(subject.subject).to eq(RDF::URI.new("http://xyz/a"))
126
+ end
127
+ end
128
+ end
data/spec/spec_helper.rb CHANGED
@@ -5,7 +5,6 @@ $LOAD_PATH.unshift(File.dirname(__FILE__))
5
5
  require 'rspec/autorun'
6
6
  require 'ldp'
7
7
  require 'faraday'
8
- require "byebug"
9
8
 
10
9
  RSpec.configure do |config|
11
10
 
metadata CHANGED
@@ -1,83 +1,83 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ldp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Beer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-07-31 00:00:00.000000000 Z
11
+ date: 2014-03-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - '>='
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: '0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - '>='
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: linkeddata
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - '>='
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: '0'
33
+ version: '1.1'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - '>='
38
+ - - ">="
39
39
  - !ruby/object:Gem::Version
40
- version: '0'
40
+ version: '1.1'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: bundler
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - ~>
45
+ - - "~>"
46
46
  - !ruby/object:Gem::Version
47
47
  version: '1.3'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - ~>
52
+ - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '1.3'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: rake
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - '>='
59
+ - - ">="
60
60
  - !ruby/object:Gem::Version
61
61
  version: '0'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - '>='
66
+ - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rspec
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - '>='
73
+ - - ">="
74
74
  - !ruby/object:Gem::Version
75
75
  version: '0'
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - '>='
80
+ - - ">="
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
83
  description: Linked Data Platform client library
@@ -87,7 +87,8 @@ executables: []
87
87
  extensions: []
88
88
  extra_rdoc_files: []
89
89
  files:
90
- - .gitignore
90
+ - ".gitignore"
91
+ - ".travis.yml"
91
92
  - Gemfile
92
93
  - LICENSE.txt
93
94
  - README.md
@@ -95,11 +96,18 @@ files:
95
96
  - ldp.gemspec
96
97
  - lib/ldp.rb
97
98
  - lib/ldp/client.rb
99
+ - lib/ldp/client/methods.rb
98
100
  - lib/ldp/container.rb
101
+ - lib/ldp/orm.rb
99
102
  - lib/ldp/resource.rb
100
103
  - lib/ldp/response.rb
104
+ - lib/ldp/uri.rb
101
105
  - lib/ldp/version.rb
106
+ - spec/lib/integration/integration_spec.rb
102
107
  - spec/lib/ldp/client_spec.rb
108
+ - spec/lib/ldp/orm/orm_spec.rb
109
+ - spec/lib/ldp/resource_spec.rb
110
+ - spec/lib/ldp/response_spec.rb
103
111
  - spec/spec_helper.rb
104
112
  homepage: ''
105
113
  licenses:
@@ -111,20 +119,25 @@ require_paths:
111
119
  - lib
112
120
  required_ruby_version: !ruby/object:Gem::Requirement
113
121
  requirements:
114
- - - '>='
122
+ - - ">="
115
123
  - !ruby/object:Gem::Version
116
124
  version: '0'
117
125
  required_rubygems_version: !ruby/object:Gem::Requirement
118
126
  requirements:
119
- - - '>='
127
+ - - ">="
120
128
  - !ruby/object:Gem::Version
121
129
  version: '0'
122
130
  requirements: []
123
131
  rubyforge_project:
124
- rubygems_version: 2.0.3
132
+ rubygems_version: 2.2.2
125
133
  signing_key:
126
134
  specification_version: 4
127
135
  summary: Linked Data Platform client library
128
136
  test_files:
137
+ - spec/lib/integration/integration_spec.rb
129
138
  - spec/lib/ldp/client_spec.rb
139
+ - spec/lib/ldp/orm/orm_spec.rb
140
+ - spec/lib/ldp/resource_spec.rb
141
+ - spec/lib/ldp/response_spec.rb
130
142
  - spec/spec_helper.rb
143
+ has_rdoc: