ldp 0.0.3 → 0.0.4

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: 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