ldp 0.0.3 → 0.0.4

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8ea0daaba00e2614ee8dc9597db049fb7081e3ae
4
- data.tar.gz: e15f930db252d3dbe6fc03a638f29d703ea53da1
3
+ metadata.gz: b564742703ded9a1ea57b61c4d35095152470b0d
4
+ data.tar.gz: 92c8f28addad7872849b36acfdeac29bb2ebf280
5
5
  SHA512:
6
- metadata.gz: 976bd681a8322b5c1a1780b27caf56dd5e65cc09420fea41a2db234fec392c4d63c4ad3eb1f2272c87b9b49927af2bfd98931f60ec1bd623d9b970183a6db2b4
7
- data.tar.gz: 9de1c9c88954f66e2856cbf6c10a6624396667bb1f5efda080c983d40e382ef855ab42e39372c65c45417221ee1d0d069a52d2444f05e4545d107f10d172503d
6
+ metadata.gz: c47ff6ed7653614374db095e93defbfa21138b072862de841464c06b8c0494707ee5d39610d06bdb640d8b4214590aae81de5f264ccbb61349afe2ffffaeb594
7
+ data.tar.gz: 2ad220371ea1d981c8ab3d29aca7f615e0bf573d56da2c56de84ba0d15a23a1173638eba23e97ef38d09eab26a7cef398037113453b017a8ffe5b01c138b25aa
data/Gemfile CHANGED
@@ -4,4 +4,4 @@ source 'https://rubygems.org'
4
4
  gemspec
5
5
 
6
6
  gem 'byebug', :platforms => [:mri_20]
7
-
7
+ gem 'activesupport'
data/bin/ldp ADDED
@@ -0,0 +1,127 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ lib = File.expand_path('../../lib', __FILE__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+
6
+ require 'ldp'
7
+ require 'slop'
8
+
9
+ HttpLogger.logger = Logger.new(STDERR)
10
+ HttpLogger.log_headers = true
11
+
12
+ def with_error_handling
13
+ yield
14
+ rescue Ldp::NotFound
15
+ exit 1
16
+ end
17
+
18
+ do_help = lambda do |x|
19
+
20
+ x.command 'help' do
21
+ run do |opts, args|
22
+ puts opts.help
23
+ end
24
+ end
25
+
26
+ end
27
+
28
+ Slop.parse do
29
+ banner "Usage: ldp [command] --host [HOST] [options]"
30
+
31
+ on '-v', 'Print the version' do
32
+ puts "ldp #{Ldp::VERSION}"
33
+ end
34
+
35
+ on :host=, "Host"
36
+
37
+ do_help.call(self)
38
+
39
+ command 'get' do
40
+ banner "Usage: ldp get --host [HOST] [PATH]"
41
+
42
+ on :host=, "Host"
43
+
44
+ do_help.call(self)
45
+
46
+ run do |opts, (path)|
47
+ with_error_handling do
48
+ host = opts[:host] || ENV['LDP_HOST']
49
+ puts Ldp::Client.new(url: host).find_or_initialize(path).get.body
50
+ end
51
+ end
52
+ end
53
+
54
+ command 'delete' do
55
+ banner "Usage: ldp delete --host [HOST] [PATH]"
56
+
57
+ on :host=, "Host"
58
+ do_help.call(self)
59
+
60
+ run do |opts, (path)|
61
+ with_error_handling do
62
+ host = opts[:host] || ENV['LDP_HOST']
63
+ Ldp::Client.new(url: host).delete path
64
+ end
65
+ end
66
+ end
67
+
68
+ command 'create' do
69
+ banner "Usage: ldp create --host [HOST] [PATH] [SOURCE]"
70
+
71
+ on :host=, "Host"
72
+ do_help.call(self)
73
+
74
+ run do |opts, (path, file)|
75
+ with_error_handling do
76
+ host = opts[:host] || ENV['LDP_HOST']
77
+ if file.nil?
78
+ Ldp::Client.new(url: host).post path
79
+ elsif file == "-"
80
+ Ldp::Client.new(url: host).post path, $stdin
81
+ else
82
+ Ldp::Client.new(url: host).post path, File.read(file)
83
+ end
84
+ end
85
+ end
86
+ end
87
+
88
+ command 'replace' do
89
+ banner "Usage: ldp replace --host [HOST] [PATH] [SOURCE]"
90
+
91
+ on :host=, "Host"
92
+ do_help.call(self)
93
+
94
+ run do |opts, (path,file)|
95
+ with_error_handling do
96
+ host = opts[:host] || ENV['LDP_HOST']
97
+ if file.nil?
98
+ Ldp::Client.new(url: host).put path
99
+ elsif file == "-"
100
+ Ldp::Client.new(url: host).put path, $stdin
101
+ else
102
+ Ldp::Client.new(url: host).put path, File.read(file)
103
+ end
104
+ end
105
+ end
106
+ end
107
+
108
+ command 'patch' do
109
+ banner "Usage: ldp patch --host [HOST] [PATH] [SOURCE]"
110
+
111
+ on :host=, "Host"
112
+ do_help.call(self)
113
+
114
+ run do |opts, (path,file)|
115
+ with_error_handling do
116
+ host = opts[:host] || ENV['LDP_HOST']
117
+ if file.nil?
118
+ Ldp::Client.new(url: host).patch path
119
+ elsif file == "-"
120
+ Ldp::Client.new(url: host).patch path, $stdin.read
121
+ else
122
+ Ldp::Client.new(url: host).patch path, File.read(file)
123
+ end
124
+ end
125
+ end
126
+ end
127
+ end
data/ldp.gemspec CHANGED
@@ -20,6 +20,8 @@ Gem::Specification.new do |spec|
20
20
 
21
21
  spec.add_dependency "faraday"
22
22
  spec.add_dependency "linkeddata", ">= 1.1"
23
+ spec.add_dependency "http_logger"
24
+ spec.add_dependency "slop"
23
25
  spec.add_development_dependency "bundler", "~> 1.3"
24
26
  spec.add_development_dependency "rake"
25
27
  spec.add_development_dependency "rspec"
@@ -1,12 +1,46 @@
1
+ require 'faraday'
2
+
3
+ ##
4
+ # HTTP client methods for making requests to an LDP resource and getting a response back.
1
5
  module Ldp::Client::Methods
2
- def logger
3
- Ldp.logger
6
+
7
+ attr_reader :http
8
+ def initialize_http_client *http_client
9
+ if http_client.length == 1 and http_client.first.is_a? Faraday::Connection
10
+ @http = http_client.first
11
+ else
12
+ @http = Faraday.new *http_client
13
+ end
14
+ end
15
+
16
+ def head url
17
+ logger.debug "LDP: HEAD [#{url}]"
18
+ resp = http.head do |req|
19
+ req.url munge_to_relative_url(url)
20
+
21
+ yield req if block_given?
22
+ end
23
+
24
+ check_for_errors(resp)
4
25
  end
5
26
 
6
27
  # Get a LDP Resource by URI
7
- def get url
28
+ def get url, options = {}
29
+ logger.debug "LDP: GET [#{url}]"
8
30
  resp = http.get do |req|
9
- req.url url
31
+ req.url munge_to_relative_url(url)
32
+
33
+ if options[:minimal]
34
+ req.headers["Prefer"] = "return=minimal"
35
+ else
36
+ includes = Array(options[:include]).map { |x| Ldp.send("prefer_#{x}") if Ldp.respond_to? "prefer_#{x}" }
37
+ omits = Array(options[:omit]).map { |x| Ldp.send("prefer_#{x}") if Ldp.respond_to? "prefer_#{x}" }
38
+ req.headers["Prefer"] = ["return=representation",
39
+ ("include=\"#{includes.join(" ")}\"" unless includes.empty?),
40
+ ("omit=\"#{omits.join(" ")}\"" unless omits.empty?)
41
+ ].compact.join("; ")
42
+ end
43
+
10
44
  yield req if block_given?
11
45
  end
12
46
 
@@ -15,41 +49,84 @@ module Ldp::Client::Methods
15
49
  else
16
50
  resp
17
51
  end
52
+
53
+ check_for_errors(resp)
18
54
  end
19
55
 
20
56
  # Delete a LDP Resource by URI
21
57
  def delete url
22
- http.delete do |req|
23
- req.url url
58
+ logger.debug "LDP: DELETE [#{url}]"
59
+ resp = http.delete do |req|
60
+ req.url munge_to_relative_url(url)
24
61
  yield req if block_given?
25
62
  end
63
+
64
+ check_for_errors(resp)
26
65
  end
27
66
 
28
67
  # Post TTL to an LDP Resource
29
68
  def post url, body = nil, headers = {}
30
- logger.debug "POST [#{url}] #{body}"
31
- http.post do |req|
32
- req.url url
69
+ logger.debug "LDP: POST [#{url}]"
70
+ resp = http.post do |req|
71
+ req.url munge_to_relative_url(url)
33
72
  req.headers = default_headers.merge headers
34
73
  req.body = body
35
74
  yield req if block_given?
36
75
  end
76
+ check_for_errors(resp)
37
77
  end
38
78
 
39
79
  # Update an LDP resource with TTL by URI
40
80
  def put url, body, headers = {}
41
- logger.debug "PUT [#{url}] #{body}"
42
- http.put do |req|
43
- req.url url
81
+ logger.debug "LDP: PUT [#{url}]"
82
+ resp = http.put do |req|
83
+ req.url munge_to_relative_url(url)
44
84
  req.headers = default_headers.merge headers
45
85
  req.body = body
46
86
  yield req if block_given?
47
87
  end
88
+ check_for_errors(resp)
48
89
  end
49
90
 
91
+ # Update an LDP resource with TTL by URI
92
+ def patch url, body, headers = {}
93
+ logger.debug "LDP: PATCH [#{url}]"
94
+ resp = http.patch do |req|
95
+ req.url munge_to_relative_url(url)
96
+ req.headers = default_patch_headers.merge headers
97
+ req.body = body
98
+ yield req if block_given?
99
+ end
100
+ check_for_errors(resp)
101
+ end
50
102
  private
103
+
104
+ def check_for_errors resp
105
+ resp.tap do |resp|
106
+ unless resp.success?
107
+ raise Ldp::NotFound.new(resp.body) if resp.status == 404
108
+ raise Ldp::HttpError.new("STATUS: #{resp.status} #{resp.body[0, 1000]}...")
109
+ end
110
+ end
111
+ end
51
112
 
52
113
  def default_headers
53
114
  {"Content-Type"=>"text/turtle"}
54
115
  end
116
+
117
+ def default_patch_headers
118
+ {"Content-Type"=>"application/sparql-update"}
119
+ end
120
+ ##
121
+ # Some valid query paths can be mistaken for absolute URIs
122
+ # with an alternative scheme. If the scheme isn't HTTP(S), assume
123
+ # they meant a relative URI instead.
124
+ def munge_to_relative_url url
125
+ purl = URI.parse(url)
126
+ if purl.absolute? and !((purl.scheme rescue nil) =~ /^http/)
127
+ "./" + url
128
+ else
129
+ url
130
+ end
131
+ end
55
132
  end
data/lib/ldp/client.rb CHANGED
@@ -1,36 +1,33 @@
1
- require 'faraday'
2
-
1
+ ##
2
+ # LDP client for presenting an ORM on top of an LDP resource
3
3
  module Ldp
4
4
  class Client
5
5
 
6
6
  require 'ldp/client/methods'
7
-
8
7
  include Ldp::Client::Methods
9
-
10
- attr_reader :http
11
8
 
12
9
  def initialize *http_client
13
- if http_client.length == 1 and http_client.first.is_a? Faraday::Connection
14
- @http = http_client.first
15
- else
16
- @http = Faraday.new *http_client
17
- end
10
+ initialize_http_client *http_client
18
11
  end
19
12
 
20
13
  # Find or initialize a new LDP resource by URI
21
- def find_or_initialize subject
22
- data = get(subject)
23
-
24
- unless data.is_a? Response
25
- raise "#{subject} is not an LDP Resource"
26
- end
27
-
28
- if data.container?
29
- Container.new self, subject, data
30
- else
31
- Resource.new self, subject, data
14
+ def find_or_initialize subject, options = {}
15
+ data = get(subject, options = {})
16
+
17
+ case
18
+ when !data.is_a?(Ldp::Response)
19
+ Resource::BinarySource.new self, subject, data
20
+ when data.container?
21
+ Ldp::Container.new_from_response self, subject, data
22
+ when data.resource?
23
+ Resource::RdfSource.new self, subject, data
24
+ else
25
+ Resource::BinarySource.new self, subject, data
32
26
  end
33
27
  end
34
28
 
29
+ def logger
30
+ Ldp.logger
31
+ end
35
32
  end
36
33
  end
@@ -0,0 +1,23 @@
1
+ module Ldp
2
+ class Container::Basic < Container
3
+ def members
4
+ return enum_for(:members) unless block_given?
5
+ contains.each { |k, x| yield x }
6
+ end
7
+
8
+ def contains
9
+ @contains ||= Hash[get.graph.query(predicate: Ldp.contains).map do |x|
10
+ [x.object, Ldp::Resource::RdfSource.new(client, x.object, contained_graph(x.object))]
11
+ end]
12
+ end
13
+
14
+ private
15
+ def contained_graph subject
16
+ g = RDF::Graph.new
17
+ get.graph.query(subject: subject) do |stmt|
18
+ g << stmt
19
+ end
20
+ g
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,16 @@
1
+ module Ldp
2
+ class Container::Direct < Container::Basic
3
+ def members
4
+ return enum_for(:members) unless block_given?
5
+
6
+ get.graph.query(subject: subject, predicate: member_relation).map do |x|
7
+ yield contains[x.object] || Ldp::Resource::RdfSource.new(client, x.object)
8
+ end
9
+ end
10
+
11
+ def member_relation
12
+ graph.first_object(predicate: Ldp.hasMemberRelation) || Ldp.member
13
+ end
14
+
15
+ end
16
+ end
@@ -0,0 +1,11 @@
1
+ module Ldp
2
+ class Container::Indirect < Container::Direct
3
+ def members
4
+ return enum_for(:members) unless block_given?
5
+
6
+ get.graph.query(predicate: member_relation, object: subject).map do |x|
7
+ yield contains[x.object] || Ldp::Resource::RdfSource.new(client, x.object)
8
+ end
9
+ end
10
+ end
11
+ end
data/lib/ldp/container.rb CHANGED
@@ -1,7 +1,22 @@
1
1
  module Ldp
2
- class Container < Resource
2
+ class Container < Resource::RdfSource
3
+ require 'ldp/container/basic'
4
+ require 'ldp/container/direct'
5
+ require 'ldp/container/indirect'
6
+
7
+ def self.new_from_response client, subject, data
8
+ case
9
+ when data.types.include?(Ldp.indirect_container)
10
+ Ldp::Container::Indirect.new client, subject, data
11
+ when data.types.include?(Ldp.direct_container)
12
+ Ldp::Container::Direct.new client, subject, data
13
+ else
14
+ Ldp::Container::Basic.new client, subject, data
15
+ end
16
+ end
17
+
3
18
  ##
4
- # Add a resource to the LDP container
19
+ # Add a new resource to the LDP container
5
20
  def add *args
6
21
  # slug, graph
7
22
  # graph
@@ -11,21 +26,20 @@ module Ldp
11
26
  when (args.length > 2 || args.length == 0)
12
27
 
13
28
  when (args.length == 2)
14
- slug, graph = args
29
+ slug, graph_or_content = args
15
30
  when (args.first.is_a? RDF::Graph)
16
31
  slug = nil
17
- graph = args.first
32
+ graph_or_content = args.first
18
33
  else
19
34
  slug = args.first
20
- graph = RDF::Graph.new
35
+ graph_or_content = RDF::Graph.new
21
36
  end
22
37
 
23
- resp = client.post subject, graph.dump(:ttl) do |req|
38
+ resp = client.post subject, (graph_or_content.is_a?(RDF::Graph) ? graph_or_content.dump(:ttl) : graph_or_content) do |req|
24
39
  req.headers['Slug'] = slug
25
40
  end
26
41
 
27
- subject = resp.headers['Location']
28
- return client.find_or_initialize subject
42
+ client.find_or_initialize resp.headers['Location']
29
43
  end
30
44
  end
31
- end
45
+ end
data/lib/ldp/orm.rb CHANGED
@@ -7,39 +7,67 @@ module Ldp
7
7
  def initialize resource
8
8
  @resource = resource
9
9
  end
10
+
11
+ def subject_uri
12
+ resource.subject_uri
13
+ end
14
+
15
+ def new?
16
+ resource.new?
17
+ end
18
+
19
+ def persisted?
20
+ !new?
21
+ end
10
22
 
11
23
  def graph
12
- resource.graph
24
+ Ldp.instrument 'graph.orm.ldp', subject: subject_uri do
25
+ resource.graph
26
+ end
13
27
  end
14
28
 
15
29
  def value predicate
16
- graph.query(:subject => resource.subject_uri, :predicate => predicate).map do |stmt|
30
+ graph.query(:subject => subject_uri, :predicate => predicate).map do |stmt|
17
31
  stmt.object
18
32
  end
19
33
  end
20
34
 
21
35
  def query *args, &block
22
- graph.query *args, &block
36
+ Ldp.instrument 'query.orm.ldp', subject: subject_uri do
37
+ graph.query *args, &block
38
+ end
23
39
  end
24
40
 
25
41
  def reload
26
- Ldp::Orm.new resource.reload
42
+ Ldp.instrument 'reload.orm.ldp', subject: subject_uri do
43
+ Ldp::Orm.new resource.reload
44
+ end
27
45
  end
28
46
 
29
47
  def create
30
- nil
48
+ Ldp.instrument 'create.orm.ldp', subject: subject_uri do
49
+ # resource.create returns a reloaded resource which causes any default URIs (e.g. "<>")
50
+ # in the graph to be transformed to routable URIs
51
+ Ldp::Orm.new resource.create
52
+ end
31
53
  end
32
54
 
33
55
  def save
34
- @last_response = resource.update
56
+ Ldp.instrument 'save.orm.ldp', subject: subject_uri do
57
+ @last_response = resource.save
35
58
 
36
- diff = Ldp::Resource.check_for_differences_and_reload_resource self
59
+ diff = resource.check_for_differences_and_reload
37
60
 
38
- if diff.any?
39
- diff
40
- else
41
- @last_response.success?
61
+ if diff.any?
62
+ diff
63
+ else
64
+ @last_response.success?
65
+ end
42
66
  end
67
+ rescue Ldp::HttpError => e
68
+ @last_response = e
69
+ logger.debug e
70
+ false
43
71
  end
44
72
 
45
73
  def save!
@@ -48,14 +76,16 @@ module Ldp
48
76
  if result.is_a? RDF::Graph
49
77
  raise GraphDifferenceException.new "", result
50
78
  elsif !result
51
- raise SaveException.new "", @last_response
79
+ raise SaveException.new @last_response
52
80
  end
53
81
 
54
82
  result
55
83
  end
56
84
 
57
85
  def delete
58
- resource.delete
86
+ Ldp.instrument 'delete.orm.ldp', subject: subject_uri do
87
+ resource.delete
88
+ end
59
89
  end
60
90
 
61
91
  def method_missing meth, *args, &block
@@ -66,6 +96,11 @@ module Ldp
66
96
  super
67
97
  end
68
98
 
99
+ private
100
+
101
+ def logger
102
+ Ldp.logger
103
+ end
69
104
  end
70
105
 
71
106
  class GraphDifferenceException < Exception
@@ -76,12 +111,6 @@ module Ldp
76
111
  end
77
112
  end
78
113
 
79
- class SaveException < Exception
80
- attr_reader :response
81
- def initialize message, response
82
- super(message)
83
- @response = response
84
- end
114
+ class SaveException < RuntimeError
85
115
  end
86
116
  end
87
-
@@ -0,0 +1,23 @@
1
+ module Ldp
2
+ class Resource::BinarySource < Ldp::Resource
3
+ attr_accessor :content
4
+
5
+ def initialize client, subject, content_or_response = nil
6
+ super client, subject, content_or_response
7
+
8
+ case content_or_response
9
+ when Faraday::Response
10
+ else
11
+ @content = content_or_response
12
+ end
13
+ end
14
+
15
+ def content
16
+ @content ||= get.body
17
+ end
18
+
19
+ def described_by
20
+ client.find_or_initialize Array(Ldp::Response.links(self)["describedby"]).first
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,77 @@
1
+ module Ldp
2
+ class Resource::RdfSource < Ldp::Resource
3
+
4
+ def initialize client, subject, graph_or_response = nil
5
+ super client, subject, graph_or_response
6
+
7
+ case graph_or_response
8
+ when RDF::Graph
9
+ @graph = graph_or_response
10
+ when Ldp::Response
11
+ when NilClass
12
+ #nop
13
+ else
14
+ raise ArgumentError, "Third argument to #{self.class}.new should be a RDF::Graph or a Ldp::Response. You provided #{graph_or_response.class}"
15
+ end
16
+ end
17
+
18
+ def create
19
+ super do |req|
20
+ req.headers = { "Content-Type" => "text/turtle" }
21
+ end
22
+ end
23
+
24
+ def content
25
+ graph.dump(:ttl) if graph
26
+ end
27
+
28
+ def graph
29
+ @graph ||= RDF::Graph.new if new?
30
+ @graph ||= begin
31
+ original_graph = get.graph
32
+
33
+ inlinedResources = get.graph.query(:predicate => Ldp.contains).map { |x| x.object }
34
+
35
+ # we want to scope this graph to just statements about this model, not contained relations
36
+ unless inlinedResources.empty?
37
+ new_graph = RDF::Graph.new
38
+
39
+ original_graph.each_statement do |s|
40
+ unless inlinedResources.include? s.subject
41
+ new_graph << s
42
+ end
43
+ end
44
+
45
+ new_graph
46
+ else
47
+ original_graph
48
+ end
49
+ end
50
+ end
51
+
52
+ def check_for_differences_and_reload
53
+ self.class.check_for_differences_and_reload_resource self
54
+ end
55
+
56
+ def self.check_for_differences_and_reload_resource old_object
57
+ new_object = old_object.reload
58
+
59
+ bijection = new_object.graph.bijection_to(old_object.graph)
60
+ diff = RDF::Graph.new
61
+
62
+ old_object.graph.each do |statement|
63
+ if statement.has_blank_nodes?
64
+ subject = bijection.fetch(statement.subject, false) if statement.subject.node?
65
+ object = bijection.fetch(statement.object, false) if statement.object.node?
66
+ bijection_statement = RDF::Statement.new :subject => subject || statemnet.subject, :predicate => statement.predicate, :object => object || statement.object
67
+
68
+ diff << statement if subject === false or object === false or new_object.graph.has_statement?(bijection_statement)
69
+ elsif !new_object.graph.has_statement? statement
70
+ diff << statement
71
+ end
72
+ end
73
+
74
+ diff
75
+ end
76
+ end
77
+ end