fedora_lens 0.0.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: af9796406a65d286343ded62b45876a28f2c542f
4
+ data.tar.gz: c1237758ac4f5fc1131f9add45cbddf5d657c840
5
+ SHA512:
6
+ metadata.gz: 5bfca9e2b65760e3674ceb986153df14f6de8885c320287cd2367ade51aa1ca78cb0d97a824b479871df63e298442b4b49d590c6469b539984a00704cd440d2e
7
+ data.tar.gz: 9b4fe962ae9a7dc2e70fc9533129b2247e10acd14554247b3ebcdfa25c051f6620f779eadab9f4a3e7c1d4455af4e92c9c57e7e5cd989bf117102d805e3cfbfe
data/.gitignore ADDED
@@ -0,0 +1,8 @@
1
+ /.bundle
2
+ /fedora/*.war
3
+ /fcrepo4-data/
4
+ Gemfile.lock
5
+ tags
6
+ jetty
7
+ tmp
8
+ pkg
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.0.0-p353
data/.travis.yml ADDED
@@ -0,0 +1,10 @@
1
+ notifications:
2
+ email: false
3
+
4
+ rvm:
5
+ - 2.1.0
6
+
7
+ env:
8
+ global:
9
+ - NOKOGIRI_USE_SYSTEM_LIBRARIES=true
10
+
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ gem "ldp", github: 'cbeer/ldp'
6
+ gem 'jettywrapper'
7
+
8
+ group :development, :test do
9
+ gem 'byebug' unless ENV['TRAVIS']
10
+ end
data/README.md ADDED
@@ -0,0 +1,29 @@
1
+ ## Usage
2
+
3
+ Download and extract Fedora 4:
4
+ ```bash
5
+ rake jetty:download
6
+ rake jetty:unzip
7
+ ```
8
+
9
+ Start Fedora:
10
+ ```bash
11
+ rake jetty:start
12
+ ```
13
+
14
+ ```ruby
15
+ # bundle console
16
+ require 'test_class'
17
+ b = TestClass.new(title: "New resource")
18
+ b.uri
19
+ b.save
20
+ b.id
21
+ a = TestClass.find(b.id)
22
+ a.attributes
23
+ a.primary_id = "some id"
24
+ a.save
25
+ b.reload
26
+ b.attributes
27
+ c = TestClass.create(title: "created resource")
28
+ c.id
29
+ ```
data/Rakefile ADDED
@@ -0,0 +1,19 @@
1
+ require 'bundler'
2
+ require 'rspec/core/rake_task'
3
+ Bundler::GemHelper.install_tasks
4
+
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ ZIP_URL = "https://github.com/projecthydra/hydra-jetty/archive/fedora-4a4.zip"
9
+ require 'jettywrapper'
10
+
11
+ task default: :ci
12
+
13
+ task ci: 'jetty:unzip' do
14
+ jetty_params = Jettywrapper.load_config('test')
15
+ error = Jettywrapper.wrap(jetty_params) do
16
+ Rake::Task[:spec].invoke
17
+ end
18
+ raise "test failures: #{error}" if error
19
+ end
data/config/jetty.yml ADDED
@@ -0,0 +1,6 @@
1
+ default:
2
+ jetty_port: <%= ENV['TEST_JETTY_PORT'] || 8983 %>
3
+ startup_wait: 90
4
+ java_opts:
5
+ - "-Xmx256m"
6
+ - "-XX:MaxPermSize=128m"
data/demo.rb ADDED
@@ -0,0 +1,76 @@
1
+ require 'fedora_lens'
2
+ require 'active_support/core_ext/string'
3
+
4
+ module GenericFileRdfDatastream
5
+ def self.mappings
6
+ {
7
+ part_of: [RDF::DC.isPartOf],
8
+ resource_type: [RDF::DC.type],
9
+ part_of: [RDF::DC.isPartOf],
10
+ resource_type: [RDF::DC.type],
11
+ title: [RDF::DC.title],
12
+ creator: [RDF::DC.creator],
13
+ contributor: [RDF::DC.contributor],
14
+ description: [RDF::DC.description],
15
+ tag: [RDF::DC.relation],
16
+ rights: [RDF::DC.rights],
17
+ publisher: [RDF::DC.publisher],
18
+ date_created: [RDF::DC.created],
19
+ date_uploaded: [RDF::DC.dateSubmitted],
20
+ date_modified: [RDF::DC.modified],
21
+ subject: [RDF::DC.subject],
22
+ language: [RDF::DC.language],
23
+ identifier: [RDF::DC.identifier],
24
+ based_near: [RDF::FOAF.based_near],
25
+ related_url: [RDF::RDFS.seeAlso],
26
+ }
27
+ end
28
+ end
29
+
30
+ module Metadata
31
+ extend ActiveSupport::Concern
32
+
33
+ included do
34
+ # has_metadata "descMetadata", type: GenericFileRdfDatastream
35
+ # has_metadata "properties", type: PropertiesDatastream
36
+ # has_file_datastream "content", type: FileContentDatastream
37
+ # has_file_datastream "thumbnail"
38
+
39
+ # has_attributes :relative_path, :depositor, :import_url, datastream: :properties, multiple: false
40
+ has_attributes :date_uploaded, :date_modified, datastream: :descMetadata, multiple: false
41
+ has_attributes :related_url, :based_near, :part_of, :creator,
42
+ :contributor, :title, :tag, :description, :rights,
43
+ :publisher, :date_created, :subject,
44
+ :resource_type, :identifier, :language, datastream: :descMetadata, multiple: true
45
+ end
46
+
47
+ def descMetadata
48
+ {path: [RDF::URI.new('http://digitalcurationexperts.com/example#descMetadata'),
49
+ FedoraLens::Lenses.single,
50
+ FedoraLens::Lenses.load_or_build_orm],
51
+ type: GenericFileRdfDatastream}
52
+ end
53
+ def properties
54
+ {path: [RDF::URI.new('http://digitalcurationexperts.com/example#properties'),
55
+ FedoraLens::Lenses.single,
56
+ FedoraLens::Lenses.load_or_build_orm],
57
+ type: PropertiesDatastream}
58
+ end
59
+ def has_attributes(*attributes)
60
+ opts = attributes.pop
61
+ attributes.each do |name|
62
+ path = opts[:datastream][:path] + opts[:datastream][:type].mappings[name]
63
+ path << FedoraLens::Lenses.single unless opts[:multiple]
64
+ attribute name, path
65
+ end
66
+ end
67
+ end
68
+
69
+ class Example
70
+ include FedoraLens
71
+ include FedoraLens::Lenses
72
+ extend Metadata
73
+ has_attributes :date_uploaded, datastream: descMetadata, multiple: true
74
+ has_attributes :title, datastream: descMetadata, multiple: true
75
+ # has_attributes :relative_path, datastream: properties, multiple: false
76
+ end
data/fedora/.gitkeep ADDED
File without changes
@@ -0,0 +1,30 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'fedora_lens/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "fedora_lens"
8
+ spec.version = FedoraLens::VERSION
9
+ spec.authors = ["Justin Coyne", "Brian Maddy"]
10
+ spec.email = ["justin@curationexperts.com"]
11
+ spec.summary = %q{A client for Fedora Commons Repository (fcrepo) version 4}
12
+ spec.description = %q{}
13
+ spec.homepage = ""
14
+ spec.license = "APACHE2"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.5"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "rspec"
24
+ spec.add_development_dependency "childprocess"
25
+
26
+ spec.add_dependency 'rdf-turtle', '~> 1.1.2'
27
+ spec.add_dependency 'activemodel', '>= 4.0.2', "< 5.0"
28
+ spec.add_dependency 'nokogiri', '~> 1.6.1'
29
+ spec.add_dependency 'ldp'
30
+ end
@@ -0,0 +1,7 @@
1
+ module FedoraLens
2
+ # generic exception class
3
+ class FedoraLensError < StandardError; end
4
+ # raised when a resource can't be saved
5
+ class RecordNotSaved < FedoraLensError; end
6
+ class AttributeNotSupportedError < FedoraLensError; end
7
+ end
@@ -0,0 +1,15 @@
1
+ require 'active_support/core_ext/hash'
2
+
3
+ module FedoraLens
4
+ class Lens < ActiveSupport::HashWithIndifferentAccess
5
+ def get(source)
6
+ self[:get].call(source)
7
+ end
8
+ def put(source, value)
9
+ self[:put].call(source, value)
10
+ end
11
+ def create(value)
12
+ self[:create].call(value)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,60 @@
1
+ module FedoraLens
2
+ module LensTests
3
+ # See page 6 of the manual for the Harmony language for a description
4
+ # on how lenses work
5
+ # http://www.seas.upenn.edu/~harmony/manual.pdf
6
+ # @example testing a lens that converts xml to a dom and back
7
+ # test_lens(lens, Nokogiri::XML("<a/>"), Nokogiri::XML("<b/>") do |v|
8
+ # v.to_xml
9
+ # end
10
+ # @param [lens]
11
+ # the lens to test
12
+ # @param [value]
13
+ # the value to test with (when calling put)
14
+ # @param [source]
15
+ # the source document this lens operates on
16
+ # @yield [converter]
17
+ # a block that converts the value from the lens to something that can be
18
+ # compared with #== (defaults to the identity function)
19
+ # @yieldparam [value or source]
20
+ # a value that will be compared
21
+ def test_lens(lens, source, value, &block)
22
+ test_lens_get_put(lens, source, &block)
23
+ test_lens_put_get(lens, source, value, &block)
24
+ test_lens_create_get(lens, value, &block)
25
+ end
26
+
27
+ def test_lens_get_put(lens, source)
28
+ it "is well-behaved (GetPut: put(source, get(source)) == source)" do
29
+ converted = lens.put(source, lens.get(source))
30
+ if block_given?
31
+ yield(converted).should eq yield(source)
32
+ else
33
+ converted.should eq source
34
+ end
35
+ end
36
+ end
37
+
38
+ def test_lens_put_get(lens, source, value)
39
+ it "is well-behaved (PutGet: get(put(source, value)) == value)" do
40
+ converted = lens.get(lens.put(source, value))
41
+ if block_given?
42
+ yield(converted).should eq yield(value)
43
+ else
44
+ converted.should eq value
45
+ end
46
+ end
47
+ end
48
+
49
+ def test_lens_create_get(lens, value)
50
+ it "is well-behaved (CreateGet: get(create(value)) == value)" do
51
+ created = lens.get(lens.create(value))
52
+ if block_given?
53
+ yield(created).should eq yield(value)
54
+ else
55
+ created.should eq value
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,149 @@
1
+ require 'fedora_lens/lens'
2
+
3
+ module FedoraLens
4
+ module Lenses
5
+ # We would like to use lenses like this: Lenses.at_css.get(...)
6
+ # Using a simple hash is gross because calls are like this: Lenses.at_css[:get].call(...)
7
+ # If we use a Module, we can't pass in parameters to build a more specific lens like
8
+ # in the case of the at_css lens.
9
+ # If we use a class, we need to call it like this: Lenses::AtCss.new('path').get(...)
10
+ # that's a lot of ".new" that just becomes line noise
11
+ # Classes don't seem to close over their environment as a function would (not sure why...)
12
+ # so we can create anonymous classes with functions
13
+ # Instead, we're just adding the helper functions to a simple hash.
14
+
15
+ class << self
16
+ def single
17
+ Lens[
18
+ get: :first.to_proc,
19
+ put: lambda {|source, value| source[0] = value; source},
20
+ create: lambda {|value| [value]}
21
+ ]
22
+ end
23
+
24
+ def literal_to_string
25
+ Lens[
26
+ get: :to_s.to_proc,
27
+ put: lambda do |source, value|
28
+ if source.is_a?(RDF::Literal)
29
+ RDF::Literal.new(value, language: source.language, datatype: source.datatype)
30
+ else
31
+ RDF::Literal.new(value)
32
+ end
33
+ end
34
+ ]
35
+ end
36
+
37
+ def hash_update
38
+ Lens[
39
+ get: lambda {|hash| hash[key]},
40
+ put: lambda {|hash, pair| hash.merge(pair[0] => pair[1])}
41
+ ]
42
+ end
43
+
44
+ def orm_to_hash(name_to_lens)
45
+ Lens[
46
+ get: lambda do |orm|
47
+ name_to_lens.reduce({}) do |hash, pair|
48
+ key, lens = pair
49
+ hash.merge(key => lens.get(orm))
50
+ end
51
+ end,
52
+ put: lambda do |orm, hash|
53
+ name_to_lens.each do |pair|
54
+ key, lens = pair
55
+ lens.put(orm, hash[key])
56
+ end
57
+ orm
58
+ end
59
+ ]
60
+ end
61
+
62
+ def as_dom
63
+ Lens[
64
+ # TODO figure out a memoizing strategy so we don't parse multiple times
65
+ get: lambda {|xml| Nokogiri::XML(xml)},
66
+ put: lambda {|xml, value| value.to_xml},
67
+ create: lambda {|value| value.to_xml}
68
+ ]
69
+ end
70
+
71
+ def at_css(selector)
72
+ Lens[
73
+ get: lambda {|doc| doc.at_css(selector).content},
74
+ put: lambda {|doc, value|
75
+ doc.at_css(selector).content = value; doc},
76
+ # can't do a css create because we don't know the structure
77
+ ]
78
+ end
79
+
80
+ def get_predicate(predicate)
81
+ Lens[
82
+ get: lambda do |orm|
83
+ orm.value(predicate)
84
+ end,
85
+ put: lambda do |orm, values|
86
+ orm.graph.delete([orm.resource.subject_uri, predicate, nil])
87
+ (values || []).each do |value|
88
+ orm.graph.insert([orm.resource.subject_uri, predicate, value])
89
+ end
90
+ orm
91
+ end,
92
+ create: lambda do |values|
93
+ orm = Ldp::Orm.new(Ldp::Resource.new(nil, '', RDF::Graph.new))
94
+ values.each do |value|
95
+ orm.graph.insert([orm.resource.subject_uri, predicate, value])
96
+ end
97
+ orm
98
+ end
99
+ ]
100
+ end
101
+
102
+ def load_model(klass)
103
+ Lens[
104
+ get: lambda do |id|
105
+ klass.find(id)
106
+ end,
107
+ put: lambda do |id, model|
108
+ model.save
109
+ id
110
+ end
111
+ ]
112
+ end
113
+
114
+ def load_or_build_orm
115
+ Lens[
116
+ get: lambda do |uri|
117
+ if uri.present?
118
+ Ldp::Orm.new(Ldp::Resource.new(FedoraLens.connection, uri.to_s))
119
+ else
120
+ Ldp::Orm.new(Ldp::Resource.new(FedoraLens.connection, nil, RDF::Graph.new))
121
+ end
122
+ end,
123
+ put: lambda do |uri, orm|
124
+ if orm.resource.subject.present?
125
+ orm.save
126
+ else
127
+ orm.resource.create
128
+ end
129
+ orm.resource.subject_uri
130
+ end
131
+ ]
132
+ end
133
+
134
+ def compose(outer, inner)
135
+ Lens[
136
+ get: lambda do |source|
137
+ inner.get(outer.get(source))
138
+ end,
139
+ put: lambda do |source, value|
140
+ outer.put(source, inner.put(outer.get(source), value))
141
+ end
142
+ ]
143
+ end
144
+
145
+ def zip(first, second)
146
+ end
147
+ end
148
+ end
149
+ end
@@ -0,0 +1,3 @@
1
+ module FedoraLens
2
+ VERSION = '0.0.1'
3
+ end
@@ -0,0 +1,181 @@
1
+ require 'rdf'
2
+ require 'ldp'
3
+ require 'ldp_monkeypatch'
4
+ require 'rdf/turtle'
5
+ require 'nokogiri'
6
+ require 'active_model'
7
+ require 'active_support/concern'
8
+ require 'active_support/core_ext/object'
9
+ require 'active_support/core_ext/class/attribute'
10
+ require 'active_support/core_ext/module/attribute_accessors'
11
+ require 'active_support/core_ext/hash'
12
+ require 'fedora_lens/errors'
13
+
14
+ module FedoraLens
15
+ extend ActiveSupport::Concern
16
+ HOST = "http://localhost:8983/fedora"
17
+ #HOST = "http://localhost:8080"
18
+ PATH = '/rest'
19
+
20
+ def self.connection
21
+ @@connection ||= Ldp::Client.new(HOST)
22
+ end
23
+
24
+ included do
25
+ extend ActiveModel::Naming
26
+ include ActiveModel::Validations
27
+ include ActiveModel::Conversion
28
+
29
+ class_attribute :attributes_as_lenses
30
+ self.attributes_as_lenses = {}.with_indifferent_access
31
+
32
+ attr_reader :attributes
33
+ attr_reader :orm
34
+ end
35
+
36
+ def initialize(subject_or_data = {}, data = nil)
37
+ init_core(subject_or_data, data)
38
+ end
39
+
40
+ def persisted?() false end
41
+
42
+ def errors
43
+ obj = Object.new
44
+ def obj.[](key) [] end
45
+ def obj.full_messages() [] end
46
+ obj
47
+ end
48
+
49
+ def read_attribute_for_validation(key)
50
+ @attributes[key]
51
+ end
52
+
53
+ def reload
54
+ @orm = @orm.reload
55
+ @attributes = get_attributes_from_orm(@orm)
56
+ end
57
+
58
+ def delete
59
+ @orm.resource.delete
60
+ end
61
+
62
+ def save
63
+ @orm = self.class.orm_to_hash.put(@orm, @attributes)
64
+ new_record? ? create_record : update_record
65
+ end
66
+
67
+ def save!
68
+ save || raise(RecordNotSaved)
69
+ end
70
+
71
+ def new_record?
72
+ @new_record
73
+ end
74
+
75
+ def uri
76
+ @orm.try(:resource).try(:subject_uri).try(:to_s)
77
+ end
78
+
79
+ def id
80
+ URI.parse(uri).to_s.sub(HOST + PATH, '') if uri.present?
81
+ end
82
+
83
+ protected
84
+ # This allows you to overide the initializer, but still use this behavior
85
+ def init_core(subject_or_data = {}, data = nil)
86
+ @new_record = true
87
+ case subject_or_data
88
+ when Ldp::Resource
89
+ @orm = Ldp::Orm.new(subject_or_data)
90
+ @attributes = get_attributes_from_orm(@orm)
91
+ when NilClass, Hash
92
+ data = subject_or_data || {}
93
+ @orm = Ldp::Orm.new(Ldp::Resource.new(FedoraLens.connection, nil, RDF::Graph.new))
94
+ @attributes = data.with_indifferent_access
95
+ when String
96
+ data ||= {}
97
+ @orm = Ldp::Orm.new(Ldp::Resource.new(FedoraLens.connection, subject_or_data, RDF::Graph.new))
98
+ @attributes = data.with_indifferent_access
99
+ else
100
+ raise ArgumentError, "#{data.class} is not acceptable"
101
+ end
102
+ end
103
+
104
+ def create_record
105
+ orm.resource.create
106
+ @new_record = false
107
+ true
108
+ end
109
+
110
+ def update_record
111
+ # Fedora errors out when you try to set the rdf:type
112
+ # see https://github.com/cbeer/ldp/issues/2
113
+ orm.graph.delete([@orm.resource.subject_uri,
114
+ RDF::URI.new("http://www.w3.org/1999/02/22-rdf-syntax-ns#type"),
115
+ nil])
116
+ orm.save
117
+ orm.last_response.success?
118
+ end
119
+
120
+
121
+ private
122
+
123
+ def get_attributes_from_orm(orm)
124
+ self.class.orm_to_hash.get(orm).with_indifferent_access
125
+ end
126
+
127
+ module ClassMethods
128
+ def find(id)
129
+ resource = Ldp::Resource.new(FedoraLens.connection, id_to_uri(id))
130
+ self.new(resource)
131
+ end
132
+
133
+ def id_to_uri(id)
134
+ "#{HOST}#{PATH}#{id}"
135
+ end
136
+
137
+ def attribute(name, path, options={})
138
+ raise AttributeNotSupportedException if name.to_sym == :id
139
+ attributes_as_lenses[name] = path.map{|s| coerce_to_lens(s)}
140
+ define_method name do
141
+ @attributes[name]
142
+ end
143
+ define_method "#{name}=" do |value|
144
+ @attributes[name] = value
145
+ end
146
+ orm_to_hash = nil # force us to rebuild the aggregate_lens in case it was already built.
147
+ end
148
+
149
+ def create(data)
150
+ model = self.new(data)
151
+ model.save
152
+ model
153
+ end
154
+
155
+ def orm_to_hash
156
+ if @orm_to_hash.nil?
157
+ aggregate_lens = attributes_as_lenses.reduce({}) do |acc, pair|
158
+ name, path = pair
159
+ lens = path.reduce {|outer, inner| Lenses.compose(outer, inner)}
160
+ acc.merge(name => lens)
161
+ end
162
+ @orm_to_hash = Lenses.orm_to_hash(aggregate_lens)
163
+ end
164
+ @orm_to_hash
165
+ end
166
+
167
+ def coerce_to_lens(path_segment)
168
+ if path_segment.is_a? RDF::URI
169
+ Lenses.get_predicate(path_segment)
170
+ else
171
+ path_segment
172
+ end
173
+ end
174
+
175
+ # def has_one(name, scope = nil, options = {})
176
+ # ActiveRecord::Associations::Builder::HasOne.build(self, name, scope, options)
177
+ # end
178
+ end
179
+ end
180
+
181
+ require 'fedora_lens/lenses'
@@ -0,0 +1,22 @@
1
+ require 'ldp'
2
+ module Ldp
3
+
4
+ class Resource
5
+ def create
6
+ # create seems to not be working in ldp, but it's not entirely clear how it should work
7
+ # for example, :new? calls :get, which will raise an error when an object doesn't exist,
8
+ # this means it is impossible to create anything.
9
+ # raise "" if new?
10
+ # puts "MONKEY patch create #{client.inspect}"
11
+ # puts "Graph #{graph.dump(:ttl)}"
12
+ resp = client.post '/fedora/rest', graph.dump(:ttl) do |req|
13
+ # req.headers['Slug'] = subject
14
+ end
15
+
16
+ # puts "Heards #{resp.headers.inspect}"
17
+ @subject = resp.headers['Location']
18
+ # puts "Loc #{@subject.inspect}"
19
+ @subject_uri = nil
20
+ end
21
+ end
22
+ end
data/lib/test_class.rb ADDED
@@ -0,0 +1,64 @@
1
+ require 'fedora_lens'
2
+ class TestClass
3
+ include FedoraLens
4
+ include FedoraLens::Lenses
5
+
6
+ def self.custom_lens(type)
7
+ Lens[
8
+ get: lambda do |doc|
9
+ if node = doc.at_css("relationship[type=#{type}]")
10
+ node.content
11
+ else
12
+ ""
13
+ end
14
+ end,
15
+ put: lambda do |doc, value|
16
+ # TODO: create missing nodes
17
+ # doc.at_css(selector).content = value; doc
18
+ doc << Nokogiri::XML::Node.new("relationships", doc) if doc.root.nil?
19
+ root = doc.root
20
+ relationship = root.at_css("relationship[type=#{type}]")
21
+ if relationship.nil?
22
+ relationship = Nokogiri::XML::Node.new("relationship", doc)
23
+ relationship.set_attribute("type", type)
24
+ root << relationship
25
+ end
26
+ relationship.content = value
27
+ doc
28
+ end,
29
+ create: lambda do |value|
30
+ Nokogiri::XML("<relationships>
31
+ <relationship type=\"#{type}\">#{value}</relationship>
32
+ </relationships>")
33
+ end
34
+ ]
35
+ end
36
+
37
+ attribute :title, [RDF::DC11.title, Lenses.single, Lenses.literal_to_string]
38
+ attribute :mixinTypes, [
39
+ RDF::URI.new("http://fedora.info/definitions/v4/repository#mixinTypes")]
40
+ attribute :primaryType, [
41
+ RDF::URI.new("http://fedora.info/definitions/v4/repository#primaryType"),
42
+ Lenses.single,
43
+ Lenses.literal_to_string]
44
+ attribute :primary_id, [
45
+ RDF::DC11.relation,
46
+ Lenses.single,
47
+ Lenses.literal_to_string,
48
+ Lenses.as_dom,
49
+ # Lenses.at_css("relationship[type=primary]")]
50
+ custom_lens("primary")]
51
+ attribute :secondary, [
52
+ RDF::DC11.relation,
53
+ Lenses.single,
54
+ Lenses.literal_to_string,
55
+ Lenses.as_dom,
56
+ custom_lens("secondary"),
57
+ # Lenses.at_css("relationship[type=secondary]"),
58
+ # Lenses.load_model(TestRelated)
59
+ ]
60
+ # def self.generated_feature_methods
61
+ # end
62
+ # has_one :primary
63
+ end
64
+
@@ -0,0 +1,87 @@
1
+ require 'spec_helper'
2
+ require 'fedora_lens/lens_tests'
3
+
4
+ module FedoraLens
5
+ describe Lenses do
6
+ extend LensTests
7
+
8
+ describe ".single" do
9
+ it "gets the first item" do
10
+ Lenses.single.get([:first, :second]).should eq :first
11
+ end
12
+ it "sets the first item" do
13
+ Lenses.single.put([:first, :second], :changed).should eq [:changed, :second]
14
+ end
15
+ it "creates an item in an array" do
16
+ Lenses.single.create(:value).should eq [:value]
17
+ end
18
+ test_lens Lenses.single, [], :foo
19
+ test_lens Lenses.single, [:one], :foo
20
+ test_lens Lenses.single, [:one, :two], :foo
21
+ end
22
+
23
+ describe ".as_dom" do
24
+ it "converts xml to a Nokogiri::XML::Document" do
25
+ xml = "<foo><bar>content</bar></foo>"
26
+ Lenses.as_dom.get(xml).to_xml.should eq Nokogiri::XML(xml).to_xml
27
+ end
28
+ it "converts a modified Nokogiri::XML::Document back to xml" do
29
+ xml = "<foo><bar>content</bar></foo>"
30
+ modified = Nokogiri::XML("<foo><bar>changed</bar></foo>")
31
+ Lenses.as_dom.put(xml, modified.dup).should eq modified.to_xml
32
+ end
33
+ it "creates a new string of xml" do
34
+ value = Nokogiri::XML("<foo><bar>created</bar></foo>")
35
+ Lenses.as_dom.create(value).should eq value.to_xml
36
+ end
37
+ test_lens(Lenses.as_dom,
38
+ Nokogiri::XML("<foo><bar>content</bar></foo>").to_xml,
39
+ Nokogiri::XML("<foo><bar>new content</bar></foo>")){|v| v.to_s}
40
+ end
41
+
42
+ describe ".at_css" do
43
+ it "gets the content at the css selector" do
44
+ dom = Nokogiri::XML("<foo><bar>content</bar></foo>")
45
+ Lenses.at_css('foo bar').get(dom).should eq 'content'
46
+ end
47
+ it "sets the first item" do
48
+ dom = Nokogiri::XML("<foo><bar>content</bar></foo>")
49
+ expected = Nokogiri::XML("<foo><bar>changed</bar></foo>")
50
+ Lenses.at_css('foo bar').put(dom, :changed).to_xml.should eq expected.to_xml
51
+ end
52
+ test_lens_get_put(Lenses.at_css("foo bar"), Nokogiri::XML("<foo><bar>content</bar></foo>"))
53
+ test_lens_put_get(Lenses.at_css("foo bar"),
54
+ Nokogiri::XML("<foo><bar>content</bar></foo>"),
55
+ "new content")
56
+ end
57
+
58
+ describe ".get_predicate" do
59
+ let(:orm) do
60
+ graph = RDF::Graph.new
61
+ orm = Ldp::Orm.new(Ldp::Resource.new(nil, '', graph))
62
+ orm.graph.insert([orm.resource.subject_uri, RDF::DC11.title, "title"])
63
+ orm
64
+ end
65
+ it "converts an Ldp::Orm to the value of the specified predicate" do
66
+ Lenses.get_predicate(RDF::DC11.title).get(orm).first.should eq RDF::Literal.new("title")
67
+ end
68
+ it "sets the value of an Ldp::Orm for the specified predicate" do
69
+ Lenses.get_predicate(RDF::DC11.title).put(orm, [RDF::Literal.new("new")]).value(RDF::DC11.title).first.should eq RDF::Literal.new("new")
70
+ end
71
+ it "creates a new Ldp::Orm with the value for a specified predicate" do
72
+ converted = Lenses.get_predicate(RDF::DC11.title).create([RDF::Literal.new("title")])
73
+ converted.graph.dump(:ttl).should eq orm.graph.dump(:ttl)
74
+ end
75
+ graph = RDF::Graph.new
76
+ orm = Ldp::Orm.new(Ldp::Resource.new(nil, '', graph))
77
+ orm.graph.insert([orm.resource.subject_uri, RDF::DC11.title, "title"])
78
+ test_lens(Lenses.get_predicate(RDF::DC11.title), orm, [RDF::Literal.new("new title")]) do |v|
79
+ if v.is_a? Ldp::Orm
80
+ v.value(RDF::DC11.title)
81
+ else
82
+ v
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,107 @@
1
+ require 'spec_helper'
2
+
3
+ describe FedoraLens do
4
+ include ActiveModel::Lint::Tests
5
+
6
+ class TestClass
7
+ include FedoraLens
8
+ attribute :title, [RDF::DC11.title, Lenses.single, Lenses.literal_to_string]
9
+ # attribute :xml_title, [RDF::DC11.title, Lenses.single]
10
+ end
11
+
12
+ # for ActiveModel::Lint::Tests
13
+ def setup
14
+ @model = TestClass.new
15
+ end
16
+
17
+ subject { TestClass.new }
18
+
19
+ describe ".find" do
20
+ context "when the object doesn't exist" do
21
+ it "should raise an error" do
22
+ expect{ TestClass.find('bahvejlavawwv') }.to raise_error Ldp::NotFound
23
+ end
24
+ end
25
+
26
+ context "when the object exists" do
27
+ let(:existing) { TestClass.create(title: "created resource") }
28
+ subject { TestClass.find(existing.id) }
29
+ it { should be_kind_of TestClass }
30
+ its(:id) { should eq existing.id }
31
+ end
32
+ end
33
+
34
+ describe ".delete" do
35
+ subject { TestClass.create(title: "created resource") }
36
+
37
+ it "should be deleted" do
38
+ subject.delete
39
+ expect{ TestClass.find(subject.id) }.to raise_error Ldp::NotFound
40
+ end
41
+ end
42
+
43
+ describe ".create" do
44
+ subject { TestClass.create(attributes) }
45
+ context "with a hash" do
46
+ let(:attributes) { { title: "created resource" } }
47
+ it "creates a resource" do
48
+ expect(TestClass.find(subject.id).title).to eq "created resource"
49
+ end
50
+ end
51
+ context "with nil" do
52
+ let(:attributes) { nil }
53
+ it "creates a resource" do
54
+ expect(TestClass.find(subject.id)).to be_kind_of TestClass
55
+ end
56
+ end
57
+ end
58
+
59
+ describe "#id" do
60
+ it "should not have 'fedora' in the id" do
61
+ m = TestClass.new('http://localhost:8983/fedora/rest/41/0d/6b/47/410d6b47-ce9c-4fa0-91e2-d62765667c52')
62
+ expect(m.id).to eq '/41/0d/6b/47/410d6b47-ce9c-4fa0-91e2-d62765667c52'
63
+ end
64
+ end
65
+
66
+
67
+ describe ".save" do
68
+ it "saves a new resource" do
69
+ m = TestClass.new(title: "created resource")
70
+ m.save
71
+ TestClass.find(m.id).title.should eq "created resource"
72
+ end
73
+
74
+ it "saves an updated resource" do
75
+ m = TestClass.create(title: "created resource")
76
+ m.reload
77
+ m.title = "changed title"
78
+ m.save
79
+ TestClass.find(m.id).title.should eq "changed title"
80
+ end
81
+
82
+ context "with a supplied id" do
83
+ subject { TestClass.new(TestClass.id_to_uri('foobar')) }
84
+
85
+ it "saves with that id" do
86
+ expect(subject.new_record?).to be_true
87
+ expect(subject.save).to be_true
88
+ expect(subject.new_record?).to be_false
89
+ end
90
+
91
+ end
92
+
93
+ end
94
+
95
+ describe ".attribute" do
96
+ it "makes a setter/getter" do
97
+ subject.title = "foo"
98
+ subject.title.should eq "foo"
99
+ end
100
+
101
+ it "loads from rdf" do
102
+ end
103
+
104
+ it "mixes rdf and xml" do
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,16 @@
1
+ require 'bundler'
2
+ require "bundler/setup"
3
+ require 'fedora_lens'
4
+ require 'fedora_lens/lenses'
5
+
6
+ RSpec.configure do |config|
7
+ config.treat_symbols_as_metadata_keys_with_true_values = true
8
+ config.run_all_when_everything_filtered = true
9
+ config.filter_run :focus
10
+
11
+ # Run specs in random order to surface order dependencies. If you find an
12
+ # order dependency and want to debug it, you can fix the order by providing
13
+ # the seed, which is printed after each run.
14
+ # --seed 1234
15
+ config.order = 'random'
16
+ end
metadata ADDED
@@ -0,0 +1,189 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fedora_lens
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Justin Coyne
8
+ - Brian Maddy
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-04-14 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ~>
19
+ - !ruby/object:Gem::Version
20
+ version: '1.5'
21
+ type: :development
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ~>
26
+ - !ruby/object:Gem::Version
27
+ version: '1.5'
28
+ - !ruby/object:Gem::Dependency
29
+ name: rake
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - '>='
33
+ - !ruby/object:Gem::Version
34
+ version: '0'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - '>='
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: rspec
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - '>='
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - '>='
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ - !ruby/object:Gem::Dependency
57
+ name: childprocess
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - '>='
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ - !ruby/object:Gem::Dependency
71
+ name: rdf-turtle
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ~>
75
+ - !ruby/object:Gem::Version
76
+ version: 1.1.2
77
+ type: :runtime
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ~>
82
+ - !ruby/object:Gem::Version
83
+ version: 1.1.2
84
+ - !ruby/object:Gem::Dependency
85
+ name: activemodel
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - '>='
89
+ - !ruby/object:Gem::Version
90
+ version: 4.0.2
91
+ - - <
92
+ - !ruby/object:Gem::Version
93
+ version: '5.0'
94
+ type: :runtime
95
+ prerelease: false
96
+ version_requirements: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - '>='
99
+ - !ruby/object:Gem::Version
100
+ version: 4.0.2
101
+ - - <
102
+ - !ruby/object:Gem::Version
103
+ version: '5.0'
104
+ - !ruby/object:Gem::Dependency
105
+ name: nokogiri
106
+ requirement: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ~>
109
+ - !ruby/object:Gem::Version
110
+ version: 1.6.1
111
+ type: :runtime
112
+ prerelease: false
113
+ version_requirements: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ~>
116
+ - !ruby/object:Gem::Version
117
+ version: 1.6.1
118
+ - !ruby/object:Gem::Dependency
119
+ name: ldp
120
+ requirement: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - '>='
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ type: :runtime
126
+ prerelease: false
127
+ version_requirements: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - '>='
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ description: ''
133
+ email:
134
+ - justin@curationexperts.com
135
+ executables: []
136
+ extensions: []
137
+ extra_rdoc_files: []
138
+ files:
139
+ - .gitignore
140
+ - .rspec
141
+ - .ruby-version
142
+ - .travis.yml
143
+ - Gemfile
144
+ - README.md
145
+ - Rakefile
146
+ - config/jetty.yml
147
+ - demo.rb
148
+ - fedora/.gitkeep
149
+ - fedora_lens.gemspec
150
+ - lib/fedora_lens.rb
151
+ - lib/fedora_lens/errors.rb
152
+ - lib/fedora_lens/lens.rb
153
+ - lib/fedora_lens/lens_tests.rb
154
+ - lib/fedora_lens/lenses.rb
155
+ - lib/fedora_lens/version.rb
156
+ - lib/ldp_monkeypatch.rb
157
+ - lib/test_class.rb
158
+ - spec/fedora_lens/lenses_spec.rb
159
+ - spec/fedora_lens_spec.rb
160
+ - spec/spec_helper.rb
161
+ homepage: ''
162
+ licenses:
163
+ - APACHE2
164
+ metadata: {}
165
+ post_install_message:
166
+ rdoc_options: []
167
+ require_paths:
168
+ - lib
169
+ required_ruby_version: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - '>='
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ required_rubygems_version: !ruby/object:Gem::Requirement
175
+ requirements:
176
+ - - '>='
177
+ - !ruby/object:Gem::Version
178
+ version: '0'
179
+ requirements: []
180
+ rubyforge_project:
181
+ rubygems_version: 2.1.11
182
+ signing_key:
183
+ specification_version: 4
184
+ summary: A client for Fedora Commons Repository (fcrepo) version 4
185
+ test_files:
186
+ - spec/fedora_lens/lenses_spec.rb
187
+ - spec/fedora_lens_spec.rb
188
+ - spec/spec_helper.rb
189
+ has_rdoc: