forked from Ortus-Solutions/DocBox
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathDocBox.cfc
228 lines (195 loc) · 7.7 KB
/
DocBox.cfc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
/**
* Core DocBox documentation class
* <br>
* <small><em>Copyright 2015 Ortus Solutions, Corp <a href="www.ortussolutions.com">www.ortussolutions.com</a></em></small>
*/
component accessors="true"{
/**
* The strategy to use for document generation. Must extend docbox.strategy.AbstractTemplateStrategy
*/
property name="strategy" doc_generic="docbox.strategy.AbstractTemplateStrategy";
/**
* Constructor
* @strategy The optional strategy to generate the documentation with. This can be a class path or an instance of the strategy. If none is passed then
* we create the default strategy of 'docbox.strategy.api.HTMLAPIStrategy'
* @properties The struct of properties to instantiate the strategy with.
*/
DocBox function init(
any strategy ="docbox.strategy.api.HTMLAPIStrategy",
struct properties={}
){
// if instance?
if( isObject( arguments.strategy ) ){
variables.strategy = arguments.strategy;
} else {
// Create it
variables.strategy = new "#arguments.strategy#"( argumentCollection=arguments.properties );
}
return this;
}
/**
* Generate the docs
* @source Either, the string directory source, OR an array of structs containing 'dir' and 'mapping' key
* @mapping The base mapping for the folder. Only required if the source is a string
* @excludes A regex that will be applied to the input source to exclude from the docs
*/
DocBox function generate(
required source,
string mapping="",
string excludes=""
){
// verify we have a strategy
if( isNull( variables.strategy ) ){
throw(
type = "StrategyNotSetException",
message = "No Template Strategy has been set.",
detail = "Create a Template Strategy, and set it with setStrategy() before calling generate() or pass it via the constructor."
);
}
// inflate the incoming input and mappings
var thisSource = "";
if( isSimpleValue( arguments.source ) ){
thisSource = [ { dir = arguments.source, mapping = arguments.mapping } ];
} else {
thisSource = arguments.source;
}
// build metadata collection
var qMetaData = buildMetaDataCollection( thisSource, arguments.excludes );
// run the strategy
variables.strategy.run( qMetaData );
return this;
}
/************************************ PRIVATE ******************************************/
/**
* Clean input path
* @path The incoming path to clean
* @inputDir The input dir to clean off
*/
private function cleanPath( required path, required inputDir ){
var currentPath = replace( getDirectoryFromPath( arguments.path ), arguments.inputDir, "" );
currentPath = reReplace( currentPath, "[/\\]", "" );
currentPath = reReplace( currentPath, "[/\\]", ".", "all" );
return rEReplace( currentPath, "\.$", "" );
}
/**
* Builds the searchable meta data collection
* @inputSource an array of structs containing inputDir and mapping
* @excludes A regex that will be applied to the input source to exclude from the docs
*/
query function buildMetaDataCollection( required array inputSource, string excludes="" ){
var qMetaData = QueryNew( "package,name,extends,metadata,type,implements,fullextends,currentMapping" );
// iterate over input sources
for( var thisInput in arguments.inputSource ){
var aFiles = directoryList( thisInput.dir, true, "path", "*.cfc" );
// iterate over files found
for( var thisFile in aFiles ){
// Excludes?
if( len( arguments.excludes ) && rEFindNoCase( arguments.excludes, thisFile ) ){
continue;
}
// get current path
var currentPath = cleanPath( thisFile, thisInput.dir );
// calculate package path according to mapping
var packagePath = thisInput.mapping;
if( len( currentPath ) ){
packagePath = ListAppend( thisInput.mapping, currentPath, "." );
}
// setup cfc name
var cfcName = listFirst( getFileFromPath( thisFile ), "." );
// Core Excludes, don't document the Application.cfc
if( cfcName == "Application" ){ continue; }
try{
// Get component metadatata
var meta = "";
if( Len( packagePath ) ){
meta = getComponentMetaData( packagePath & "." & cfcName );
} else {
meta = getComponentMetaData( cfcName );
}
//let's do some cleanup, in case CF sucks.
if( Len( packagePath ) AND NOT meta.name contains packagePath ){
meta.name = packagePath & "." & cfcName;
}
// Add row
QueryAddRow( qMetaData );
// Add contents
QuerySetCell( qMetaData, "package", packagePath );
QuerySetCell( qMetaData, "name", cfcName );
QuerySetCell( qMetaData, "metadata", meta );
QuerySetCell( qMetaData, "type", meta.type );
QuerySetCell( qMetaData, "currentMapping", thisInput.mapping );
// Get implements
var implements = getImplements( meta );
implements = listQualify( arrayToList( implements ), ':' );
QuerySetCell( qMetaData, "implements", implements );
// Get inheritance
var fullextends = getInheritance( meta );
fullextends = listQualify( arrayToList( fullextends ), ':' );
QuerySetCell( qMetaData, "fullextends", fullextends );
//so we cane easily query direct desendents
if( StructKeyExists( meta, "extends" ) ){
if( meta.type eq "interface" ){
QuerySetCell( qMetaData, "extends", meta.extends[ structKeyList( meta.extends ) ].name );
} else {
QuerySetCell( qMetaData, "extends", meta.extends.name );
}
} else {
QuerySetCell( qMetaData, "extends", "-" );
}
}
catch(Any e){
trace(
type = "warning",
category = "docbox",
inline = "true",
text = "Warning! The following script has errors: " & packagePath & cfcName & ". #e.toString()#"
);
}
} // end qFiles iteration
} // end input source iteration
return qMetadata;
}
/**
* Gets an array of the classes that this metadata implements, in order of extension
* @metadata The metadata to look at
*/
private array function getImplements( required struct metadata ){
var interfaces = {};
// check if a cfc
if( arguments.metadata.type neq "component" ){
return [];
}
// iterate
while( StructKeyExists( arguments.metadata, "extends" ) ){
if( StructKeyExists( arguments.metadata, "implements" ) ){
for( var key in arguments.metadata.implements ){
interfaces[ arguments.metadata.implements[ key ].name ] = 1;
}
}
arguments.metadata = arguments.metadata.extends;
}
// get as an array
interfaces = structKeyArray( interfaces );
// sort it
arraySort( interfaces, "textnocase" );
return interfaces;
}
/**
* Gets an array of the classes that this metadata extends, in order of extension
* @metadata The metadata to look at
*/
private array function getInheritance( required struct metadata ){
//ignore top level
var inheritence = [];
while( StructKeyExists( arguments.metadata, "extends" ) ){
//manage interfaces
if( arguments.metadata.type == "interface" ){
arguments.metadata = arguments.metadata.extends[ structKeyList( arguments.metadata.extends ) ];
} else {
arguments.metadata = arguments.metadata.extends;
}
arrayPrepend( inheritence, arguments.metadata.name );
}
return inheritence;
}
}