Skip to content

Commit

Permalink
Temporarily override active_fedora until changed upstream
Browse files Browse the repository at this point in the history
  • Loading branch information
randalldfloyd committed Oct 15, 2024
1 parent bffa85d commit 79be733
Showing 1 changed file with 244 additions and 0 deletions.
244 changes: 244 additions & 0 deletions lib/active_fedora/attribute_methods.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
require 'active_support/core_ext/object'
require 'active_support/core_ext/class/attribute'
require 'mutex_m'

module ActiveFedora
module AttributeMethods
extend ActiveSupport::Concern
include ActiveModel::AttributeMethods

AttrNames = Module.new do
def self.set_name_cache(name, value)
const_name = "ATTR_#{name}"
const_set const_name, value.dup.freeze unless const_defined? const_name
end
end

RESTRICTED_CLASS_METHODS = %w[private public protected allocate new name parent superclass].freeze

class GeneratedAttributeMethods < Module; end # :nodoc:

module ClassMethods
def inherited(child_class) # :nodoc:
child_class.initialize_generated_modules
super
end

def initialize_generated_modules # :nodoc:
@generated_attribute_methods = GeneratedAttributeMethods.new { extend Mutex_m }
@attribute_methods_generated = false
include @generated_attribute_methods

super
end

# Raises an ActiveFedora::DangerousAttributeError exception when an
# \Active \Record method is defined in the model, otherwise +false+.
#
# class Person < ActiveRecord::Base
# def save
# 'already defined by Active Fedora'
# end
# end
#
# Person.instance_method_already_implemented?(:save)
# # => ActiveFedora::DangerousAttributeError: save is defined by Active Record. Check to make sure that you don't have an attribute or method with the same name.
#
# Person.instance_method_already_implemented?(:name)
# # => false
def instance_method_already_implemented?(method_name)
raise DangerousAttributeError, "#{method_name} is defined by Active Fedora. Check to make sure that you don't have an attribute or method with the same name." if dangerous_attribute_method?(method_name)

if superclass == Base
super
else
# If ThisClass < ... < SomeSuperClass < ... < Base and SomeSuperClass
# defines its own attribute method, then we don't want to overwrite that.
defined = method_defined_within?(method_name, superclass, Base) &&
!superclass.instance_method(method_name).owner.is_a?(GeneratedAttributeMethods)
defined || super
end
end

# A method name is 'dangerous' if it is already (re)defined by Active Fedora, but
# not by any ancestors. (So 'puts' is not dangerous but 'save' is.)
def dangerous_attribute_method?(name) # :nodoc:
method_defined_within?(name, Base)
end

def method_defined_within?(name, klass, superklass = klass.superclass) # :nodoc:
if klass.method_defined?(name) || klass.private_method_defined?(name)
if superklass.method_defined?(name) || superklass.private_method_defined?(name)
klass.instance_method(name).owner != superklass.instance_method(name).owner
else
true
end
else
false
end
end

# A class method is 'dangerous' if it is already (re)defined by Active Record, but
# not by any ancestors. (So 'puts' is not dangerous but 'new' is.)
def dangerous_class_method?(method_name)
RESTRICTED_CLASS_METHODS.include?(method_name.to_s) || class_method_defined_within?(method_name, Base)
end

def class_method_defined_within?(name, klass, superklass = klass.superclass) # :nodoc:
if klass.respond_to?(name, true)
if superklass.respond_to?(name, true)
klass.method(name).owner != superklass.method(name).owner
else
true
end
else
false
end
end

private

# @param name [Symbol] name of the attribute to generate
def generate_method(name)
generated_attribute_methods.synchronize do
define_attribute_methods name
end
end
end

included do
initialize_generated_modules
include Read
include Write
include Dirty
end

# Returns +true+ if the given attribute is in the attributes hash, otherwise +false+.
#
# class Person < ActiveRecord::Base
# end
#
# person = Person.new
# person.has_attribute?(:name) # => true
# person.has_attribute?('age') # => true
# person.has_attribute?(:nothing) # => false
def has_attribute?(attr_name)
attribute_names.include?(attr_name.to_s)
end

# Returns an array of names for the attributes available on this object.
#
# class Person < ActiveFedora::Base
# end
#
# person = Person.new
# person.attribute_names
# # => ["id", "created_at", "updated_at", "name", "age"]
def attribute_names
@local_attributes.keys
end

# Returns a hash of all the attributes with their names as keys and the values of the attributes as values.
#
# class Person < ActiveFedora::Base
# end
#
# person = Person.create(name: 'Francesco', age: 22)
# person.attributes
# # => {"id"=>3, "created_at"=>Sun, 21 Oct 2012 04:53:04, "updated_at"=>Sun, 21 Oct 2012 04:53:04, "name"=>"Francesco", "age"=>22}
def attributes
result = {}
attribute_names.each do |name|
result[name] = read_attribute(name)
end
result
end

# Returns an <tt>#inspect</tt>-like string for the value of the
# attribute +attr_name+. String attributes are truncated up to 50
# characters, Date and Time attributes are returned in the
# <tt>:db</tt> format, Array attributes are truncated up to 10 values.
# Other attributes return the value of <tt>#inspect</tt> without
# modification.
#
# person = Person.create!(name: 'David Heinemeier Hansson ' * 3)
#
# person.attribute_for_inspect(:name)
# # => "\"David Heinemeier Hansson David Heinemeier Hansson ...\""
#
# person.attribute_for_inspect(:created_at)
# # => "\"2012-10-22 00:15:07\""
#
# person.attribute_for_inspect(:tag_ids)
# # => "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ...]"
def attribute_for_inspect(attr_name)
value = self[attr_name]

if value.is_a?(String) && value.length > 50
"#{value[0, 50]}...".inspect
elsif value.is_a?(Date) || value.is_a?(Time)
%("#{value.to_s}")
elsif value.is_a?(Array) && value.size > 10
inspected = value.first(10).inspect
%(#{inspected[0...-1]}, ...])
else
value.inspect
end
end

# Returns +true+ if the specified +attribute+ has been set by the user or by a
# database load and is neither +nil+ nor <tt>empty?</tt> (the latter only applies
# to objects that respond to <tt>empty?</tt>, most notably Strings). Otherwise, +false+.
# Note that it always returns +true+ with boolean attributes.
#
# class Task < ActiveRecord::Base
# end
#
# task = Task.new(title: '', is_done: false)
# task.attribute_present?(:title) # => false
# task.attribute_present?(:is_done) # => true
# task.title = 'Buy milk'
# task.is_done = true
# task.attribute_present?(:title) # => true
# task.attribute_present?(:is_done) # => true
def attribute_present?(attribute)
value = self[attribute]
!value.nil? && !(value.respond_to?(:empty?) && value.empty?)
end

# Returns the value of the attribute identified by <tt>attr_name</tt> after it has been typecast (for example,
# "2004-12-12" in a date column is cast to a date object, like Date.new(2004, 12, 12)). It raises
# <tt>ActiveModel::MissingAttributeError</tt> if the identified attribute is missing.
#
# Alias for the <tt>read_attribute</tt> method.
#
# class Person < ActiveRecord::Base
# belongs_to :organization
# end
#
# person = Person.new(name: 'Francesco', age: '22')
# person[:name] # => "Francesco"
# person[:age] # => 22
#
# person = Person.select('id').first
# person[:name] # => ActiveModel::MissingAttributeError: missing attribute: name
# person[:organization_id] # => ActiveModel::MissingAttributeError: missing attribute: organization_id
def [](attr_name)
read_attribute(attr_name) { |n| missing_attribute(n, caller) }
end

# Updates the attribute identified by <tt>attr_name</tt> with the specified +value+.
# (Alias for the protected <tt>write_attribute</tt> method).
#
# class Person < ActiveFedora::Base
# end
#
# person = Person.new
# person[:age] = '22'
# person[:age] # => 22
# person[:age] # => Integer
def []=(attr_name, value)
write_attribute(attr_name, value)
end
end
end

0 comments on commit 79be733

Please sign in to comment.