moab-versioning 2.1.0 → 2.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|