moab-versioning 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/moab.rb +59 -0
- data/lib/moab/bagger.rb +289 -0
- data/lib/moab/config.rb +21 -0
- data/lib/moab/exceptions.rb +18 -0
- data/lib/moab/file_group.rb +244 -0
- data/lib/moab/file_group_difference.rb +336 -0
- data/lib/moab/file_group_difference_subset.rb +45 -0
- data/lib/moab/file_instance.rb +82 -0
- data/lib/moab/file_instance_difference.rb +54 -0
- data/lib/moab/file_inventory.rb +279 -0
- data/lib/moab/file_inventory_difference.rb +132 -0
- data/lib/moab/file_manifestation.rb +85 -0
- data/lib/moab/file_signature.rb +200 -0
- data/lib/moab/signature_catalog.rb +195 -0
- data/lib/moab/signature_catalog_entry.rb +61 -0
- data/lib/moab/storage_object.rb +220 -0
- data/lib/moab/storage_object_version.rb +333 -0
- data/lib/moab/storage_repository.rb +57 -0
- data/lib/moab/storage_services.rb +104 -0
- data/lib/moab/verification_result.rb +83 -0
- data/lib/moab/version_metadata.rb +38 -0
- data/lib/moab/version_metadata_entry.rb +64 -0
- data/lib/moab/version_metadata_event.rb +47 -0
- data/lib/moab_stanford.rb +18 -0
- data/lib/monkey_patches.rb +65 -0
- data/lib/serializer.rb +36 -0
- data/lib/serializer/manifest.rb +76 -0
- data/lib/serializer/serializable.rb +178 -0
- data/lib/stanford/active_fedora_object.rb +34 -0
- data/lib/stanford/content_inventory.rb +236 -0
- data/lib/stanford/dor_metadata.rb +49 -0
- data/lib/stanford/storage_repository.rb +46 -0
- data/lib/stanford/storage_services.rb +66 -0
- data/lib/tasks/yard.rake +34 -0
- data/lib/tools/api_doc_generator.rb +396 -0
- data/lib/tools/spec_generator.rb +410 -0
- data/lib/tools/spec_generator_old.rb +49 -0
- metadata +252 -0
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'moab_stanford'
|
2
|
+
|
3
|
+
module Stanford
|
4
|
+
|
5
|
+
# Stanford-specific utility methods for interfacing with DOR metadata files
|
6
|
+
#
|
7
|
+
# ====Data Model
|
8
|
+
# * <b>{DorMetadata} = utility methods for interfacing with Stanford metadata files (esp contentMetadata)</b>
|
9
|
+
# * {ContentInventory} [1..1] = utilities for transforming contentMetadata to versionInventory and doing comparsions
|
10
|
+
# * {ActiveFedoraObject} [1..*] = utility for extracting content or other information from a Fedora Instance
|
11
|
+
#
|
12
|
+
# @note Copyright (c) 2012 by The Board of Trustees of the Leland Stanford Junior University.
|
13
|
+
# All rights reserved. See {file:LICENSE.rdoc} for details.
|
14
|
+
class DorMetadata
|
15
|
+
|
16
|
+
# @return [String] The digital object identifier (druid)
|
17
|
+
attr_accessor :digital_object_id
|
18
|
+
|
19
|
+
# @return [Integer] \@versionId = The ordinal version number
|
20
|
+
attr_accessor :version_id
|
21
|
+
|
22
|
+
# @param digital_object_id [String] The digital object identifier
|
23
|
+
# @param version_id [Integer] The ordinal version number
|
24
|
+
# @return [Stanford::DorMetadata]
|
25
|
+
def initialize(digital_object_id, version_id=nil)
|
26
|
+
@digital_object_id = digital_object_id
|
27
|
+
@version_id = version_id
|
28
|
+
end
|
29
|
+
|
30
|
+
# @api internal
|
31
|
+
# @param directory [String] The location of the directory to be inventoried
|
32
|
+
# @param version_id (see #initialize)
|
33
|
+
# @return [FileInventory] Inventory of the files under the specified directory
|
34
|
+
def inventory_from_directory(directory, version_id=nil)
|
35
|
+
version_id ||= @version_id
|
36
|
+
version_inventory = FileInventory.new(:type=>'version',:digital_object_id=>@digital_object_id, :version_id=>version_id)
|
37
|
+
content_metadata = IO.read(File.join(directory,'contentMetadata.xml'))
|
38
|
+
content_group = ContentInventory.new.group_from_cm(content_metadata, 'preserve' )
|
39
|
+
version_inventory.groups << content_group
|
40
|
+
metadata_group = FileGroup.new(:group_id=>'metadata').group_from_directory(directory)
|
41
|
+
version_inventory.groups << metadata_group
|
42
|
+
version_inventory
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'moab_stanford'
|
2
|
+
|
3
|
+
module Stanford
|
4
|
+
|
5
|
+
# A class to represent the SDR repository store
|
6
|
+
#
|
7
|
+
# ====Data Model
|
8
|
+
# * <b>{StorageRepository} = represents a digital object repository storage node</b>
|
9
|
+
#
|
10
|
+
# @note Copyright (c) 2012 by The Board of Trustees of the Leland Stanford Junior University.
|
11
|
+
# All rights reserved. See {file:LICENSE.rdoc} for details.
|
12
|
+
class StorageRepository < Moab::StorageRepository
|
13
|
+
|
14
|
+
# @param object_id [String] The identifier of the digital object whose version is desired
|
15
|
+
# @return [Pathname] The location of the desired object's home directory
|
16
|
+
def storage_object_pathname(object_id)
|
17
|
+
case Moab::Config.path_method.to_s
|
18
|
+
when 'druid_tree'
|
19
|
+
repository_home.join(druid_tree(object_id))
|
20
|
+
when 'druid'
|
21
|
+
repository_home.join(object_id.split(/:/)[-1])
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# @param object_id [String] The identifier of the digital object whose path is requested
|
26
|
+
# @return [String] the druid tree directory path based on the given object identifier.
|
27
|
+
def druid_tree(object_id)
|
28
|
+
syntax_msg = "Identifier has invalid suri syntax: #{object_id}"
|
29
|
+
raise syntax_msg + "nil or empty" if object_id.to_s.empty?
|
30
|
+
identifier = object_id.split(':')[-1]
|
31
|
+
raise syntax_msg if identifier.to_s.empty?
|
32
|
+
# The object identifier must be in the SURI format, otherwise an exception is raised:
|
33
|
+
# e.g. druid:aannnaannnn or aannnaannnn
|
34
|
+
# where 'a' is an alphabetic character
|
35
|
+
# where 'n' is a numeric character
|
36
|
+
if identifier =~ /^([a-z]{2})(\d{3})([a-z]{2})(\d{4})$/
|
37
|
+
return File.join( $1, $2, $3, $4, identifier)
|
38
|
+
else
|
39
|
+
raise syntax_msg
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'moab_stanford'
|
2
|
+
|
3
|
+
module Stanford
|
4
|
+
|
5
|
+
# An interface class to support access to SDR storage via a RESTful server
|
6
|
+
class StorageServices < Moab::StorageServices
|
7
|
+
|
8
|
+
# @return [StorageRepository] an instance of the interface to SDR storage
|
9
|
+
@@repository = Stanford::StorageRepository.new
|
10
|
+
|
11
|
+
# @param new_content_metadata [String] The content metadata to be compared to the base
|
12
|
+
# @param object_id [String] The digital object identifier of the object whose version inventory is the basis of the comparison
|
13
|
+
# @param subset [String] Speciifes which subset of files to list in the inventories extracted from the contentMetadata (all|preserve|publish|shelve)
|
14
|
+
# @param base_version [Integer] The ID of the version whose inventory is the basis of, if nil use latest version
|
15
|
+
# @return [FileInventoryDifference] The report of differences between the content metadata and the specified version
|
16
|
+
def self.compare_cm_to_version(new_content_metadata, object_id, subset, base_version=nil)
|
17
|
+
new_inventory = ContentInventory.new.inventory_from_cm(new_content_metadata, object_id, subset)
|
18
|
+
begin
|
19
|
+
# ObjectNotFoundException is raised if the object does not exist in storage
|
20
|
+
base_version ||= self.current_version(object_id)
|
21
|
+
# FileNotFoundException is raised if object exists but has no contentMetadata file
|
22
|
+
base_cm_pathname = self.retrieve_file('metadata', 'contentMetadata.xml', object_id, base_version)
|
23
|
+
base_inventory = ContentInventory.new.inventory_from_cm(base_cm_pathname.read, object_id, subset, base_version)
|
24
|
+
rescue Moab::ObjectNotFoundException, Moab::FileNotFoundException
|
25
|
+
# Create a skeletal FileInventory object, containing no file entries
|
26
|
+
storage_object = StorageObject.new(object_id, 'dummy')
|
27
|
+
base_version = StorageObjectVersion.new(storage_object,0)
|
28
|
+
base_inventory = base_version.file_inventory('version')
|
29
|
+
end
|
30
|
+
diff = FileInventoryDifference.new.compare(base_inventory, new_inventory)
|
31
|
+
metadata_diff = diff.group_difference('metadata')
|
32
|
+
diff.group_differences.delete(metadata_diff) if metadata_diff
|
33
|
+
diff
|
34
|
+
end
|
35
|
+
|
36
|
+
# @param new_content_metadata [String] The content metadata to be compared to the current signtature catalog
|
37
|
+
# @param object_id [String] The digital object identifier of the object whose signature catalog is to be used
|
38
|
+
# @param version_id [Integer] The ID of the version whose signature catalog is to be used, if nil use latest version
|
39
|
+
# @return [FileInventory] The versionAddtions report showing which files are new or modified in the content metadata
|
40
|
+
def self.cm_version_additions(new_content_metadata, object_id, version_id=nil)
|
41
|
+
new_inventory = ContentInventory.new.inventory_from_cm(new_content_metadata, object_id, 'preserve')
|
42
|
+
begin
|
43
|
+
# ObjectNotFoundException is raised if the object does not exist in storage
|
44
|
+
version_id ||= self.current_version(object_id)
|
45
|
+
storage_object_version = @@repository.storage_object(object_id).find_object_version(version_id)
|
46
|
+
signature_catalog = storage_object_version.signature_catalog
|
47
|
+
rescue Moab::ObjectNotFoundException
|
48
|
+
storage_object = StorageObject.new(object_id, 'dummy')
|
49
|
+
base_version = StorageObjectVersion.new(storage_object,0)
|
50
|
+
signature_catalog = base_version.signature_catalog
|
51
|
+
end
|
52
|
+
signature_catalog.version_additions(new_inventory)
|
53
|
+
end
|
54
|
+
|
55
|
+
# @param object_id [String] The digital object identifier of the object whose contentMetadata is to be remediated
|
56
|
+
# @param version_id [Integer] The ID of the version whose file data is to be used, if nil use latest version
|
57
|
+
# @return [String] Returns a remediated copy of the contentMetadata with fixity data filled in
|
58
|
+
def self.cm_remediate(object_id, version_id=nil)
|
59
|
+
cm = self.retrieve_file('metadata', 'contentMetadata.xml', object_id, version_id)
|
60
|
+
group = self.retrieve_file_group('content', object_id, version_id)
|
61
|
+
ContentInventory.new.remediate_content_metadata(cm,group)
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
data/lib/tasks/yard.rake
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
desc "Generate RDoc"
|
2
|
+
task :doc => ['doc:generate']
|
3
|
+
|
4
|
+
namespace :doc do
|
5
|
+
project_root = File.expand_path(File.join(File.dirname(__FILE__), '..', '..'))
|
6
|
+
doc_destination = File.join(project_root, 'doc')
|
7
|
+
|
8
|
+
begin
|
9
|
+
require 'yard'
|
10
|
+
require 'yard/rake/yardoc_task'
|
11
|
+
|
12
|
+
YARD::Rake::YardocTask.new(:generate) do |yt|
|
13
|
+
yt.files = Dir.glob(File.join(project_root, 'lib', '*.rb')) +
|
14
|
+
Dir.glob(File.join(project_root, 'lib', 'serializer', '*.rb')) +
|
15
|
+
Dir.glob(File.join(project_root, 'lib', 'moab', '*.rb')) +
|
16
|
+
Dir.glob(File.join(project_root, 'lib', 'stanford', '*.rb')) +
|
17
|
+
['-'] +
|
18
|
+
[ File.join(project_root, 'LICENSE.rdoc') ]
|
19
|
+
|
20
|
+
yt.options = ['--output-dir', doc_destination, '--hide-void-return']
|
21
|
+
end
|
22
|
+
rescue LoadError
|
23
|
+
desc "Generate YARD Documentation"
|
24
|
+
task :generate do
|
25
|
+
abort "Please install the YARD gem to generate rdoc."
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
desc "Remove generated documentation"
|
30
|
+
task :clean do
|
31
|
+
rm_r doc_destination if File.exists?(doc_destination)
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,396 @@
|
|
1
|
+
#$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', '..', 'lib'))
|
2
|
+
require 'rubygems'
|
3
|
+
require 'hashery/orderedhash'
|
4
|
+
require 'pathname'
|
5
|
+
require 'yard'
|
6
|
+
include YARD
|
7
|
+
require 'moab_stanford'
|
8
|
+
include Serializer
|
9
|
+
include Moab
|
10
|
+
include Stanford
|
11
|
+
|
12
|
+
class String
|
13
|
+
def snake_case
|
14
|
+
self.gsub(/::/, '/').
|
15
|
+
gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2').
|
16
|
+
gsub(/([a-z\d])([A-Z])/, '\1_\2').
|
17
|
+
tr("-", "_").
|
18
|
+
downcase
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class Symbol
|
23
|
+
def snake_case
|
24
|
+
self.to_s.snake_case
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class ApiClass
|
29
|
+
attr_accessor :class_name
|
30
|
+
attr_accessor :yard_class
|
31
|
+
attr_accessor :mhash
|
32
|
+
attr_accessor :ruby_class
|
33
|
+
attr_accessor :xml_tag
|
34
|
+
attr_accessor :description
|
35
|
+
attr_accessor :model
|
36
|
+
|
37
|
+
def self.class_hash=(class_hash)
|
38
|
+
@@class_hash = class_hash
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.rootpath=(rootpath)
|
42
|
+
@@rootpath = rootpath
|
43
|
+
end
|
44
|
+
|
45
|
+
def initialize(class_name)
|
46
|
+
@class_name = class_name
|
47
|
+
@yard_class = @@class_hash[class_name]
|
48
|
+
if yard_class.nil?
|
49
|
+
raise "class_hash[#{class_name}] is Nil"
|
50
|
+
end
|
51
|
+
@mhash = categorize_members(@yard_class)
|
52
|
+
if @yard_class.path.include?('::')
|
53
|
+
ruby_module = Object::const_get(yard_class.path.split(/::/)[0])
|
54
|
+
@ruby_class = ruby_module.const_get(yard_class.name)
|
55
|
+
else
|
56
|
+
@ruby_class = Object::const_get(yard_class.path)
|
57
|
+
end
|
58
|
+
|
59
|
+
if @ruby_class.respond_to?(:tag_name)
|
60
|
+
@xml_tag = "<#{ruby_class.tag_name}>"
|
61
|
+
else
|
62
|
+
@xml_tag = '-'
|
63
|
+
end
|
64
|
+
docstring_all = confluence_translate(yard_class.docstring.all.split(/@/)[0])
|
65
|
+
docstring_parts = docstring_all.split(/h4. Data Model\n/)
|
66
|
+
@description = docstring_parts[0]
|
67
|
+
@model = docstring_parts.size > 1 ? docstring_parts[1] : nil
|
68
|
+
end
|
69
|
+
|
70
|
+
def categorize_members(yard_class)
|
71
|
+
mhash = {
|
72
|
+
:class_attributes => OrderedHash.new,
|
73
|
+
:instance_attributes => OrderedHash.new,
|
74
|
+
:class_methods => Array.new,
|
75
|
+
:instance_methods => Array.new
|
76
|
+
}
|
77
|
+
yard_class.children.each do |member|
|
78
|
+
attr_symbol = member.name.to_s.gsub(/=$/, '').to_sym
|
79
|
+
if member.name == :initialize
|
80
|
+
mhash[:constructor] = member
|
81
|
+
elsif yard_class.class_attributes[attr_symbol]
|
82
|
+
mhash[:class_attributes][attr_symbol] = yard_class.class_attributes[attr_symbol]
|
83
|
+
elsif yard_class.instance_attributes[attr_symbol]
|
84
|
+
mhash[:instance_attributes][attr_symbol] = yard_class.instance_attributes[attr_symbol]
|
85
|
+
elsif member.scope == :class
|
86
|
+
mhash[:class_methods] << member
|
87
|
+
elsif member.scope == :instance
|
88
|
+
mhash[:instance_methods] << member
|
89
|
+
end
|
90
|
+
end
|
91
|
+
mhash
|
92
|
+
end
|
93
|
+
|
94
|
+
def title
|
95
|
+
title = "\n{anchor:#{yard_class.name}}\n"
|
96
|
+
title << "h3. Class #{yard_class.path}"
|
97
|
+
title
|
98
|
+
end
|
99
|
+
|
100
|
+
def description
|
101
|
+
description = "\nh4. Description\n\n"
|
102
|
+
description << @description
|
103
|
+
description
|
104
|
+
end
|
105
|
+
|
106
|
+
def model
|
107
|
+
model = "\nh4. Data Model\n\n"
|
108
|
+
model << @model
|
109
|
+
model
|
110
|
+
end
|
111
|
+
|
112
|
+
def xml_example
|
113
|
+
xml_example = String.new
|
114
|
+
if yard_class.docstring.has_tag?('example')
|
115
|
+
xml_example << "\nh4. XML Example\n"
|
116
|
+
xml_example << "{code:lang=xml}\n"
|
117
|
+
filename = yard_class.docstring.tag('example').name.split(/[:)}]/)[2]
|
118
|
+
example = IO.read(File.join(@@rootpath, filename))
|
119
|
+
xml_example << example
|
120
|
+
xml_example << "{code}\n"
|
121
|
+
end
|
122
|
+
xml_example
|
123
|
+
end
|
124
|
+
|
125
|
+
def instance_attributes_table(mode=:all)
|
126
|
+
table = String.new
|
127
|
+
table << "\\\\\n||XML Element||Ruby Class||Inherits From||\n"
|
128
|
+
table << "|#{xml_tag}|[##{class_name}]|#{yard_class.superclass}|\n"
|
129
|
+
table << "\\\\\n||XML Child Node||Ruby Attribute||Data Type||Description||\n"
|
130
|
+
xml_names = xml_name_hash
|
131
|
+
@mhash[:instance_attributes].values.each do |attribute|
|
132
|
+
read = attribute[:read]
|
133
|
+
return_tag = read.docstring.tag(:return)
|
134
|
+
ruby_name = read.name.to_s
|
135
|
+
xml_name = xml_names[ruby_name] ? xml_names[ruby_name] : "-"
|
136
|
+
data_type = return_tag.types[0]
|
137
|
+
description = confluence_translate(return_tag.text.gsub(/\n/, ' '))
|
138
|
+
table_row = "|#{xml_name}|#{ruby_name}|#{data_type}|#{description.gsub(/\|/, '\\|')}|\n"
|
139
|
+
case mode
|
140
|
+
when :xml
|
141
|
+
if xml_name != '-'
|
142
|
+
table << table_row
|
143
|
+
end
|
144
|
+
when :ruby
|
145
|
+
if xml_name == '-'
|
146
|
+
table << table_row
|
147
|
+
end
|
148
|
+
else
|
149
|
+
table << table_row
|
150
|
+
end
|
151
|
+
end
|
152
|
+
table
|
153
|
+
end
|
154
|
+
|
155
|
+
def xml_name_hash
|
156
|
+
xml_name_hash = Hash.new
|
157
|
+
if @ruby_class.respond_to?(:attributes)
|
158
|
+
@ruby_class.attributes.each do |attribute|
|
159
|
+
xml_name_hash[attribute.name.to_s] = "@#{attribute.tag}"
|
160
|
+
end
|
161
|
+
end
|
162
|
+
if @ruby_class.respond_to?(:elements)
|
163
|
+
@ruby_class.elements.each do |element|
|
164
|
+
if element.options.size == 0 or element.options[:single]
|
165
|
+
xml_name_hash[element.name.to_s] = "<#{element.tag}>"
|
166
|
+
else
|
167
|
+
xml_name_hash[element.name.to_s] = "<#{element.tag}> \\[1..\\*]"
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
xml_name_hash
|
172
|
+
end
|
173
|
+
|
174
|
+
def methods_documentation
|
175
|
+
|
176
|
+
methods_documentation = String.new
|
177
|
+
|
178
|
+
#methods_documentation ""
|
179
|
+
#methods_documentation "h4. Constructor"
|
180
|
+
#method = mhash[:constructor]
|
181
|
+
#methods_documentation_method(method, yard_class) if method
|
182
|
+
|
183
|
+
methods =mhash[:class_methods]
|
184
|
+
if methods.size > 0
|
185
|
+
methods_documentation << "\nh4. Class Methods\n"
|
186
|
+
methods.each do |method|
|
187
|
+
methods_documentation << method_documentation(method)
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
methods =mhash[:instance_methods]
|
192
|
+
if methods.size > 0
|
193
|
+
public_methods = Array.new
|
194
|
+
methods.each do |method|
|
195
|
+
if method.docstring.has_tag?(:api) && method.docstring.tag(:api).text == 'external'
|
196
|
+
public_methods << method
|
197
|
+
end
|
198
|
+
end
|
199
|
+
if public_methods.size > 0
|
200
|
+
methods_documentation << "\nh4. Instance Methods\n"
|
201
|
+
public_methods.each do |method|
|
202
|
+
methods_documentation << method_documentation(method)
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
methods_documentation
|
207
|
+
end
|
208
|
+
|
209
|
+
def method_documentation(method)
|
210
|
+
|
211
|
+
method_documentation = String.new
|
212
|
+
if method.nil?
|
213
|
+
raise "method is nil"
|
214
|
+
end
|
215
|
+
method_documentation << "\nh5. #{method.path}\n"
|
216
|
+
|
217
|
+
method_documentation << "||Method||Return Type||Description||\n"
|
218
|
+
if method.name == :initialize
|
219
|
+
return_type = @yard_class.name
|
220
|
+
description = 'constructor'
|
221
|
+
else
|
222
|
+
return_tag = method.docstring.tag(:return)
|
223
|
+
if return_tag.nil?
|
224
|
+
raise "#{method.name} return tag is nil"
|
225
|
+
end
|
226
|
+
return_type = return_tag.types[0]
|
227
|
+
description = confluence_translate(return_tag.text.gsub(/\n/, ' '))
|
228
|
+
end
|
229
|
+
method_documentation << "|#{method.name}|#{return_type}|#{example.description}|\n"
|
230
|
+
|
231
|
+
if method.respond_to?(:docstring)
|
232
|
+
params = method.docstring.tags(:param)
|
233
|
+
if params && params.size > 0
|
234
|
+
method_documentation << "\n||Parameter||Data Type||Description||\n"
|
235
|
+
params.each do |p|
|
236
|
+
description = confluence_translate(p.text.gsub(/\n/, ' '))
|
237
|
+
method_documentation << "|#{p.name}|#{p.types.join(', ')}|#{example.description}|\n"
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
method_documentation << "{code:lang=none|title=Ruby Source Code}\n"
|
243
|
+
method_documentation << method.source
|
244
|
+
method_documentation << "{code}\n"
|
245
|
+
|
246
|
+
if method.docstring.has_tag?('example')
|
247
|
+
method_documentation << "\n{code:lang=none|title=Usage Example}\n"
|
248
|
+
filename = method.docstring.tag('example').name.split(/[:)}]/)[2]
|
249
|
+
example = IO.read(File.join(@@rootpath, filename))
|
250
|
+
method_documentation << example
|
251
|
+
method_documentation << "{code}\n"
|
252
|
+
end
|
253
|
+
method_documentation
|
254
|
+
end
|
255
|
+
|
256
|
+
def confluence_translate(input)
|
257
|
+
map = OrderedHash.new
|
258
|
+
map[/\|/] = "\\|"
|
259
|
+
map[/====/] = "h4. "
|
260
|
+
map[/\*/] = "\\*"
|
261
|
+
map[/\n\s{6}\\\*\s/] = "\n****\s"
|
262
|
+
map[/\n\s{4}\\\*\s/] = "\n***\s"
|
263
|
+
map[/\n\s{2}\\\*\s/] = "\n**\s"
|
264
|
+
map[/\n\\\*\s/] = "\n*\s"
|
265
|
+
map[/<[\/]*b>/] = "*"
|
266
|
+
map[/<[\/]*i>/] = "_"
|
267
|
+
map[/\[/] = "\\["
|
268
|
+
map[/\{#/] = "[#"
|
269
|
+
map[/\{http/] = "[http"
|
270
|
+
map[/\{/] = "[#"
|
271
|
+
map[/\}/] = "]"
|
272
|
+
output = input
|
273
|
+
map.each do |regex, replacement|
|
274
|
+
output.gsub!(regex, replacement)
|
275
|
+
end
|
276
|
+
output
|
277
|
+
end
|
278
|
+
|
279
|
+
end
|
280
|
+
|
281
|
+
|
282
|
+
class ApiDoc < ApiClass
|
283
|
+
attr_accessor :ios
|
284
|
+
attr_accessor :classes
|
285
|
+
|
286
|
+
def process_doc(ios)
|
287
|
+
@ios = ios
|
288
|
+
parse_model
|
289
|
+
|
290
|
+
output component
|
291
|
+
output model
|
292
|
+
output description
|
293
|
+
output xml_example
|
294
|
+
output xml_nodes
|
295
|
+
classes_detail
|
296
|
+
end
|
297
|
+
|
298
|
+
def component
|
299
|
+
component = "h2. Component: #{yard_class.name}\n"
|
300
|
+
component
|
301
|
+
end
|
302
|
+
|
303
|
+
def xml_nodes
|
304
|
+
nodes = "\nh4. XML Nodes\n"
|
305
|
+
@classes.each do |cls|
|
306
|
+
nodes << cls.instance_attributes_table(mode=:xml)
|
307
|
+
end
|
308
|
+
nodes
|
309
|
+
end
|
310
|
+
|
311
|
+
def classes_detail
|
312
|
+
@classes.each do |cls|
|
313
|
+
output cls.title
|
314
|
+
output cls.description if cls != self
|
315
|
+
output cls.methods_documentation
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
def parse_model
|
320
|
+
@classes = Array.new
|
321
|
+
@classes << self
|
322
|
+
if @model
|
323
|
+
model.lines.each do |line|
|
324
|
+
matches = line.scan(/\[#(.*)?\]/)
|
325
|
+
if matches.size > 0
|
326
|
+
match = matches[0][0].split(/\]/)[0]
|
327
|
+
if match != class_name
|
328
|
+
@classes << ApiClass.new(match)
|
329
|
+
end
|
330
|
+
end
|
331
|
+
end
|
332
|
+
end
|
333
|
+
|
334
|
+
end
|
335
|
+
|
336
|
+
def output(string)
|
337
|
+
@ios.puts string
|
338
|
+
end
|
339
|
+
|
340
|
+
|
341
|
+
end
|
342
|
+
|
343
|
+
|
344
|
+
class DocGenerator
|
345
|
+
|
346
|
+
def initialize(rootpath)
|
347
|
+
@rootpath = rootpath
|
348
|
+
end
|
349
|
+
|
350
|
+
def generate_docs(apis)
|
351
|
+
temp_pathname = Pathname(@rootpath).join('api', 'temp')
|
352
|
+
ApiClass.class_hash = get_class_hash
|
353
|
+
ApiClass.rootpath = @rootpath
|
354
|
+
apis.each do |api_name|
|
355
|
+
doc_pathname = temp_pathname.join(api_name + "_confluence.txt")
|
356
|
+
doc_pathname.parent.mkpath
|
357
|
+
#puts doc_pathname.to_s
|
358
|
+
#unless doc_pathname.exist?
|
359
|
+
begin
|
360
|
+
@constructor_params = Array.new
|
361
|
+
@indent = 0
|
362
|
+
ios = doc_pathname.open("w")
|
363
|
+
api_doc = ApiDoc.new(api_name)
|
364
|
+
api_doc.process_doc(ios)
|
365
|
+
ensure
|
366
|
+
ios.close
|
367
|
+
end
|
368
|
+
#end
|
369
|
+
end
|
370
|
+
end
|
371
|
+
|
372
|
+
def get_class_hash()
|
373
|
+
class_hash = Hash.new
|
374
|
+
yardoc = File.join(@rootpath, '.yardoc')
|
375
|
+
Registry.load!(yardoc) # loads all objects into memory
|
376
|
+
class_array = Registry.all(:class) # Array
|
377
|
+
class_array.each do |cls|
|
378
|
+
class_hash[cls.name.to_s] = cls
|
379
|
+
end
|
380
|
+
class_hash
|
381
|
+
end
|
382
|
+
|
383
|
+
end
|
384
|
+
|
385
|
+
apis = Array.new
|
386
|
+
apis << 'FileInventory'
|
387
|
+
apis << 'SignatureCatalog'
|
388
|
+
apis << 'FileInventoryDifference'
|
389
|
+
apis << 'VersionMetadata'
|
390
|
+
apis << 'Serializable'
|
391
|
+
apis << 'StorageObject'
|
392
|
+
apis << 'StorageRepository'
|
393
|
+
apis << 'DorMetadata'
|
394
|
+
|
395
|
+
sg = DocGenerator.new(File.expand_path(File.join(File.dirname(__FILE__), '..', '..')))
|
396
|
+
sg.generate_docs(apis)
|