Class: Fixes

Inherits:
Object
  • Object
show all
Extended by:
Enumerable
Defined in:
app/models/fixes.rb

Overview

Singleton representing a collection of arbitrary transformations to apply to Finding Aids, with dependency resolution.

Each “fix” is a key-value pair, with the key consisting of a string that matches an Issue.identifier, and the value consisting of a lambda

Within a fix, the instance variable `@xml` represents the current state of the finding aid being altered.

To get all the Fixes in the order that they should be run.

Defined Under Namespace

Classes: Failure

Constant Summary

FILE_DIR =

Directory that fix files are stored in

File.join(Rails.root, 'system', 'fixes')

Class Method Summary (collapse)

Instance Method Summary (collapse)

Class Method Details

+ (Lambda) [](identifier)

Convenience accessor - Fixes gets back the fix in question

Parameters:

  • identifier (Symbol, String)

    identifier this fix is associated

Returns:

  • (Lambda)

    the lambda associated with this identifier



102
103
104
# File 'app/models/fixes.rb', line 102

def self.[](identifier)
  @@fixes[identifier]
end

+ (Object) definitions(&block)

Definitions block, which sets the context to a Fix instantiation Within this block, #fix_for can be used to define new fixes.



58
59
60
61
62
# File 'app/models/fixes.rb', line 58

def self.definitions(&block)
  instance = new
  instance.instance_eval(&block)
  @@fixes = reorder
end

+ (Object) each(&block)

Iterates over fixes as hash



40
41
42
43
44
45
# File 'app/models/fixes.rb', line 40

def each(&block)
  return @@fixes.to_enum unless block_given?
  @@fixes.each do |member|
    yield member
  end
end

+ (Boolean) key?(key)

Delegate #key? on the class to the internal hash

Parameters:

  • key (Symbol, String)

    key to check for

Returns:

  • (Boolean)

    whether key is present



52
53
54
# File 'app/models/fixes.rb', line 52

def self.key?(key)
  @@fixes.key?(key)
end

+ (Hash{String => Lambda}) preflights

Expose preflights variable for use in Run

Returns:

  • (Hash{String => Lambda})

    preflights



31
32
33
# File 'app/models/fixes.rb', line 31

def self.preflights
  @@preflights
end

+ (Object) refresh_fixes(dir = nil)

For each file in the FILE_DIR, create or replace a fix Uses the file's name to determine fix name

Parameters:

  • dir (String?) (defaults to: nil)

    directory name to refresh fixes from, defaults to Fixes::FILE_DIR at runtime



143
144
145
146
147
148
149
150
151
152
153
154
# File 'app/models/fixes.rb', line 143

def self.refresh_fixes(dir = nil)
  @@fixes.clear
  @@constraints.clear
  @@preflights.clear
  definitions do
    Dir[File.join(dir || FILE_DIR, '*.rb')].each do |fname|
      fixes_content = IO.read(fname)
      raise "File `#{fname}` does not contain fixes" unless fixes_content.index('fix_for')
      eval fixes_content, nil, fname, 1
    end
  end
end

+ (Hash{String => Lambda}) reorder

Ensures that order-dependent fixes are ordered properly Depends on Ruby's ordered hash semantics For implementation details, search on “topological sort” and “Kahn's Algorithm”

Returns:

  • (Hash{String => Lambda})

    fixes



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'app/models/fixes.rb', line 68

def self.reorder
  order = {}.with_indifferent_access
  edges = @@constraints.dup
  incoming = edges.map(&:last).uniq
  nodes = edges.flatten.uniq
  s = Set.new(nodes.reject{|e| incoming.include? e})
  while !s.empty?
    n = s.first
    s.delete n
    order[n] = @@fixes[n]
    nodes.each do |m|
      if edges.member? [n,m]
        edges.delete [n,m]
        s.add m if edges.none? {|(_,y)| y == m}
      end
    end
  end
  raise(<<-ERROR_TXT) if edges.any?
Cyclical dependency found in your fixes.  Please inspect your @depends_on statements.
The following edges remain in your dependency graph after processing:
#{edges}
  ERROR_TXT

  # Add fixes without dependency concerns
  @@fixes.keys.reject {|fix_id| order.key? fix_id}.each do |fix_id|
    order[fix_id] = @@fixes[fix_id]
  end

  order
end

Instance Method Details

- (Lambda) fix_for(identifier, depends_on: [], preflight: false, &block)

Define an individual fix

#fix_for is always called with a block. The contents of this block constiutute a transformation to be applied later to finding aids.

Within this block, the instance variable `@xml` refers to a

Nokogiri::XML::Document

that represents the current state of a finding aid.

Any changes made to `@xml` will be applied to the eventual output.

Parameters:

  • identifier (String, Symbol)

    key for retrieving the fix, should match an Issue identifier

  • depends_on (Array?)

    fixes that must be run before this fix

  • preflight (Boolean)

    Whether a fix should be run unconditionally prior to run

  • block (Block)

    implementation of the fix

Returns:

  • (Lambda)

    the fix



120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'app/models/fixes.rb', line 120

def fix_for(identifier, depends_on: [], preflight: false, &block)
  unless preflight
    depends_on.each do |dep|
      @@constraints << [dep, identifier]
    end

    @@fixes[identifier] = -> (xml) do
      @xml = xml
      yield
      @xml
    end
  else
    @@preflights[identifier] = -> (xml) do
      @xml = xml
      yield
      @xml
    end
  end
end