Skip to content

Commit

Permalink
Add support for contained RDF sources.
Browse files Browse the repository at this point in the history
  • Loading branch information
Trey Terrell committed Oct 1, 2015
1 parent 80b830e commit 02eecf7
Show file tree
Hide file tree
Showing 15 changed files with 245 additions and 87 deletions.
1 change: 1 addition & 0 deletions lib/active_fedora.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ module ActiveFedora #:nodoc:
extend ActiveSupport::Autoload

eager_autoload do
autoload :AssociationHash
autoload :AssociationRelation
autoload :Associations
autoload :AttachedFiles
Expand Down
116 changes: 116 additions & 0 deletions lib/active_fedora/association_hash.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
module ActiveFedora
##
# Used as an access method for associations on a model, given some
# reflections.
class AssociationHash
def initialize (model, reflections)
@base = model
@reflections = reflections
end

def [] (name)
association(name).reader if association(name)
end

def []= (name, object)
association(name).writer(object) if association(name)
end

def association(name)
# Check to see if the key exists before casting to a symbol, because symbols
# are not garbage collected in earlier versions of Ruby
@base.association(name.to_sym) if key?(name)
end

def reflections
@reflections
end

def merge(other_hash)
Merged.new(self, other_hash)
end

def each
keys.each do |k|
yield k, self[k]
end
end

def keys
reflections.keys
end

# Check that the key exists with indifferent access (symbol or string) in a
# manner that avoids generating extra symbols. Symbols are not garbage collected
# in earlier versions of ruby.
def key?(key)
keys.include?(key) || keys.map(&:to_s).include?(key)
end

def values
keys.map { |k| self[k] }
end

def has_key?(key)
keys.include?(key)
end

def size
keys.size
end

def empty?
reflections.empty?
end

def each_value
keys.each do |k|
yield self[k]
end
end

def changed
select do |_, obj|
obj.changed?
end
end

def select
keys.each_with_object({}) do |k, h|
val = self[k]
h[k] = val if yield k, val
end
end

def freeze
keys.each do |name|
association(name).reader.freeze if association(name).loaded?
end
super
end
end
##
# Represents the result of merging two association hashes.
# @note As the keys can come from multiple models, the attributes become
# unwritable.
class Merged < AssociationHash
attr_reader :first, :second

def initialize(first, second)
@first = first
@second = second
end

def [] (name)
first[name] || second[name]
end

def []= (name)
raise NotImplementedError "Unable to set properties on a merged association hash"
end

def keys
first.keys + second.keys
end
end
end
6 changes: 3 additions & 3 deletions lib/active_fedora/associations.rb
Original file line number Diff line number Diff line change
Expand Up @@ -162,13 +162,13 @@ def has_many(name, options={})
# This method is used to specify the details of a contained resource.
# Pass the name as the first argument and a hash of options as the second argument
# Note that this method doesn't actually execute the block, but stores it, to be executed
# by any the implementation of the datastream(specified as :class_name)
# by any the implementation of the resource(specified as :class_name)
#
# @param [String] name the handle to refer to this child as
# @param [Hash] options
# @option options [Class] :class_name The class that will represent this child, should extend ``ActiveFedora::File''
# @option options [Class] :class_name The class that will represent this child, should extend ``ActiveFedora::File'' or ``ActiveFedora::Base''
# @option options [String] :url
# @option options [Boolean] :autocreate Always create this datastream on new objects
# @option options [Boolean] :autocreate Always create this resource on new objects
# @yield block executed by some types of child resources
def contains(name, options = {}, &block)
options[:block] = block if block
Expand Down
16 changes: 14 additions & 2 deletions lib/active_fedora/associations/basic_contains_association.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ def reader(force_reload = false)
end

def find_target
reflection.build_association(target_uri) do |record|
find_or_initialize_target do |record|
configure_datastream(record) if reflection.options[:block]
end
end

def target_uri
"#{owner.uri}/#{reflection.name}"
"#{owner.id}/#{reflection.name}"
end

private
Expand All @@ -23,6 +23,18 @@ def raise_on_type_mismatch(record)
super
end

def find_or_initialize_target(&block)
begin
if reflection.klass < ActiveFedora::File
reflection.build_association(target_uri, &block)
else
reflection.klass.find(target_uri)
end
rescue ActiveFedora::ObjectNotFoundError
reflection.build_association(target_uri, &block)
end
end

def replace(record)
if record
raise_on_type_mismatch(record)
Expand Down
7 changes: 6 additions & 1 deletion lib/active_fedora/associations/builder/contains.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,12 @@ def self.define_readers(mixin, name)
set_uri = uri.kind_of?(RDF::URI) ? uri.value.present? : uri.present?
if set_uri
file_uri = "#{uri}/#{name}"
file.uri = file_uri
begin
file.uri = file_uri
rescue ActiveFedora::AlreadyPersistedError
end
end
if file.respond_to?(:exists!)
file.exists! if contains_assertions.include?(file_uri)
end
end
Expand Down
5 changes: 4 additions & 1 deletion lib/active_fedora/associations/id_composite.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ module ActiveFedora::Associations
class IDComposite
attr_reader :ids, :id_translator
include Enumerable
# @param [Array<#to_s>] ids An array of ids or URIs to convert to IDs.
# @param [#call] id_translator An object to handle the conversion of a URI
# to an ID.
def initialize(ids, id_translator)
@ids = ids
@id_translator = id_translator
Expand All @@ -20,7 +23,7 @@ def each
private

def convert(id)
if id.start_with?("http")
if id.to_s.start_with?("http")
id_translator.call(id)
else
id
Expand Down
12 changes: 12 additions & 0 deletions lib/active_fedora/core.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,18 @@ def initialize(attributes_or_id = nil, &block)
run_callbacks :initialize
end

##
# @param [#to_s] uri a full fedora URI or relative ID to set this resource
# to.
# @note This can only be run on an unpersisted resource.
def uri=(uri)
if persisted?
raise AlreadyPersistedError, "You can not set a URI for a persisted ActiveFedora object."
else
@ldp_source = build_ldp_resource(self.class.uri_to_id(uri))
end
end

# Reloads the object from Fedora.
def reload
check_persistence unless persisted?
Expand Down
4 changes: 4 additions & 0 deletions lib/active_fedora/errors.rb
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,8 @@ class Rollback < ActiveFedoraError
# Raised when Fedora returns a version without a create date
class VersionLacksCreateDate < ActiveFedoraError
end

# Raised when you try to set a URI to an already persisted Base object.
class AlreadyPersistedError < ActiveFedoraError
end
end
7 changes: 5 additions & 2 deletions lib/active_fedora/file.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,11 @@ def initialize(parent_or_url_or_hash = nil, path=nil, options={}, &block)
case parent_or_url_or_hash
when Hash
@ldp_source = build_ldp_resource_via_uri
when nil, String, ::RDF::URI
@ldp_source = build_ldp_resource_via_uri parent_or_url_or_hash
when nil
@ldp_source = build_ldp_resource_via_uri nil
when String, ::RDF::URI
id = ActiveFedora::Associations::IDComposite.new([parent_or_url_or_hash], translate_uri_to_id).first
@ldp_source = build_ldp_resource id
when ActiveFedora::Base
Deprecation.warn File, "Initializing a file by passing a container is deprecated. Initialize with a uri instead. This capability will be removed in active-fedora 10.0"
uri = if parent_or_url_or_hash.uri.kind_of?(::RDF::URI) && parent_or_url_or_hash.uri.value.empty?
Expand Down
69 changes: 1 addition & 68 deletions lib/active_fedora/files_hash.rb
Original file line number Diff line number Diff line change
@@ -1,82 +1,15 @@
require 'forwardable'

module ActiveFedora
class FilesHash
extend Forwardable

class FilesHash < AssociationHash
def initialize (model)
@base = model
end

def [] (name)
association(name).reader if association(name)
end

def []= (name, object)
association(name).writer(object) if association(name)
end

def association(name)
# Check to see if the key exists before casting to a symbol, because symbols
# are not garbage collected in earlier versions of Ruby
@base.association(name.to_sym) if key?(name)
end

def reflections
@base.class.child_resource_reflections
end

def each
keys.each do |k|
yield k, self[k]
end
end

def keys
reflections.keys + @base.undeclared_files
end

# Check that the key exists with indifferent access (symbol or string) in a
# manner that avoids generating extra symbols. Symbols are not garbage collected
# in earlier versions of ruby.
def key?(key)
keys.include?(key) || keys.map(&:to_s).include?(key)
end

def values
keys.map { |k| self[k] }
end

def has_key?(key)
keys.include?(key)
end

def size
keys.size
end

def empty?
reflections.empty?
end

def each_value
keys.each do |k|
yield self[k]
end
end

def select
keys.each_with_object({}) do |k, h|
val = self[k]
h[k] = val if yield k, val
end
end

def freeze
keys.each do |name|
association(name).reader.freeze if association(name).loaded?
end
super
end
end
end
27 changes: 18 additions & 9 deletions lib/active_fedora/persistence.rb
Original file line number Diff line number Diff line change
Expand Up @@ -145,15 +145,15 @@ def create_record(options = {})
assign_rdf_subject
serialize_attached_files
@ldp_source = @ldp_source.create
assign_uri_to_attached_files
save_attached_files
assign_uri_to_contained_resources
save_contained_resources
refresh
end

def update_record(options = {})
serialize_attached_files
execute_sparql_update
save_attached_files
save_contained_resources
refresh
end

Expand Down Expand Up @@ -195,16 +195,25 @@ def init_root_path
ActiveFedora.fedora.connection.put(path, "")
end

def assign_uri_to_attached_files
attached_files.each do |name, ds|
ds.uri= "#{uri}/#{name}"
def assign_uri_to_contained_resources
contained_resources.each do |name, source|
source.uri= "#{uri}/#{name}"
end
end

def save_attached_files
attached_files.select { |_, file| file.changed? }.each do |_, file|
file.save # Don't call save! because if the content_changed? returns false, it'll raise an error.
def save_contained_resources
contained_resources.changed.each do |_, resource|
resource.save
end
end

def contained_resources
@contained_resources ||= attached_files.merge(contained_rdf_sources)
end

def contained_rdf_sources
@contained_rdf_sources ||=
AssociationHash.new(self, self.class.contained_rdf_source_reflections)
end
end
end
6 changes: 5 additions & 1 deletion lib/active_fedora/reflection.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,11 @@ def outgoing_reflections
end

def child_resource_reflections
reflections.select { |_, reflection| reflection.kind_of?(AssociationReflection) && reflection.macro == :contains }
reflections.select { |_, reflection| reflection.kind_of?(AssociationReflection) && reflection.macro == :contains && reflection.klass <= ActiveFedora::File}
end

def contained_rdf_source_reflections
reflections.select { |_, reflection| reflection.kind_of?(AssociationReflection) && reflection.macro == :contains && !(reflection.klass <= ActiveFedora::File)}
end

# Returns the AssociationReflection object for the +association+ (use the symbol).
Expand Down
Loading

0 comments on commit 02eecf7

Please sign in to comment.