Skip to content

Commit

Permalink
Added File.metadata to define metadata properties
Browse files Browse the repository at this point in the history
This writes the properties to ./fcr:metadata relative to the files URI
Fixes #499
  • Loading branch information
jcoyne committed Nov 11, 2014
1 parent fabc1cc commit 10f90a8
Show file tree
Hide file tree
Showing 3 changed files with 152 additions and 0 deletions.
1 change: 1 addition & 0 deletions lib/active_fedora.rb
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ class Rollback < RuntimeError; end # :nodoc:
autoload :Predicates
autoload :Validations
autoload :Versionable
autoload :WithMetadata
end

module AttributeMethods
Expand Down
99 changes: 99 additions & 0 deletions lib/active_fedora/with_metadata.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
module ActiveFedora
module WithMetadata
extend ActiveSupport::Concern

def metadata_node
@metadata_node ||= self.class.metadata_schema.new(self)
end

def save(*)
if super
metadata_node.metadata_uri = described_by # TODO only necessary if the URI was < > before
metadata_node.save # TODO if changed?
end
end

module ClassMethods
def metadata(&block)
metadata_schema.exec_block(&block)
end

def metadata_schema
@metadata_schema ||= MetadataNode(self)
end

def MetadataNode(parent_klass)

This comment has been minimized.

Copy link
@awead

awead Nov 12, 2014

Contributor

There's apparently something very clever going on here, however, I'm not clever enough to understand it. A method name that's the same name as the class defined below? MetadataNode really looks like a different thing and ought to be in its own file for clarity. Then at least some comments about what's going on in that method.

This comment has been minimized.

Copy link
@jcoyne

jcoyne Nov 12, 2014

Author Member

This comment has been minimized.

Copy link
@awead

awead Nov 12, 2014

Contributor

Yes, but that doesn't make it any more coherent. Can you explain more about what's going on? It appears your modifying someone's parent class. Why?

This comment has been minimized.

Copy link
@jcoyne

jcoyne Nov 12, 2014

Author Member

parent_class= is just a method on MetadataNode. It needs to know that so it can generate the correct delegates from the parent File to the MetadataNode.

This comment has been minimized.

Copy link
@awead

awead Nov 12, 2014

Contributor

OK. So what's GeneratedMetadataSchema?

klass = self.const_set(:GeneratedMetadataSchema, Class.new(MetadataNode))
klass.parent_class = parent_klass
klass
end
end

class MetadataNode < ActiveTriples::Resource
include ActiveModel::Dirty
attr_reader :file

def initialize(file)
@file = file
super(file.uri, ldp_source.graph)
end

def metadata_uri= uri
@metadata_uri = uri
end

def metadata_uri
@metadata_uri ||= if file.new_record?
RDF::URI.new nil
else
raise "#{file} must respond_to described_by" unless file.respond_to? :described_by
file.described_by
end
end

def set_value(*args)
super
attribute_will_change! args.first
end

def self.parent_class= parent
@parent_class = parent
end

def self.parent_class
@parent_class
end

def self.property(name, options)
parent_class.delegate name, :"#{name}=", :"#{name}_changed?", to: :metadata_node
super
end

def self.create_delegating_setter(name)
file.class.delegate(name, to: :metadata_node)
end

def ldp_source
@ldp_source ||= LdpResource.new(ldp_connection, metadata_uri)
end

def ldp_connection
ActiveFedora.fedora.connection
end

def save
raise "Save the file first" if file.new_record?
change_set = ChangeSet.new(self, self, changed_attributes.keys)
SparqlInsert.new(change_set.changes, RDF::URI.new(file.uri)).execute(metadata_uri)
@ldp_source = nil
true
end

def self.exec_block(&block)
class_eval &block
end

end

end
end
52 changes: 52 additions & 0 deletions spec/integration/with_metadata_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
require 'spec_helper'

describe ActiveFedora::WithMetadata do
before do
class Sample < ActiveFedora::Base
contains :file, class_name: 'SampleFile'
end

class SampleFile < ActiveFedora::File
include ActiveFedora::WithMetadata

metadata do
property :title, predicate: RDF::DC.title

This comment has been minimized.

Copy link
@awead

awead Nov 12, 2014

Contributor

I'm assuming you can define solrization fields here in the same way as you would a base object.

This comment has been minimized.

Copy link
@jcoyne

jcoyne Nov 12, 2014

Author Member

You can define them, but there's no to_solr method on MetadataNode that would do anything with those definitions.

This comment has been minimized.

Copy link
@awead

awead Nov 12, 2014

Contributor

So presently they're not getting solrized at all?

This comment has been minimized.

Copy link
@jcoyne

jcoyne Nov 12, 2014

Author Member

Right. If we decided to solrize them, we'd need some mechanism to deconflict the fields defined on MetadataNode vs the fields defined on the File (think NokogiriRDFDatastream) itself.

end
end
end

after do
Object.send(:remove_const, :SampleFile)
Object.send(:remove_const, :Sample)
end

let(:base) { Sample.new }
let(:file) { base.file }

describe "properties" do
before do
file.title = ['one', 'two']
end
it "should set and retrieve properties" do
expect(file.title).to eq ['one', 'two']
end

it "should track changes" do
expect(file.title_changed?).to be true
end
end

describe "#save" do
before do
file.content = "Hey"
file.title = ["foo"]
base.save
base.reload
end

it "should save the metadata too" do
expect(base.file.title).to eq ['foo']
end
end

end

0 comments on commit 10f90a8

Please sign in to comment.