moab-versioning 2.1.0 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/moab/bagger.rb +6 -3
- data/lib/moab/file_group.rb +1 -3
- data/lib/moab/file_group_difference.rb +9 -9
- data/lib/moab/file_instance_difference.rb +3 -2
- data/lib/moab/file_inventory.rb +11 -8
- data/lib/moab/file_signature.rb +1 -1
- data/lib/moab/storage_object.rb +13 -8
- data/lib/moab/storage_object_version.rb +3 -4
- data/lib/moab/storage_repository.rb +5 -8
- data/lib/stanford/content_inventory.rb +2 -1
- data/lib/stanford/dor_metadata.rb +2 -5
- data/lib/stanford/storage_repository.rb +10 -16
- metadata +21 -10
- data/lib/tools/api_doc_generator.rb +0 -395
- data/lib/tools/spec_generator.rb +0 -409
- data/lib/tools/spec_generator_old.rb +0 -49
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8a0b643706a4149747c76abbbbdb8458a73c62ed
|
4
|
+
data.tar.gz: 485960168209b4768a76deb35fbd56360a8b32e3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 26f4fee9182c007e5c68fdef45a53fbb75330dfcdc9ed39e02e719a095a1a4d684adbb2ab7ff049fa15631cf1e72711f78b3b6ee2633a99f31c264f2b77ddb05
|
7
|
+
data.tar.gz: 31c2bc1b8120546686d6952b06ab2b20b046d4f010a829bb546bc3c4669832ac15ef627b388dd4414e6c59e2628b1737f7a1c2dc1d07c37c3d26db9f365babea
|
data/lib/moab/bagger.rb
CHANGED
@@ -90,9 +90,11 @@ module Moab
|
|
90
90
|
end
|
91
91
|
|
92
92
|
# @api external
|
93
|
-
# @param package_mode [Symbol] The operational mode controlling what gets bagged and the full path of
|
93
|
+
# @param package_mode [Symbol] The operational mode controlling what gets bagged and the full path of
|
94
|
+
# source files (Bagger#fill_payload)
|
94
95
|
# @param source_base_pathname [Pathname] The home location of the source files
|
95
|
-
# @return [Bagger] Perform all the operations required to fill the bag payload, write the manifests and
|
96
|
+
# @return [Bagger] Perform all the operations required to fill the bag payload, write the manifests and
|
97
|
+
# tagfiles, and checksum the tagfiles
|
96
98
|
# @example {include:file:spec/features/storage/deposit_spec.rb}
|
97
99
|
def fill_bag(package_mode, source_base_pathname)
|
98
100
|
create_bag_inventory(package_mode)
|
@@ -102,7 +104,8 @@ module Moab
|
|
102
104
|
end
|
103
105
|
|
104
106
|
# @api external
|
105
|
-
# @param package_mode [Symbol] The operational mode controlling what gets bagged and the full path of
|
107
|
+
# @param package_mode [Symbol] The operational mode controlling what gets bagged and the full path of
|
108
|
+
# source files (Bagger#fill_payload)
|
106
109
|
# @return [FileInventory] Create, write, and return the inventory of the files that will become the payload
|
107
110
|
def create_bag_inventory(package_mode)
|
108
111
|
@package_mode = package_mode
|
data/lib/moab/file_group.rb
CHANGED
@@ -155,11 +155,10 @@ module Moab
|
|
155
155
|
|
156
156
|
# @return [Pathname] The full path used as the basis of the relative paths reported
|
157
157
|
# in {FileInstance} objects that are children of the {FileManifestation} objects contained in this file group
|
158
|
-
attr_accessor :base_directory
|
159
|
-
|
160
158
|
def base_directory=(basepath)
|
161
159
|
@base_directory = Pathname.new(basepath).expand_path
|
162
160
|
end
|
161
|
+
attr_reader :base_directory
|
163
162
|
|
164
163
|
# @api internal
|
165
164
|
# @param pathname [Pathname] The file path to be tested
|
@@ -241,4 +240,3 @@ module Moab
|
|
241
240
|
end
|
242
241
|
|
243
242
|
end
|
244
|
-
|
@@ -63,7 +63,8 @@ module Moab
|
|
63
63
|
attribute :group_id, String, :tag => 'groupId', :key => true
|
64
64
|
|
65
65
|
# @attribute
|
66
|
-
# @return [Integer] the total number of differences found between the two inventories that were
|
66
|
+
# @return [Integer] the total number of differences found between the two inventories that were
|
67
|
+
# compared (dynamically calculated)
|
67
68
|
attribute :difference_count, Integer, :tag => 'differenceCount', :on_save => Proc.new { |i| i.to_s }
|
68
69
|
|
69
70
|
def difference_count
|
@@ -263,14 +264,13 @@ module Moab
|
|
263
264
|
fid.basis_path = basis_only_paths[n]
|
264
265
|
fid.other_path = other_only_paths[n]
|
265
266
|
fid.signatures << signature
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
fid.change = 'renamed'
|
267
|
+
if fid.basis_path.nil?
|
268
|
+
fid.change = 'copyadded'
|
269
|
+
fid.basis_path = basis_paths[0]
|
270
|
+
elsif fid.other_path.nil?
|
271
|
+
fid.change = 'copydeleted'
|
272
|
+
else
|
273
|
+
fid.change = 'renamed'
|
274
274
|
end
|
275
275
|
@subset_hash[fid.change.to_sym].files << fid
|
276
276
|
end
|
@@ -5,7 +5,8 @@ module Moab
|
|
5
5
|
# A container for recording difference information at the file level
|
6
6
|
# * If there was no change, the change type is set to <i>identical</i>
|
7
7
|
# * If the signature is unchanged, but the path has moved, the change type is set to <i>renamed</i>
|
8
|
-
# * If path is unchanged, but the signature has changed, the change type is set to <i>modified</i> and
|
8
|
+
# * If path is unchanged, but the signature has changed, the change type is set to <i>modified</i> and
|
9
|
+
# both signatures are reported
|
9
10
|
# * If the signature and path are only in the basis inventory, the change type is set to <i>deleted</i>
|
10
11
|
# * If the signature and path are only in the other inventory, the change type is set to <i>added</i>
|
11
12
|
# This is a child element of {FileGroupDifferenceSubset}, which is in turn a descendent of {FileInventoryDifference},
|
@@ -51,4 +52,4 @@ module Moab
|
|
51
52
|
|
52
53
|
end
|
53
54
|
|
54
|
-
end
|
55
|
+
end
|
data/lib/moab/file_inventory.rb
CHANGED
@@ -134,9 +134,11 @@ module Moab
|
|
134
134
|
# @return [FileSignature] The signature of the specified file
|
135
135
|
def file_signature(group_id, file_id)
|
136
136
|
file_group = group(group_id)
|
137
|
-
|
137
|
+
errmsg = "group #{group_id} not found for #{@digital_object_id} - #{@version_id}"
|
138
|
+
raise FileNotFoundException, errmsg if file_group.nil?
|
138
139
|
file_signature = file_group.path_hash[file_id]
|
139
|
-
|
140
|
+
errmsg = "#{group_id} file #{file_id} not found for #{@digital_object_id} - #{@version_id}"
|
141
|
+
raise FileNotFoundException, errmsg if file_signature.nil?
|
140
142
|
file_signature
|
141
143
|
end
|
142
144
|
|
@@ -182,19 +184,20 @@ module Moab
|
|
182
184
|
# if nil, then the directory is assumed to contain both content and metadata subdirectories
|
183
185
|
# @return [FileInventory] Traverse a directory and return an inventory of the files it contains
|
184
186
|
# @example {include:file:spec/features/inventory/harvest_inventory_spec.rb}
|
185
|
-
def inventory_from_directory(data_dir,group_id=nil)
|
187
|
+
def inventory_from_directory(data_dir, group_id=nil)
|
186
188
|
if group_id
|
187
|
-
@groups << FileGroup.new(:group_id
|
189
|
+
@groups << FileGroup.new(group_id: group_id).group_from_directory(data_dir)
|
188
190
|
else
|
189
|
-
['content','metadata'].each do |
|
190
|
-
@groups << FileGroup.new(:
|
191
|
+
['content', 'metadata'].each do |gid|
|
192
|
+
@groups << FileGroup.new(group_id: gid).group_from_directory(Pathname(data_dir).join(gid))
|
191
193
|
end
|
192
194
|
end
|
193
195
|
self
|
194
196
|
end
|
195
197
|
|
196
198
|
# @param bag_dir [Pathname,String] The location of the BagIt bag to be inventoried
|
197
|
-
# @return [FileInventory] Traverse a BagIt bag's payload and return an inventory of the files it contains
|
199
|
+
# @return [FileInventory] Traverse a BagIt bag's payload and return an inventory of the files it contains
|
200
|
+
# (using fixity from bag manifest files)
|
198
201
|
def inventory_from_bagit_bag(bag_dir)
|
199
202
|
bag_pathname = Pathname(bag_dir)
|
200
203
|
signatures_from_bag = signatures_from_bagit_manifests(bag_pathname)
|
@@ -261,7 +264,7 @@ module Moab
|
|
261
264
|
when "directory"
|
262
265
|
'directoryInventory.xml'
|
263
266
|
else
|
264
|
-
raise "unknown inventory type: #{type.to_s}"
|
267
|
+
raise ArgumentError, "unknown inventory type: #{type.to_s}"
|
265
268
|
end
|
266
269
|
end
|
267
270
|
|
data/lib/moab/file_signature.rb
CHANGED
data/lib/moab/storage_object.rb
CHANGED
@@ -115,7 +115,8 @@ module Moab
|
|
115
115
|
# @return [Pathname] The absolute storage path of the file, including the object's home directory
|
116
116
|
def storage_filepath(catalog_filepath)
|
117
117
|
storage_filepath = @object_pathname.join(catalog_filepath)
|
118
|
-
|
118
|
+
errmsg = "#{catalog_filepath} missing from storage location #{storage_filepath}"
|
119
|
+
raise FileNotFoundException, errmsg unless storage_filepath.exist?
|
119
120
|
storage_filepath
|
120
121
|
end
|
121
122
|
|
@@ -132,7 +133,7 @@ module Moab
|
|
132
133
|
return list unless @object_pathname.exist?
|
133
134
|
@object_pathname.children.each do |dirname|
|
134
135
|
vnum = dirname.basename.to_s
|
135
|
-
if vnum
|
136
|
+
if vnum =~ /^v(\d+)$/
|
136
137
|
list << vnum[1..-1].to_i
|
137
138
|
end
|
138
139
|
end
|
@@ -153,11 +154,7 @@ module Moab
|
|
153
154
|
# @api external
|
154
155
|
# @return [Integer] The identifier of the latest version of this object, or 0 if no versions exist
|
155
156
|
def current_version_id
|
156
|
-
|
157
|
-
#@current_version_id = self.version_id_list.last || 0
|
158
|
-
list = self.version_id_list
|
159
|
-
version_id = list.empty? ? 0 : list.last
|
160
|
-
@current_version_id = version_id
|
157
|
+
@current_version_id ||= self.version_id_list.last || 0
|
161
158
|
end
|
162
159
|
|
163
160
|
# @return [StorageObjectVersion] The most recent version in the storage object
|
@@ -218,7 +215,7 @@ module Moab
|
|
218
215
|
# @return [Boolean] Restore all recovered versions to online storage and verify results
|
219
216
|
def restore_object(recovery_path)
|
220
217
|
timestamp = Time.now
|
221
|
-
recovery_object = StorageObject.new(@digital_object_id, recovery_path,
|
218
|
+
recovery_object = StorageObject.new(@digital_object_id, recovery_path, false)
|
222
219
|
recovery_object.versions.each do |recovery_version|
|
223
220
|
version_id = recovery_version.version_id
|
224
221
|
storage_version = self.storage_object_version(version_id)
|
@@ -230,6 +227,14 @@ module Moab
|
|
230
227
|
self
|
231
228
|
end
|
232
229
|
|
230
|
+
# @return [Integer] the size occupied on disk by the storage object, in bytes. this is the entire moab (all versions).
|
231
|
+
def size
|
232
|
+
size = 0
|
233
|
+
Find.find(object_pathname) do |path|
|
234
|
+
size += FileTest.size(path) unless FileTest.directory?(path)
|
235
|
+
end
|
236
|
+
size
|
237
|
+
end
|
233
238
|
end
|
234
239
|
|
235
240
|
end
|
@@ -204,8 +204,9 @@ module Moab
|
|
204
204
|
:type=>'manifests',
|
205
205
|
:digital_object_id=>@storage_object.digital_object_id,
|
206
206
|
:version_id=>@version_id)
|
207
|
-
|
208
|
-
manifest_inventory.
|
207
|
+
pathname = @version_pathname.join('manifests')
|
208
|
+
manifest_inventory.groups << FileGroup.new(:group_id=>'manifests').group_from_directory(pathname, false)
|
209
|
+
manifest_inventory.write_xml_file(pathname)
|
209
210
|
end
|
210
211
|
|
211
212
|
# @return [VerificationResult] return result of testing correctness of version manifests
|
@@ -330,7 +331,5 @@ module Moab
|
|
330
331
|
@version_pathname.rename(demote_pathame)
|
331
332
|
end
|
332
333
|
end
|
333
|
-
|
334
334
|
end
|
335
|
-
|
336
335
|
end
|
@@ -15,8 +15,9 @@ module Moab
|
|
15
15
|
# All rights reserved. See {file:LICENSE.rdoc} for details.
|
16
16
|
class StorageRepository
|
17
17
|
|
18
|
-
#Note that Moab::Config is not initialized from environment config file until after
|
19
|
-
#
|
18
|
+
# Note that Moab::Config is not initialized from environment config file until after
|
19
|
+
# this object is initialized by StorageServices
|
20
|
+
# (see sequence of requires in spec_helper.rb and in applications that use)
|
20
21
|
|
21
22
|
# @return [Array<Pathname>] The list of filesystem root paths in which objects are stored
|
22
23
|
def storage_roots
|
@@ -45,7 +46,7 @@ module Moab
|
|
45
46
|
# split a object ID into 2-character segments, followed by a copy of the object ID
|
46
47
|
# for a more sophisticated pairtree implementation see https://github.com/microservices/pairtree
|
47
48
|
path_segments = object_id.scan(/..?/) << object_id
|
48
|
-
path_segments.join(File::SEPARATOR).
|
49
|
+
path_segments.join(File::SEPARATOR).tr(':', '_')
|
49
50
|
end
|
50
51
|
|
51
52
|
# @return [String] The trunk segment of the object deposit path
|
@@ -109,11 +110,7 @@ module Moab
|
|
109
110
|
storage_pathname = find_storage_object(object_id, include_deposit).object_pathname
|
110
111
|
size = 0
|
111
112
|
Find.find(storage_pathname) do |path|
|
112
|
-
|
113
|
-
Find.prune if File.basename(path)[0] == '.'
|
114
|
-
else
|
115
|
-
size += FileTest.size(path)
|
116
|
-
end
|
113
|
+
size += FileTest.size(path) unless FileTest.directory?(path)
|
117
114
|
end
|
118
115
|
size
|
119
116
|
end
|
@@ -20,7 +20,8 @@ module Stanford
|
|
20
20
|
# @return [FileInventory] The versionInventory equivalent of the contentMetadata
|
21
21
|
# if the supplied content_metadata is blank or empty, then a skeletal FileInventory will be returned
|
22
22
|
def inventory_from_cm(content_metadata, object_id, subset, version_id=nil)
|
23
|
-
# The contentMetadata datastream is not required for ingest, since some object types, such as collection
|
23
|
+
# The contentMetadata datastream is not required for ingest, since some object types, such as collection
|
24
|
+
# or APO do not require one.
|
24
25
|
# Many of these objects have contentMetadata with no child elements, such as this:
|
25
26
|
# <contentMetadata objectId="bd608mj3166" type="file"/>
|
26
27
|
# but there are also objects that have no datasteam of this name at all
|
@@ -33,7 +33,7 @@ module Stanford
|
|
33
33
|
# @return [FileInventory] Inventory of the files under the specified directory
|
34
34
|
def inventory_from_directory(directory, version_id=nil)
|
35
35
|
version_id ||= @version_id
|
36
|
-
version_inventory = Moab::FileInventory.new(:
|
36
|
+
version_inventory = Moab::FileInventory.new(type: 'version', digital_object_id: @digital_object_id, version_id: version_id)
|
37
37
|
content_metadata = IO.read(File.join(directory,'contentMetadata.xml'))
|
38
38
|
content_group = Stanford::ContentInventory.new.group_from_cm(content_metadata, 'preserve' )
|
39
39
|
version_inventory.groups << content_group
|
@@ -41,9 +41,6 @@ module Stanford
|
|
41
41
|
version_inventory.groups << metadata_group
|
42
42
|
version_inventory
|
43
43
|
end
|
44
|
-
|
45
|
-
|
46
|
-
|
47
44
|
end
|
48
45
|
|
49
|
-
end
|
46
|
+
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'moab/stanford'
|
2
|
+
require 'druid-tools'
|
2
3
|
|
3
4
|
module Stanford
|
4
5
|
|
@@ -15,10 +16,10 @@ module Stanford
|
|
15
16
|
# @return [String] The branch segment of the object storage path
|
16
17
|
def storage_branch(object_id)
|
17
18
|
case Moab::Config.path_method.to_s
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
19
|
+
when 'druid_tree'
|
20
|
+
druid_tree(object_id)
|
21
|
+
when 'druid'
|
22
|
+
object_id.split(/:/)[-1]
|
22
23
|
end
|
23
24
|
end
|
24
25
|
|
@@ -31,22 +32,15 @@ module Stanford
|
|
31
32
|
# @param object_id [String] The identifier of the digital object whose path is requested
|
32
33
|
# @return [String] the druid tree directory path based on the given object identifier.
|
33
34
|
def druid_tree(object_id)
|
35
|
+
# Note: this seems an odd place to do druid validation, but leaving it here for now
|
34
36
|
syntax_msg = "Identifier has invalid suri syntax: #{object_id}"
|
35
|
-
raise syntax_msg + "nil or empty" if object_id.to_s.empty?
|
37
|
+
raise syntax_msg + " nil or empty" if object_id.to_s.empty?
|
36
38
|
identifier = object_id.split(':')[-1]
|
37
|
-
raise syntax_msg if
|
38
|
-
|
39
|
-
|
40
|
-
# where 'a' is an alphabetic character
|
41
|
-
# where 'n' is a numeric character
|
42
|
-
if identifier =~ /^([a-z]{2})(\d{3})([a-z]{2})(\d{4})$/
|
43
|
-
return File.join( $1, $2, $3, $4, identifier)
|
44
|
-
else
|
45
|
-
raise syntax_msg
|
46
|
-
end
|
39
|
+
raise syntax_msg if identifier.to_s.empty?
|
40
|
+
raise syntax_msg unless DruidTools::Druid.valid?(identifier, true)
|
41
|
+
DruidTools::Druid.new(identifier, true).tree.join(File::SEPARATOR)
|
47
42
|
end
|
48
43
|
|
49
|
-
|
50
44
|
end
|
51
45
|
|
52
46
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: moab-versioning
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Darren Weber
|
@@ -11,7 +11,7 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date: 2017-
|
14
|
+
date: 2017-10-20 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: confstruct
|
@@ -83,6 +83,20 @@ dependencies:
|
|
83
83
|
- - ">="
|
84
84
|
- !ruby/object:Gem::Version
|
85
85
|
version: '0'
|
86
|
+
- !ruby/object:Gem::Dependency
|
87
|
+
name: druid-tools
|
88
|
+
requirement: !ruby/object:Gem::Requirement
|
89
|
+
requirements:
|
90
|
+
- - ">="
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
version: 1.0.0
|
93
|
+
type: :runtime
|
94
|
+
prerelease: false
|
95
|
+
version_requirements: !ruby/object:Gem::Requirement
|
96
|
+
requirements:
|
97
|
+
- - ">="
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
version: 1.0.0
|
86
100
|
- !ruby/object:Gem::Dependency
|
87
101
|
name: awesome_print
|
88
102
|
requirement: !ruby/object:Gem::Requirement
|
@@ -201,28 +215,28 @@ dependencies:
|
|
201
215
|
requirements:
|
202
216
|
- - "~>"
|
203
217
|
- !ruby/object:Gem::Version
|
204
|
-
version: 0.
|
218
|
+
version: 0.50.0
|
205
219
|
type: :development
|
206
220
|
prerelease: false
|
207
221
|
version_requirements: !ruby/object:Gem::Requirement
|
208
222
|
requirements:
|
209
223
|
- - "~>"
|
210
224
|
- !ruby/object:Gem::Version
|
211
|
-
version: 0.
|
225
|
+
version: 0.50.0
|
212
226
|
- !ruby/object:Gem::Dependency
|
213
227
|
name: rubocop-rspec
|
214
228
|
requirement: !ruby/object:Gem::Requirement
|
215
229
|
requirements:
|
216
230
|
- - "~>"
|
217
231
|
- !ruby/object:Gem::Version
|
218
|
-
version: 1.
|
232
|
+
version: 1.18.0
|
219
233
|
type: :development
|
220
234
|
prerelease: false
|
221
235
|
version_requirements: !ruby/object:Gem::Requirement
|
222
236
|
requirements:
|
223
237
|
- - "~>"
|
224
238
|
- !ruby/object:Gem::Version
|
225
|
-
version: 1.
|
239
|
+
version: 1.18.0
|
226
240
|
description: Contains classes to process digital object version content and metadata
|
227
241
|
email:
|
228
242
|
- darren.weber@stanford.edu
|
@@ -264,9 +278,6 @@ files:
|
|
264
278
|
- lib/stanford/storage_repository.rb
|
265
279
|
- lib/stanford/storage_services.rb
|
266
280
|
- lib/tasks/yard.rake
|
267
|
-
- lib/tools/api_doc_generator.rb
|
268
|
-
- lib/tools/spec_generator.rb
|
269
|
-
- lib/tools/spec_generator_old.rb
|
270
281
|
homepage: https://github.com/sul-dlss/moab-versioning
|
271
282
|
licenses:
|
272
283
|
- Apache-2.0
|
@@ -287,7 +298,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
287
298
|
version: 1.3.6
|
288
299
|
requirements: []
|
289
300
|
rubyforge_project:
|
290
|
-
rubygems_version: 2.
|
301
|
+
rubygems_version: 2.6.11
|
291
302
|
signing_key:
|
292
303
|
specification_version: 4
|
293
304
|
summary: Ruby implementation of digital object versioning toolkit used by the SULAIR
|
@@ -1,395 +0,0 @@
|
|
1
|
-
#$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', '..', 'lib'))
|
2
|
-
require 'rubygems'
|
3
|
-
require 'pathname'
|
4
|
-
require 'yard'
|
5
|
-
include YARD
|
6
|
-
require 'moab/stanford'
|
7
|
-
include Serializer
|
8
|
-
include Moab
|
9
|
-
include Stanford
|
10
|
-
|
11
|
-
class String
|
12
|
-
def snake_case
|
13
|
-
self.gsub(/::/, '/').
|
14
|
-
gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2').
|
15
|
-
gsub(/([a-z\d])([A-Z])/, '\1_\2').
|
16
|
-
tr("-", "_").
|
17
|
-
downcase
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
class Symbol
|
22
|
-
def snake_case
|
23
|
-
self.to_s.snake_case
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
class ApiClass
|
28
|
-
attr_accessor :class_name
|
29
|
-
attr_accessor :yard_class
|
30
|
-
attr_accessor :mhash
|
31
|
-
attr_accessor :ruby_class
|
32
|
-
attr_accessor :xml_tag
|
33
|
-
attr_accessor :description
|
34
|
-
attr_accessor :model
|
35
|
-
|
36
|
-
def self.class_hash=(class_hash)
|
37
|
-
@@class_hash = class_hash
|
38
|
-
end
|
39
|
-
|
40
|
-
def self.rootpath=(rootpath)
|
41
|
-
@@rootpath = rootpath
|
42
|
-
end
|
43
|
-
|
44
|
-
def initialize(class_name)
|
45
|
-
@class_name = class_name
|
46
|
-
@yard_class = @@class_hash[class_name]
|
47
|
-
if @yard_class.nil?
|
48
|
-
raise "class_hash[#{class_name}] is Nil"
|
49
|
-
end
|
50
|
-
@mhash = categorize_members(@yard_class)
|
51
|
-
if @yard_class.path.include?('::')
|
52
|
-
ruby_module = Object::const_get(yard_class.path.split(/::/)[0])
|
53
|
-
@ruby_class = ruby_module.const_get(yard_class.name)
|
54
|
-
else
|
55
|
-
@ruby_class = Object::const_get(yard_class.path)
|
56
|
-
end
|
57
|
-
|
58
|
-
if @ruby_class.respond_to?(:tag_name)
|
59
|
-
@xml_tag = "<#{ruby_class.tag_name}>"
|
60
|
-
else
|
61
|
-
@xml_tag = '-'
|
62
|
-
end
|
63
|
-
docstring_all = confluence_translate(yard_class.docstring.all.split(/@/)[0])
|
64
|
-
docstring_parts = docstring_all.split(/h4. Data Model\n/)
|
65
|
-
@description = docstring_parts[0]
|
66
|
-
@model = docstring_parts.size > 1 ? docstring_parts[1] : nil
|
67
|
-
end
|
68
|
-
|
69
|
-
def categorize_members(yard_class)
|
70
|
-
mhash = {
|
71
|
-
:class_attributes => Hash.new,
|
72
|
-
:instance_attributes => Hash.new,
|
73
|
-
:class_methods => Array.new,
|
74
|
-
:instance_methods => Array.new
|
75
|
-
}
|
76
|
-
yard_class.children.each do |member|
|
77
|
-
attr_symbol = member.name.to_s.gsub(/=$/, '').to_sym
|
78
|
-
if member.name == :initialize
|
79
|
-
mhash[:constructor] = member
|
80
|
-
elsif yard_class.class_attributes[attr_symbol]
|
81
|
-
mhash[:class_attributes][attr_symbol] = yard_class.class_attributes[attr_symbol]
|
82
|
-
elsif yard_class.instance_attributes[attr_symbol]
|
83
|
-
mhash[:instance_attributes][attr_symbol] = yard_class.instance_attributes[attr_symbol]
|
84
|
-
elsif member.scope == :class
|
85
|
-
mhash[:class_methods] << member
|
86
|
-
elsif member.scope == :instance
|
87
|
-
mhash[:instance_methods] << member
|
88
|
-
end
|
89
|
-
end
|
90
|
-
mhash
|
91
|
-
end
|
92
|
-
|
93
|
-
def title
|
94
|
-
title = "\n{anchor:#{yard_class.name}}\n"
|
95
|
-
title << "h3. Class #{yard_class.path}"
|
96
|
-
title
|
97
|
-
end
|
98
|
-
|
99
|
-
def description
|
100
|
-
description = "\nh4. Description\n\n"
|
101
|
-
description << @description
|
102
|
-
description
|
103
|
-
end
|
104
|
-
|
105
|
-
def model
|
106
|
-
model = "\nh4. Data Model\n\n"
|
107
|
-
model << @model
|
108
|
-
model
|
109
|
-
end
|
110
|
-
|
111
|
-
def xml_example
|
112
|
-
xml_example = String.new
|
113
|
-
if yard_class.docstring.has_tag?('example')
|
114
|
-
xml_example << "\nh4. XML Example\n"
|
115
|
-
xml_example << "{code:lang=xml}\n"
|
116
|
-
filename = yard_class.docstring.tag('example').name.split(/[:)}]/)[2]
|
117
|
-
example = IO.read(File.join(@@rootpath, filename))
|
118
|
-
xml_example << example
|
119
|
-
xml_example << "{code}\n"
|
120
|
-
end
|
121
|
-
xml_example
|
122
|
-
end
|
123
|
-
|
124
|
-
def instance_attributes_table(mode=:all)
|
125
|
-
table = String.new
|
126
|
-
table << "\\\\\n||XML Element||Ruby Class||Inherits From||\n"
|
127
|
-
table << "|#{xml_tag}|[##{class_name}]|#{yard_class.superclass}|\n"
|
128
|
-
table << "\\\\\n||XML Child Node||Ruby Attribute||Data Type||Description||\n"
|
129
|
-
xml_names = xml_name_hash
|
130
|
-
@mhash[:instance_attributes].values.each do |attribute|
|
131
|
-
read = attribute[:read]
|
132
|
-
return_tag = read.docstring.tag(:return)
|
133
|
-
ruby_name = read.name.to_s
|
134
|
-
xml_name = xml_names[ruby_name] ? xml_names[ruby_name] : "-"
|
135
|
-
data_type = return_tag.types[0]
|
136
|
-
description = confluence_translate(return_tag.text.gsub(/\n/, ' '))
|
137
|
-
table_row = "|#{xml_name}|#{ruby_name}|#{data_type}|#{description.gsub(/\|/, '\\|')}|\n"
|
138
|
-
case mode
|
139
|
-
when :xml
|
140
|
-
if xml_name != '-'
|
141
|
-
table << table_row
|
142
|
-
end
|
143
|
-
when :ruby
|
144
|
-
if xml_name == '-'
|
145
|
-
table << table_row
|
146
|
-
end
|
147
|
-
else
|
148
|
-
table << table_row
|
149
|
-
end
|
150
|
-
end
|
151
|
-
table
|
152
|
-
end
|
153
|
-
|
154
|
-
def xml_name_hash
|
155
|
-
xml_name_hash = Hash.new
|
156
|
-
if @ruby_class.respond_to?(:attributes)
|
157
|
-
@ruby_class.attributes.each do |attribute|
|
158
|
-
xml_name_hash[attribute.name.to_s] = "@#{attribute.tag}"
|
159
|
-
end
|
160
|
-
end
|
161
|
-
if @ruby_class.respond_to?(:elements)
|
162
|
-
@ruby_class.elements.each do |element|
|
163
|
-
if element.options.size == 0 or element.options[:single]
|
164
|
-
xml_name_hash[element.name.to_s] = "<#{element.tag}>"
|
165
|
-
else
|
166
|
-
xml_name_hash[element.name.to_s] = "<#{element.tag}> \\[1..\\*]"
|
167
|
-
end
|
168
|
-
end
|
169
|
-
end
|
170
|
-
xml_name_hash
|
171
|
-
end
|
172
|
-
|
173
|
-
def methods_documentation
|
174
|
-
|
175
|
-
methods_documentation = String.new
|
176
|
-
|
177
|
-
#methods_documentation ""
|
178
|
-
#methods_documentation "h4. Constructor"
|
179
|
-
#method = mhash[:constructor]
|
180
|
-
#methods_documentation_method(method, yard_class) if method
|
181
|
-
|
182
|
-
methods =mhash[:class_methods]
|
183
|
-
if methods.size > 0
|
184
|
-
methods_documentation << "\nh4. Class Methods\n"
|
185
|
-
methods.each do |method|
|
186
|
-
methods_documentation << method_documentation(method)
|
187
|
-
end
|
188
|
-
end
|
189
|
-
|
190
|
-
methods =mhash[:instance_methods]
|
191
|
-
if methods.size > 0
|
192
|
-
public_methods = Array.new
|
193
|
-
methods.each do |method|
|
194
|
-
if method.docstring.has_tag?(:api) && method.docstring.tag(:api).text == 'external'
|
195
|
-
public_methods << method
|
196
|
-
end
|
197
|
-
end
|
198
|
-
if public_methods.size > 0
|
199
|
-
methods_documentation << "\nh4. Instance Methods\n"
|
200
|
-
public_methods.each do |method|
|
201
|
-
methods_documentation << method_documentation(method)
|
202
|
-
end
|
203
|
-
end
|
204
|
-
end
|
205
|
-
methods_documentation
|
206
|
-
end
|
207
|
-
|
208
|
-
def method_documentation(method)
|
209
|
-
|
210
|
-
method_documentation = String.new
|
211
|
-
if method.nil?
|
212
|
-
raise "method is nil"
|
213
|
-
end
|
214
|
-
method_documentation << "\nh5. #{method.path}\n"
|
215
|
-
|
216
|
-
method_documentation << "||Method||Return Type||Description||\n"
|
217
|
-
if method.name == :initialize
|
218
|
-
return_type = @yard_class.name
|
219
|
-
description = 'constructor'
|
220
|
-
else
|
221
|
-
return_tag = method.docstring.tag(:return)
|
222
|
-
if return_tag.nil?
|
223
|
-
raise "#{method.name} return tag is nil"
|
224
|
-
end
|
225
|
-
return_type = return_tag.types[0]
|
226
|
-
description = confluence_translate(return_tag.text.gsub(/\n/, ' '))
|
227
|
-
end
|
228
|
-
method_documentation << "|#{method.name}|#{return_type}|#{example.description}|\n"
|
229
|
-
|
230
|
-
if method.respond_to?(:docstring)
|
231
|
-
params = method.docstring.tags(:param)
|
232
|
-
if params && params.size > 0
|
233
|
-
method_documentation << "\n||Parameter||Data Type||Description||\n"
|
234
|
-
params.each do |p|
|
235
|
-
description = confluence_translate(p.text.gsub(/\n/, ' '))
|
236
|
-
method_documentation << "|#{p.name}|#{p.types.join(', ')}|#{example.description}|\n"
|
237
|
-
end
|
238
|
-
end
|
239
|
-
end
|
240
|
-
|
241
|
-
method_documentation << "{code:lang=none|title=Ruby Source Code}\n"
|
242
|
-
method_documentation << method.source
|
243
|
-
method_documentation << "{code}\n"
|
244
|
-
|
245
|
-
if method.docstring.has_tag?('example')
|
246
|
-
method_documentation << "\n{code:lang=none|title=Usage Example}\n"
|
247
|
-
filename = method.docstring.tag('example').name.split(/[:)}]/)[2]
|
248
|
-
example = IO.read(File.join(@@rootpath, filename))
|
249
|
-
method_documentation << example
|
250
|
-
method_documentation << "{code}\n"
|
251
|
-
end
|
252
|
-
method_documentation
|
253
|
-
end
|
254
|
-
|
255
|
-
def confluence_translate(input)
|
256
|
-
map = Hash.new
|
257
|
-
map[/\|/] = "\\|"
|
258
|
-
map[/====/] = "h4. "
|
259
|
-
map[/\*/] = "\\*"
|
260
|
-
map[/\n\s{6}\\\*\s/] = "\n****\s"
|
261
|
-
map[/\n\s{4}\\\*\s/] = "\n***\s"
|
262
|
-
map[/\n\s{2}\\\*\s/] = "\n**\s"
|
263
|
-
map[/\n\\\*\s/] = "\n*\s"
|
264
|
-
map[/<[\/]*b>/] = "*"
|
265
|
-
map[/<[\/]*i>/] = "_"
|
266
|
-
map[/\[/] = "\\["
|
267
|
-
map[/\{#/] = "[#"
|
268
|
-
map[/\{http/] = "[http"
|
269
|
-
map[/\{/] = "[#"
|
270
|
-
map[/\}/] = "]"
|
271
|
-
output = input
|
272
|
-
map.each do |regex, replacement|
|
273
|
-
output.gsub!(regex, replacement)
|
274
|
-
end
|
275
|
-
output
|
276
|
-
end
|
277
|
-
|
278
|
-
end
|
279
|
-
|
280
|
-
|
281
|
-
class ApiDoc < ApiClass
|
282
|
-
attr_accessor :ios
|
283
|
-
attr_accessor :classes
|
284
|
-
|
285
|
-
def process_doc(ios)
|
286
|
-
@ios = ios
|
287
|
-
parse_model
|
288
|
-
|
289
|
-
output component
|
290
|
-
output model
|
291
|
-
output description
|
292
|
-
output xml_example
|
293
|
-
output xml_nodes
|
294
|
-
classes_detail
|
295
|
-
end
|
296
|
-
|
297
|
-
def component
|
298
|
-
component = "h2. Component: #{yard_class.name}\n"
|
299
|
-
component
|
300
|
-
end
|
301
|
-
|
302
|
-
def xml_nodes
|
303
|
-
nodes = "\nh4. XML Nodes\n"
|
304
|
-
@classes.each do |cls|
|
305
|
-
nodes << cls.instance_attributes_table(mode=:xml)
|
306
|
-
end
|
307
|
-
nodes
|
308
|
-
end
|
309
|
-
|
310
|
-
def classes_detail
|
311
|
-
@classes.each do |cls|
|
312
|
-
output cls.title
|
313
|
-
output cls.description if cls != self
|
314
|
-
output cls.methods_documentation
|
315
|
-
end
|
316
|
-
end
|
317
|
-
|
318
|
-
def parse_model
|
319
|
-
@classes = Array.new
|
320
|
-
@classes << self
|
321
|
-
if @model
|
322
|
-
model.lines.each do |line|
|
323
|
-
matches = line.scan(/\[#(.*)?\]/)
|
324
|
-
if matches.size > 0
|
325
|
-
match = matches[0][0].split(/\]/)[0]
|
326
|
-
if match != class_name
|
327
|
-
@classes << ApiClass.new(match)
|
328
|
-
end
|
329
|
-
end
|
330
|
-
end
|
331
|
-
end
|
332
|
-
|
333
|
-
end
|
334
|
-
|
335
|
-
def output(string)
|
336
|
-
@ios.puts string
|
337
|
-
end
|
338
|
-
|
339
|
-
|
340
|
-
end
|
341
|
-
|
342
|
-
|
343
|
-
class DocGenerator
|
344
|
-
|
345
|
-
def initialize(rootpath)
|
346
|
-
@rootpath = rootpath
|
347
|
-
end
|
348
|
-
|
349
|
-
def generate_docs(apis)
|
350
|
-
temp_pathname = Pathname(@rootpath).join('api', 'temp')
|
351
|
-
ApiClass.class_hash = get_class_hash
|
352
|
-
ApiClass.rootpath = @rootpath
|
353
|
-
apis.each do |api_name|
|
354
|
-
doc_pathname = temp_pathname.join(api_name + "_confluence.txt")
|
355
|
-
doc_pathname.parent.mkpath
|
356
|
-
#puts doc_pathname.to_s
|
357
|
-
#unless doc_pathname.exist?
|
358
|
-
begin
|
359
|
-
@constructor_params = Array.new
|
360
|
-
@indent = 0
|
361
|
-
ios = doc_pathname.open("w")
|
362
|
-
api_doc = ApiDoc.new(api_name)
|
363
|
-
api_doc.process_doc(ios)
|
364
|
-
ensure
|
365
|
-
ios.close
|
366
|
-
end
|
367
|
-
#end
|
368
|
-
end
|
369
|
-
end
|
370
|
-
|
371
|
-
def get_class_hash()
|
372
|
-
class_hash = Hash.new
|
373
|
-
yardoc = File.join(@rootpath, '.yardoc')
|
374
|
-
Registry.load!(yardoc) # loads all objects into memory
|
375
|
-
class_array = Registry.all(:class) # Array
|
376
|
-
class_array.each do |cls|
|
377
|
-
class_hash[cls.name.to_s] = cls
|
378
|
-
end
|
379
|
-
class_hash
|
380
|
-
end
|
381
|
-
|
382
|
-
end
|
383
|
-
|
384
|
-
apis = Array.new
|
385
|
-
apis << 'FileInventory'
|
386
|
-
apis << 'SignatureCatalog'
|
387
|
-
apis << 'FileInventoryDifference'
|
388
|
-
apis << 'VersionMetadata'
|
389
|
-
apis << 'Serializable'
|
390
|
-
apis << 'StorageObject'
|
391
|
-
apis << 'StorageRepository'
|
392
|
-
apis << 'DorMetadata'
|
393
|
-
|
394
|
-
sg = DocGenerator.new(File.expand_path(File.join(File.dirname(__FILE__), '..', '..')))
|
395
|
-
sg.generate_docs(apis)
|
data/lib/tools/spec_generator.rb
DELETED
@@ -1,409 +0,0 @@
|
|
1
|
-
require 'rubygems'
|
2
|
-
require 'pathname'
|
3
|
-
require 'yard'
|
4
|
-
include YARD
|
5
|
-
|
6
|
-
class String
|
7
|
-
def snake_case
|
8
|
-
self.gsub(/::/, '/').
|
9
|
-
gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2').
|
10
|
-
gsub(/([a-z\d])([A-Z])/, '\1_\2').
|
11
|
-
tr("-", "_").
|
12
|
-
downcase
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
class Symbol
|
17
|
-
def snake_case
|
18
|
-
self.to_s.snake_case
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
class SpecGenerator
|
23
|
-
|
24
|
-
def initialize(rootpath)
|
25
|
-
@rootpath = rootpath
|
26
|
-
@ios = $stdout
|
27
|
-
end
|
28
|
-
|
29
|
-
def generate_tests
|
30
|
-
spec_pathname = Pathname(@rootpath).join('spec', 'temp', "temp_unit_tests")
|
31
|
-
classes = get_classes
|
32
|
-
classes.each do |cls|
|
33
|
-
test_pathname = spec_pathname.join(cls.path.snake_case + "_spec.rb")
|
34
|
-
test_pathname.parent.mkpath
|
35
|
-
#puts test_pathname.to_s
|
36
|
-
unless test_pathname.exist?
|
37
|
-
begin
|
38
|
-
@constructor_params = Array.new
|
39
|
-
@indent = 0
|
40
|
-
@ios = test_pathname.open("w")
|
41
|
-
process_class(cls)
|
42
|
-
ensure
|
43
|
-
@ios.close
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
def get_classes()
|
50
|
-
yardoc = File.join(@rootpath, '.yardoc')
|
51
|
-
Registry.load!(yardoc) # loads all objects into memory
|
52
|
-
Registry.all(:class) # Array
|
53
|
-
end
|
54
|
-
|
55
|
-
# @param string [String]
|
56
|
-
def output(string)
|
57
|
-
@ios.puts " "*@indent + string
|
58
|
-
end
|
59
|
-
|
60
|
-
# @param cls [CodeObjects::ClassObject]
|
61
|
-
def process_class(cls)
|
62
|
-
output %q{require File.join('..', '..','spec_helper')}
|
63
|
-
output ""
|
64
|
-
output "# Unit tests for class {#{cls.path}}"
|
65
|
-
output "describe '#{cls.path}' do"
|
66
|
-
@indent += 1
|
67
|
-
mhash = categorize_members(cls)
|
68
|
-
if mhash[:class_attributes].size > 0
|
69
|
-
process_attributes(cls, :class, mhash[:class_attributes])
|
70
|
-
end
|
71
|
-
if mhash[:class_methods].size > 0
|
72
|
-
process_methods(cls, :class, mhash)
|
73
|
-
end
|
74
|
-
|
75
|
-
process_constructor(cls, mhash)
|
76
|
-
|
77
|
-
if mhash[:instance_attributes].size > 0
|
78
|
-
process_attributes(cls, :instance, mhash[:instance_attributes])
|
79
|
-
end
|
80
|
-
if mhash[:instance_methods].size > 0
|
81
|
-
process_methods(cls, :instance, mhash)
|
82
|
-
end
|
83
|
-
|
84
|
-
@indent -= 1
|
85
|
-
output ""
|
86
|
-
output "end"
|
87
|
-
end
|
88
|
-
|
89
|
-
# @param cls [CodeObjects::ClassObject]
|
90
|
-
def categorize_members(cls)
|
91
|
-
mhash = {
|
92
|
-
:class_attributes => Hash.new,
|
93
|
-
:instance_attributes => Hash.new,
|
94
|
-
:class_methods => Array.new,
|
95
|
-
:instance_methods => Array.new
|
96
|
-
}
|
97
|
-
cls.children.each do |member|
|
98
|
-
attr_symbol = member.name.to_s.gsub(/=$/, '').to_sym
|
99
|
-
if member.name == :initialize
|
100
|
-
mhash[:constructor] = member
|
101
|
-
elsif cls.class_attributes[attr_symbol]
|
102
|
-
mhash[:class_attributes][attr_symbol] = cls.class_attributes[attr_symbol]
|
103
|
-
elsif member.name.to_s[0..1] == '@@'
|
104
|
-
#mhash[:class_attributes][attr_symbol] = cls.class_attributes[attr_symbol]
|
105
|
-
elsif cls.instance_attributes[attr_symbol]
|
106
|
-
mhash[:instance_attributes][attr_symbol] = cls.instance_attributes[attr_symbol]
|
107
|
-
elsif not member.respond_to?(:scope)
|
108
|
-
#puts 'huh?'
|
109
|
-
elsif member.scope == :class
|
110
|
-
mhash[:class_methods] << member
|
111
|
-
elsif member.scope == :instance
|
112
|
-
mhash[:instance_methods] << member
|
113
|
-
end
|
114
|
-
end
|
115
|
-
mhash
|
116
|
-
end
|
117
|
-
|
118
|
-
def process_constructor(cls, mhash)
|
119
|
-
output ""
|
120
|
-
output "describe '=========================== CONSTRUCTOR ===========================' do"
|
121
|
-
|
122
|
-
@indent += 1
|
123
|
-
method = mhash[:constructor]
|
124
|
-
if method.respond_to?(:docstring)
|
125
|
-
@constructor_params = method.docstring.tags(:param)
|
126
|
-
|
127
|
-
output ""
|
128
|
-
output "# Unit test for constructor: {#{method.path}}"
|
129
|
-
output "# Which returns an instance of: [#{cls.path}]"
|
130
|
-
output "# For input parameters:"
|
131
|
-
@constructor_params.each do |p|
|
132
|
-
output "# * #{p.name} [#{p.types.join(', ')}] = #{p.text.gsub(/\n/, ' ')} "
|
133
|
-
end
|
134
|
-
output "specify '#{method.path}' do"
|
135
|
-
@indent += 1
|
136
|
-
|
137
|
-
cls_instance = cls.name.snake_case
|
138
|
-
param_list = (@constructor_params.collect { |p| p.name }).join(', ')
|
139
|
-
|
140
|
-
output " "
|
141
|
-
output "# test initialization with required parameters (if any)"
|
142
|
-
@constructor_params.each do |p|
|
143
|
-
if p.name == 'opts'
|
144
|
-
output "opts = {}"
|
145
|
-
else
|
146
|
-
output "#{p.name} = #{value_for(p.name, p.types[0])} "
|
147
|
-
end
|
148
|
-
end
|
149
|
-
output "#{cls_instance} = #{cls.name}.new(#{param_list})"
|
150
|
-
output "#{cls_instance}.should be_instance_of(#{cls.name})"
|
151
|
-
@constructor_params.each do |p|
|
152
|
-
unless p.name == 'opts'
|
153
|
-
output "#{cls_instance}.#{p.name}.should == #{p.name}"
|
154
|
-
end
|
155
|
-
end
|
156
|
-
|
157
|
-
arrays_and_hashes = attribute_arrays_and_hashes(mhash)
|
158
|
-
if arrays_and_hashes.size > 0
|
159
|
-
output " "
|
160
|
-
output "# test initialization of arrays and hashes"
|
161
|
-
arrays_and_hashes.each do |attr_name, type|
|
162
|
-
output "#{cls_instance}.#{attr_name}.should be_kind_of(#{type})"
|
163
|
-
end
|
164
|
-
end
|
165
|
-
|
166
|
-
@constructor_params.each do |p|
|
167
|
-
if p.name == 'opts'
|
168
|
-
output " "
|
169
|
-
output "# test initialization with options hash"
|
170
|
-
output "opts = Hash.new"
|
171
|
-
attribute_name_type_pairs(mhash).each do |name, type|
|
172
|
-
output "opts[:#{name}] = #{value_for(name, type)}"
|
173
|
-
end
|
174
|
-
output "#{cls_instance} = #{cls.name}.new(#{param_list})"
|
175
|
-
attribute_name_type_pairs(mhash).each do |name, type|
|
176
|
-
output "#{cls_instance}.#{name}.should == opts[:#{name}]"
|
177
|
-
end
|
178
|
-
end
|
179
|
-
end
|
180
|
-
|
181
|
-
output_source(method)
|
182
|
-
@indent -= 1
|
183
|
-
|
184
|
-
output "end"
|
185
|
-
|
186
|
-
end
|
187
|
-
@indent -= 1
|
188
|
-
output ""
|
189
|
-
output "end"
|
190
|
-
end
|
191
|
-
|
192
|
-
def process_attributes(cls, scope, attributes)
|
193
|
-
output ""
|
194
|
-
output "describe '=========================== #{scope.to_s.upcase} ATTRIBUTES ===========================' do"
|
195
|
-
@indent += 1
|
196
|
-
|
197
|
-
if scope == :instance
|
198
|
-
output ""
|
199
|
-
output "before(:all) do"
|
200
|
-
@indent += 1
|
201
|
-
@constructor_params.each do |p|
|
202
|
-
if p.name == 'opts'
|
203
|
-
output "opts = {}"
|
204
|
-
else
|
205
|
-
output "#{p.name} = #{value_for(p.name, p.types[0])} "
|
206
|
-
end
|
207
|
-
end
|
208
|
-
cls_instance = cls.name.snake_case
|
209
|
-
param_list = (@constructor_params.collect { |p| p.name }).join(', ')
|
210
|
-
output "@#{cls_instance} = #{cls.name}.new(#{param_list})"
|
211
|
-
@indent -= 1
|
212
|
-
output "end"
|
213
|
-
end
|
214
|
-
|
215
|
-
attributes.values.each do |attribute|
|
216
|
-
if attribute.nil?
|
217
|
-
puts "huh?"
|
218
|
-
end
|
219
|
-
write = attribute[:write]
|
220
|
-
read = attribute[:read]
|
221
|
-
return_tag = read.docstring.tag(:return)
|
222
|
-
return_type = return_tag.types[0]
|
223
|
-
output ""
|
224
|
-
output "# Unit test for attribute: {#{read.path}}"
|
225
|
-
output "# Which stores: [#{return_type}] #{return_tag.text.gsub(/\n/, ' ')}"
|
226
|
-
output "specify '#{read.path}' do"
|
227
|
-
|
228
|
-
@indent += 1
|
229
|
-
output "value = #{value_for(read.name, return_type)}"
|
230
|
-
case scope
|
231
|
-
when :class
|
232
|
-
output "#{write.path} value"
|
233
|
-
output "#{read.path}.should == value"
|
234
|
-
when :instance
|
235
|
-
output "@#{cls.name.snake_case}.#{write.name} value"
|
236
|
-
output "@#{cls.name.snake_case}.#{read.name}.should == value"
|
237
|
-
end
|
238
|
-
|
239
|
-
output_source(write)
|
240
|
-
output_source(read)
|
241
|
-
|
242
|
-
@indent -= 1
|
243
|
-
output "end"
|
244
|
-
end
|
245
|
-
@indent -= 1
|
246
|
-
|
247
|
-
output ""
|
248
|
-
output "end"
|
249
|
-
end
|
250
|
-
|
251
|
-
def process_methods(cls, scope, mhash)
|
252
|
-
output ""
|
253
|
-
output "describe '=========================== #{scope.to_s.upcase} METHODS ===========================' do"
|
254
|
-
@indent += 1
|
255
|
-
|
256
|
-
if scope == :instance
|
257
|
-
output ""
|
258
|
-
output "before(:each) do"
|
259
|
-
@indent += 1
|
260
|
-
@constructor_params.each do |p|
|
261
|
-
if p.name == 'opts'
|
262
|
-
output "@opts = {}"
|
263
|
-
else
|
264
|
-
output "@#{p.name} = #{value_for(p.name, p.types[0])} "
|
265
|
-
end
|
266
|
-
end
|
267
|
-
cls_instance = cls.name.snake_case
|
268
|
-
param_list = (@constructor_params.collect { |p| '@'+p.name }).join(', ')
|
269
|
-
output "@#{cls_instance} = #{cls.name}.new(#{param_list})"
|
270
|
-
output ""
|
271
|
-
attribute_name_type_pairs(mhash).each do |name, type|
|
272
|
-
output "@#{cls_instance}.#{name} = #{value_for(name, type)}"
|
273
|
-
end
|
274
|
-
@indent -= 1
|
275
|
-
output "end"
|
276
|
-
end
|
277
|
-
|
278
|
-
case scope
|
279
|
-
when :class
|
280
|
-
methods =mhash[:class_methods]
|
281
|
-
when :instance
|
282
|
-
methods =mhash[:instance_methods]
|
283
|
-
end
|
284
|
-
|
285
|
-
methods.each do |method|
|
286
|
-
begin
|
287
|
-
return_tag = method.docstring.tag(:return)
|
288
|
-
return_type = return_tag.types[0]
|
289
|
-
|
290
|
-
|
291
|
-
params = method.docstring.tags(:param)
|
292
|
-
|
293
|
-
output ""
|
294
|
-
output "# Unit test for method: {#{method.path}}"
|
295
|
-
output "# Which returns: [#{return_type}] #{return_tag.text.gsub(/\n/, ' ')}"
|
296
|
-
if params.length > 0
|
297
|
-
output "# For input parameters:"
|
298
|
-
params.each do |p|
|
299
|
-
output "# * #{p.name} [#{p.types.join(', ')}] = #{p.text.gsub(/\n/, ' ')} "
|
300
|
-
end
|
301
|
-
else
|
302
|
-
output "# For input parameters: (None)"
|
303
|
-
end
|
304
|
-
|
305
|
-
output "specify '#{method.path}' do"
|
306
|
-
|
307
|
-
@indent += 1
|
308
|
-
params.each do |p|
|
309
|
-
output "#{p.name} = #{value_for(p.name, p.types[0])} "
|
310
|
-
end
|
311
|
-
param_list = (params.collect { |p| p.name }).join(', ')
|
312
|
-
if return_type == 'void'
|
313
|
-
case scope
|
314
|
-
when :class
|
315
|
-
output "#{method.path}(#{param_list})"
|
316
|
-
when :instance
|
317
|
-
output "@#{cls.name.snake_case}.#{method.name}(#{param_list})"
|
318
|
-
end
|
319
|
-
else
|
320
|
-
case scope
|
321
|
-
when :class
|
322
|
-
output "#{method.path}(#{param_list}).should == "
|
323
|
-
when :instance
|
324
|
-
output "@#{cls.name.snake_case}.#{method.name}(#{param_list}).should == "
|
325
|
-
end
|
326
|
-
end
|
327
|
-
|
328
|
-
output_source(method)
|
329
|
-
@indent -= 1
|
330
|
-
|
331
|
-
output "end"
|
332
|
-
|
333
|
-
rescue Exception => e
|
334
|
-
raise "Error processing method: #{method.path} - " + e.message
|
335
|
-
e.backtrace
|
336
|
-
end
|
337
|
-
end
|
338
|
-
@indent -= 1
|
339
|
-
|
340
|
-
output ""
|
341
|
-
output "end"
|
342
|
-
end
|
343
|
-
|
344
|
-
|
345
|
-
def value_for(name, return_type)
|
346
|
-
case return_type
|
347
|
-
when 'Symbol'
|
348
|
-
":test_#{name}"
|
349
|
-
when 'Integer'
|
350
|
-
rand(100).to_s
|
351
|
-
when 'String'
|
352
|
-
"'Test #{name}'"
|
353
|
-
when 'Boolean'
|
354
|
-
"true"
|
355
|
-
when 'Pathname'
|
356
|
-
"Pathname.new('/test/#{name}')"
|
357
|
-
when 'Time'
|
358
|
-
"Time.now"
|
359
|
-
else
|
360
|
-
type = return_type.split(/[<>]/)
|
361
|
-
if type.length > 1
|
362
|
-
case type[0]
|
363
|
-
when 'Array'
|
364
|
-
"[#{value_for(name, type[1])}]"
|
365
|
-
when 'Hash'
|
366
|
-
key, value = type[1].split(/[,]/)
|
367
|
-
"{#{value_for(name, key)} => #{value_for(name, value)}}"
|
368
|
-
else
|
369
|
-
"double(#{return_type}.name)"
|
370
|
-
end
|
371
|
-
else
|
372
|
-
"double(#{return_type}.name)"
|
373
|
-
end
|
374
|
-
end
|
375
|
-
end
|
376
|
-
|
377
|
-
def attribute_arrays_and_hashes(mhash)
|
378
|
-
arrays_and_hashes = Hash.new
|
379
|
-
attribute_name_type_pairs(mhash).each do |name, type|
|
380
|
-
if type.include?('Array')
|
381
|
-
arrays_and_hashes[name] = 'Array'
|
382
|
-
elsif type.include?('Hash')
|
383
|
-
arrays_and_hashes[name] = 'Hash'
|
384
|
-
end
|
385
|
-
end
|
386
|
-
arrays_and_hashes
|
387
|
-
end
|
388
|
-
|
389
|
-
def attribute_name_type_pairs(mhash)
|
390
|
-
pairs = Hash.new
|
391
|
-
mhash[:instance_attributes].values.each do |attribute|
|
392
|
-
read = attribute[:read]
|
393
|
-
return_tag = read.docstring.tag(:return)
|
394
|
-
pairs[read.name] =return_tag.types[0]
|
395
|
-
end
|
396
|
-
pairs
|
397
|
-
end
|
398
|
-
|
399
|
-
def output_source(method)
|
400
|
-
output " "
|
401
|
-
lines = method.source.split(/\n/)
|
402
|
-
lines.each { |line| output "# #{line}" }
|
403
|
-
end
|
404
|
-
|
405
|
-
end
|
406
|
-
|
407
|
-
|
408
|
-
sg = SpecGenerator.new(File.expand_path(File.join(File.dirname(__FILE__), '..', '..')))
|
409
|
-
sg.generate_tests
|
@@ -1,49 +0,0 @@
|
|
1
|
-
rootpath = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
2
|
-
puts rootpath
|
3
|
-
lib = File.join(rootpath,'lib')
|
4
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
-
require 'moab'
|
6
|
-
#spec_helper = File.join(rootpath,'spec','spec_helper')
|
7
|
-
|
8
|
-
class SpecGeneratorOld
|
9
|
-
|
10
|
-
attr_accessor :c
|
11
|
-
attr_accessor :members
|
12
|
-
|
13
|
-
def initialize(c)
|
14
|
-
@c=c
|
15
|
-
#require camel_to_snake_case(c.name)
|
16
|
-
i_members = c.instance_methods(include_super=false)
|
17
|
-
i_setters = i_members.grep /[^=][=]$/
|
18
|
-
i_getters = i_setters.collect { |s| s.gsub(/[=]$/,'') }
|
19
|
-
@members = Hash.new
|
20
|
-
@members['Instance Variables'] = (i_getters).sort
|
21
|
-
@members['Instance Methods'] = (i_members - i_setters - i_getters).sort
|
22
|
-
@members['Class Methods'] = c.methods(include_super=false).sort
|
23
|
-
end
|
24
|
-
|
25
|
-
def generate
|
26
|
-
puts "require 'spec_helper'"
|
27
|
-
puts ""
|
28
|
-
puts "describe '#{@c.name}' do"
|
29
|
-
puts ""
|
30
|
-
['Instance Variables','Instance Methods','Class Methods'].each do |type |
|
31
|
-
puts " context '#{type}' do"
|
32
|
-
puts ""
|
33
|
-
@members[type].each do |member|
|
34
|
-
puts " describe '##{member}' do"
|
35
|
-
puts " pending"
|
36
|
-
puts " end"
|
37
|
-
puts ""
|
38
|
-
end
|
39
|
-
puts " end"
|
40
|
-
puts ""
|
41
|
-
end
|
42
|
-
puts "end"
|
43
|
-
end
|
44
|
-
|
45
|
-
def camel_to_snake_case(camel)
|
46
|
-
camel.gsub(/(.)([A-Z])/,'\1_\2').downcase
|
47
|
-
end
|
48
|
-
|
49
|
-
end
|