Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Moved most of HTML dealing code to GBHTMLOutputGenerator.

This required some modifications to existing classes and some unit test updates.
  • Loading branch information...
commit a35467a473f95ec9110d1d61b88628fcf211f573 1 parent c6d0d4f
@tomaz authored
View
30 Application/GBApplicationSettingsProvider.m
@@ -11,13 +11,11 @@
@interface GBApplicationSettingsProvider ()
-- (BOOL)isTopLevelStoreObject:(id)object;
- (NSString *)outputPathForObject:(id)object withExtension:(NSString *)extension;
- (NSString *)relativePathPrefixFromObject:(GBModelBase *)source toObject:(GBModelBase *)destination;
- (NSString *)htmlReferenceForObjectFromIndex:(GBModelBase *)object;
- (NSString *)htmlReferenceForTopLevelObject:(GBModelBase *)object fromTopLevelObject:(GBModelBase *)source;
- (NSString *)htmlReferenceForMember:(GBModelBase *)member prefixedWith:(NSString *)prefix;
-- (NSString *)htmlExtension;
@end
@@ -43,27 +41,11 @@ - (id)init {
return self;
}
-#pragma mark HTML paths and references handling
-
-- (NSString *)htmlOutputPath {
- return [self.outputPath stringByAppendingPathComponent:@"html"];
-}
-
-- (NSString *)htmlOutputPathForIndex {
- NSString *result = [self.htmlOutputPath stringByAppendingPathComponent:@"index"];
- return [result stringByAppendingPathExtension:[self htmlExtension]];
-}
-
-- (NSString *)htmlOutputPathForObject:(GBModelBase *)object {
- NSParameterAssert(object != nil);
- NSParameterAssert([self isTopLevelStoreObject:object]);
- NSString *inner = [self htmlReferenceForObjectFromIndex:object];
- return [self.htmlOutputPath stringByAppendingPathComponent:inner];
-}
+#pragma mark HTML references handling
- (NSString *)htmlReferenceNameForObject:(GBModelBase *)object {
NSParameterAssert(object != nil);
- if ([self isTopLevelStoreObject:object]) return [self htmlReferenceForObject:object fromSource:object];
+ if (object.isTopLevelObject) return [self htmlReferenceForObject:object fromSource:object];
return [self htmlReferenceForMember:object prefixedWith:@""];
}
@@ -73,7 +55,7 @@ - (NSString *)htmlReferenceForObject:(GBModelBase *)object fromSource:(GBModelBa
// Generate hrefs from index to objects:
if (!source) {
// To top-level object.
- if ([self isTopLevelStoreObject:object]) return [self htmlReferenceForObjectFromIndex:object];
+ if (object.isTopLevelObject) return [self htmlReferenceForObjectFromIndex:object];
// To a member of top-level object.
NSString *path = [self htmlReferenceForObjectFromIndex:object.parentObject];
@@ -82,11 +64,11 @@ - (NSString *)htmlReferenceForObject:(GBModelBase *)object fromSource:(GBModelBa
}
// Generate hrefs from member to other objects:
- if (![self isTopLevelStoreObject:source]) {
+ if (!source.isTopLevelObject) {
GBModelBase *sourceParent = source.parentObject;
// To the parent or another top-level object.
- if ([self isTopLevelStoreObject:object]) return [self htmlReferenceForObject:object fromSource:sourceParent];
+ if (object.isTopLevelObject) return [self htmlReferenceForObject:object fromSource:sourceParent];
// To same or another member of the same parent.
if (object.parentObject == sourceParent) return [self htmlReferenceForMember:object prefixedWith:@"#"];
@@ -98,7 +80,7 @@ - (NSString *)htmlReferenceForObject:(GBModelBase *)object fromSource:(GBModelBa
}
// From top-level object to samo or another top level object.
- if (object == source || [self isTopLevelStoreObject:object]) {
+ if (object == source || object.isTopLevelObject) {
return [self htmlReferenceForTopLevelObject:object fromTopLevelObject:source];
}
View
48 Application/GBApplicationSettingsProviding.h
@@ -19,48 +19,24 @@
@protocol GBApplicationSettingsProviding
///---------------------------------------------------------------------------------------
-/// @name Template paths handling
+/// @name Paths handling
///---------------------------------------------------------------------------------------
/** The base path to template files used for generating various output files. */
@property (copy) NSString *templatesPath;
-///---------------------------------------------------------------------------------------
-/// @name Output paths handling
-///---------------------------------------------------------------------------------------
-
/** The base path of the generated files. */
@property (copy) NSString *outputPath;
-/** The base path of the HTML generated files.
-
- This value depends on `outputPath` and is automatically calculated.
-
- @see htmlOutputPathForObject:
- @see htmlOutputPathForIndex
- @see outputPath
- */
-@property (readonly) NSString *htmlOutputPath;
-
-/** Returns file name including full path for HTML file representing the given top-level object.
-
- This works for any top-level object: class, category or protocol. The path is automatically determined regarding to the object class.
+/** The list of all full or partial paths to be ignored.
- @param object The object for which to return the path.
- @return Returns the path.
- @exception NSException Thrown if the given object is `nil` or not top-level object.
- @see htmlOutputPathForIndex
- @see htmlOutputPath
+ It's recommended to check if a path string ends with any of the given paths before processing it. This should catch directory and file names properly as directories are processed first.
*/
-- (NSString *)htmlOutputPathForObject:(GBModelBase *)object;
+@property (retain) NSMutableArray *ignoredPaths;
-/** Returns file name including full path for HTML file representing the main index.
-
- @return Returns the path.
- @see htmlOutputPathForObject:
- @see htmlOutputPath
- */
-- (NSString *)htmlOutputPathForIndex;
+///---------------------------------------------------------------------------------------
+/// @name Application-wide HTML helpers
+///---------------------------------------------------------------------------------------
/** Returns HTML reference name for the given object.
@@ -95,15 +71,9 @@
*/
- (NSString *)htmlReferenceForObject:(GBModelBase *)object fromSource:(GBModelBase *)source;
-///---------------------------------------------------------------------------------------
-/// @name Other paths handling
-///---------------------------------------------------------------------------------------
-
-/** The list of all full or partial paths to be ignored.
-
- It's recommended to check if a path string ends with any of the given paths before processing it. This should catch directory and file names properly as directories are processed first.
+/** The file extension for html files.
*/
-@property (retain) NSMutableArray *ignoredPaths;
+@property (readonly) NSString *htmlExtension;
///---------------------------------------------------------------------------------------
/// @name Helper classes
View
138 Generating/GBGenerator.m
@@ -8,23 +8,14 @@
#import "GBApplicationSettingsProviding.h"
#import "GBStoreProviding.h"
-#import "GBDataObjects.h"
-#import "GBTemplateVariablesProvider.h"
-#import "GBTemplateHandler.h"
-#import "GBOutputGenerator.h"
+#import "GBHTMLOutputGenerator.h"
#import "GBGenerator.h"
@interface GBGenerator ()
-- (void)processClasses;
-- (void)processCategories;
-- (void)processProtocols;
-- (void)processIndex;
-- (void)writeString:(NSString *)string toFile:(NSString *)path;
-- (GBTemplateHandler *)templateHandlerFromTemplateFile:(NSString *)filename;
-@property (readonly) GBTemplateHandler *objectTemplate;
-@property (readonly) GBTemplateHandler *indexTemplate;
-@property (readonly) GBTemplateVariablesProvider *variablesProvider;
+- (void)setupGeneratorStepsWithStore:(id<GBStoreProviding>)store;
+- (void)runGeneratorStepsWithStore:(id<GBStoreProviding>)store;
+@property (readonly) NSMutableArray *outputGenerators;
@property (retain) id<GBApplicationSettingsProviding> settings;
@property (retain) id<GBStoreProviding> store;
@@ -55,107 +46,42 @@ - (id)initWithSettingsProvider:(id)settingsProvider {
- (void)generateOutputFromStore:(id<GBStoreProviding>)store {
NSParameterAssert(store != nil);
- GBLogVerbose(@"Generating output from parsed objects...");
- self.store = store;
- GBOutputGenerator *g = [[GBOutputGenerator alloc] init];
- [g copyTemplateFilesFromPath:self.settings.templatesPath toPath:self.settings.htmlOutputPath];
-// [self processClasses];
-// [self processCategories];
-// [self processProtocols];
-// [self processIndex];
+ GBLogInfo(@"Generating output from parsed objects...");
+ [self setupGeneratorStepsWithStore:store];
+ [self runGeneratorStepsWithStore:store];
}
-- (void)processClasses {
- for (GBClassData *class in self.store.classes) {
- GBLogInfo(@"Generating output for class %@...", class);
- NSDictionary *vars = [self.variablesProvider variablesForClass:class withStore:self.store];
- NSString *output = [self.objectTemplate renderObject:vars];
- NSString *path = [self.settings htmlOutputPathForObject:class];
- [self writeString:output toFile:path];
- GBLogDebug(@"Finished generating output for class %@.", class);
- }
-}
-
-- (void)processCategories {
- for (GBCategoryData *category in self.store.categories) {
- GBLogInfo(@"Generating output for category %@...", category);
- NSDictionary *vars = [self.variablesProvider variablesForCategory:category withStore:self.store];
- NSString *output = [self.objectTemplate renderObject:vars];
- NSString *path = [self.settings htmlOutputPathForObject:category];
- [self writeString:output toFile:path];
- GBLogDebug(@"Finished generating output for category %@.", category);
- }
-}
-
-- (void)processProtocols {
- for (GBProtocolData *protocol in self.store.protocols) {
- GBLogInfo(@"Generating output for protocol %@...", protocol);
- NSDictionary *vars = [self.variablesProvider variablesForProtocol:protocol withStore:self.store];
- NSString *output = [self.objectTemplate renderObject:vars];
- NSString *path = [self.settings htmlOutputPathForObject:protocol];
- [self writeString:output toFile:path];
- GBLogDebug(@"Finished generating output for protocol %@.", protocol);
- }
+- (void)setupGeneratorStepsWithStore:(id<GBStoreProviding>)store {
+ GBLogDebug(@"Initializing generation steps...");
+ [self.outputGenerators addObject:[GBHTMLOutputGenerator generatorWithSettingsProvider:self.settings]];
}
-- (void)processIndex {
- GBLogInfo(@"Generating output for index…");
- if ([self.store.classes count] > 0 || [self.store.protocols count] > 0 || [self.store.categories count] > 0) {
- NSDictionary *vars = [self.variablesProvider variablesForIndexWithStore:self.store];
- NSString *output = [self.indexTemplate renderObject:vars];
- NSString *path = [self.settings htmlOutputPathForIndex];
- [self writeString:output toFile:path];
- }
- GBLogDebug(@"Finished generating output for index.");
+- (void)runGeneratorStepsWithStore:(id<GBStoreProviding>)store {
+ GBLogDebug(@"Running generation steps...");
+ NSUInteger stepsCount = [self.outputGenerators count];
+ [self.outputGenerators enumerateObjectsUsingBlock:^(GBOutputGenerator *generator, NSUInteger idx, BOOL *stop) {
+ NSError *error = nil;
+ GBLogVerbose(@"Step %ld/%ld: Running %@...", idx, stepsCount, [generator className]);
+ if (![generator copyTemplateFilesToOutputPath:&error]) {
+ GBLogNSError(error, @"Step %ld/%ld failed: %@ failed copying template files to output, aborting!", idx, stepsCount, [generator className]);
+ *stop = YES;
+ return;
+ }
+ if (![generator generateOutputWithStore:store error:&error]) {
+ GBLogNSError(error, @"Step %ld/%ld failed: %@ failed generaing output, aborting!", idx, stepsCount, [generator className]);
+ *stop = YES;
+ return;
+ }
+ }];
}
#pragma mark Template files handling
-- (GBTemplateVariablesProvider *)variablesProvider {
- static GBTemplateVariablesProvider *result = nil;
- if (!result) result = [[GBTemplateVariablesProvider alloc] initWithSettingsProvider:self.settings];
- return result;
-}
-
-- (GBTemplateHandler *)objectTemplate {
- static GBTemplateHandler *result = nil;
- if (!result) result = [self templateHandlerFromTemplateFile:@"object-template.html"];
- return result;
-}
-
-- (GBTemplateHandler *)indexTemplate {
- static GBTemplateHandler *result = nil;
- if (!result) result = [self templateHandlerFromTemplateFile:@"index-template.html"];
- return result;
-}
-
-#pragma mark Helper methods
-
-- (void)writeString:(NSString *)string toFile:(NSString *)path {
- // Writes the given string to the given path, creating all folders if they don't exist.
- NSError *error = nil;
-
- NSString *standardized = [path stringByStandardizingPath];
- NSString *directory = [standardized stringByDeletingLastPathComponent];
- [self.fileManager createDirectoryAtPath:directory withIntermediateDirectories:YES attributes:nil error:&error];
- if (error) {
- GBLogNSError(error, @"Failed creating directory while writting %@!", path);
- return;
- }
-
- [string writeToFile:standardized atomically:YES encoding:NSUTF8StringEncoding error:&error];
- if (error) GBLogNSError(error, @"Failed writing %@!", path);
-}
-
-- (GBTemplateHandler *)templateHandlerFromTemplateFile:(NSString *)filename {
- NSError *error = nil;
- NSString *path = self.settings.templatesPath;
- GBLogDebug(@"Creating template handler for file %@ in template folder %@...", filename, path);
- path = [path stringByAppendingPathComponent:filename];
- GBTemplateHandler *result = [GBTemplateHandler handler];
- if (![result parseTemplateFromPath:[path stringByStandardizingPath] error:&error]) {
- GBLogNSError(error, @"Failed parsing template %@!", filename);
- return nil;
+- (NSMutableArray *)outputGenerators {
+ static NSMutableArray *result = nil;
+ if (!result) {
+ GBLogDebug(@"Initializing output generators array...");
+ result = [[NSMutableArray alloc] init];
}
return result;
}
View
15 Generating/GBHTMLOutputGenerator.h
@@ -0,0 +1,15 @@
+//
+// GBHTMLOutputGenerator.h
+// appledoc
+//
+// Created by Tomaz Kragelj on 29.11.10.
+// Copyright 2010 Gentle Bytes. All rights reserved.
+//
+
+#import "GBOutputGenerator.h"
+
+/** Concrete `GBOutputGenerator` that generates HTML files.
+ */
+@interface GBHTMLOutputGenerator : GBOutputGenerator
+
+@end
View
141 Generating/GBHTMLOutputGenerator.m
@@ -0,0 +1,141 @@
+//
+// GBHTMLOutputGenerator.m
+// appledoc
+//
+// Created by Tomaz Kragelj on 29.11.10.
+// Copyright 2010 Gentle Bytes. All rights reserved.
+//
+
+#import "GBApplicationSettingsProviding.h"
+#import "GBDataObjects.h"
+#import "GBTemplateVariablesProvider.h"
+#import "GBTemplateHandler.h"
+#import "GBHTMLOutputGenerator.h"
+
+@interface GBHTMLOutputGenerator ()
+
+- (BOOL)processClasses:(NSError **)error;
+- (BOOL)processCategories:(NSError **)error;
+- (BOOL)processProtocols:(NSError **)error;
+- (BOOL)processIndex:(NSError **)error;
+- (NSString *)htmlOutputPathForIndex;
+- (NSString *)htmlOutputPathForObject:(GBModelBase *)object;
+@property (readonly) GBTemplateHandler *htmlObjectTemplate;
+@property (readonly) GBTemplateHandler *htmlIndexTemplate;
+@property (readonly) GBTemplateVariablesProvider *variablesProvider;
+
+@end
+
+#pragma mark -
+
+@implementation GBHTMLOutputGenerator
+
+#pragma Generation handling
+
+- (BOOL)generateOutputWithStore:(id<GBStoreProviding>)store error:(NSError **)error {
+ if (![super generateOutputWithStore:store error:error]) return NO;
+ if (![self processClasses:error]) return NO;
+ if (![self processCategories:error]) return NO;
+ if (![self processProtocols:error]) return NO;
+ if (![self processIndex:error]) return NO;
+ return YES;
+}
+
+- (BOOL)processClasses:(NSError **)error {
+ for (GBClassData *class in self.store.classes) {
+ GBLogInfo(@"Generating output for class %@...", class);
+ NSDictionary *vars = [self.variablesProvider variablesForClass:class withStore:self.store];
+ NSString *output = [self.htmlObjectTemplate renderObject:vars];
+ NSString *path = [self htmlOutputPathForObject:class];
+ if (![self writeString:output toFile:[path stringByStandardizingPath] error:error]) {
+ GBLogWarn(@"Failed writting HTML for class %@ to '%@'!", class, path);
+ return NO;
+ }
+ GBLogDebug(@"Finished generating output for class %@.", class);
+ }
+ return YES;
+}
+
+- (BOOL)processCategories:(NSError **)error {
+ for (GBCategoryData *category in self.store.categories) {
+ GBLogInfo(@"Generating output for category %@...", category);
+ NSDictionary *vars = [self.variablesProvider variablesForCategory:category withStore:self.store];
+ NSString *output = [self.htmlObjectTemplate renderObject:vars];
+ NSString *path = [self htmlOutputPathForObject:category];
+ if (![self writeString:output toFile:[path stringByStandardizingPath] error:error]) {
+ GBLogWarn(@"Failed writting HTML for category %@ to '%@'!", category, path);
+ return NO;
+ }
+ GBLogDebug(@"Finished generating output for category %@.", category);
+ }
+ return YES;
+}
+
+- (BOOL)processProtocols:(NSError **)error {
+ for (GBProtocolData *protocol in self.store.protocols) {
+ GBLogInfo(@"Generating output for protocol %@...", protocol);
+ NSDictionary *vars = [self.variablesProvider variablesForProtocol:protocol withStore:self.store];
+ NSString *output = [self.htmlObjectTemplate renderObject:vars];
+ NSString *path = [self htmlOutputPathForObject:protocol];
+ if (![self writeString:output toFile:[path stringByStandardizingPath] error:error]) {
+ GBLogWarn(@"Failed writting HTML for protocol %@ to '%@'!", protocol, path);
+ return NO;
+ }
+ GBLogDebug(@"Finished generating output for protocol %@.", protocol);
+ }
+ return YES;
+}
+
+- (BOOL)processIndex:(NSError **)error {
+ GBLogInfo(@"Generating output for index...");
+ if ([self.store.classes count] > 0 || [self.store.protocols count] > 0 || [self.store.categories count] > 0) {
+ NSDictionary *vars = [self.variablesProvider variablesForIndexWithStore:self.store];
+ NSString *output = [self.htmlIndexTemplate renderObject:vars];
+ NSString *path = [[self htmlOutputPathForIndex] stringByStandardizingPath];
+ if (![self writeString:output toFile:[path stringByStandardizingPath] error:error]) {
+ GBLogWarn(@"Failed writting HTML index to '%@'!", path);
+ return NO;
+ }
+ }
+ GBLogDebug(@"Finished generating output for index.");
+ return YES;
+}
+
+#pragma mark Helper methods
+
+- (NSString *)htmlOutputPathForIndex {
+ // Returns file name including full path for HTML file representing the main index.
+ NSString *result = [self.outputUserPath stringByAppendingPathComponent:@"index"];
+ return [result stringByAppendingPathExtension:self.settings.htmlExtension];
+}
+
+- (NSString *)htmlOutputPathForObject:(GBModelBase *)object {
+ // Returns file name including full path for HTML file representing the given top-level object. This works for any top-level object: class, category or protocol. The path is automatically determined regarding to the object class.
+ NSString *inner = [self.settings htmlReferenceForObjectFromIndex:object];
+ return [self.outputUserPath stringByAppendingPathComponent:inner];
+}
+
+- (GBTemplateVariablesProvider *)variablesProvider {
+ static GBTemplateVariablesProvider *result = nil;
+ if (!result) {
+ GBLogDebug(@"Initializing variables provider...");
+ result = [[GBTemplateVariablesProvider alloc] initWithSettingsProvider:self.settings];
+ }
+ return result;
+}
+
+- (GBTemplateHandler *)htmlObjectTemplate {
+ return [self.templateFiles objectForKey:@"object-template.html"];
+}
+
+- (GBTemplateHandler *)htmlIndexTemplate {
+ return [self.templateFiles objectForKey:@"index-template.html"];
+}
+
+#pragma mark Overriden methods
+
+- (NSString *)outputSubpath {
+ return @"html";
+}
+
+@end
View
112 Generating/GBOutputGenerator.h
@@ -7,6 +7,9 @@
//
#import <Foundation/Foundation.h>
+#import "GBStoreProviding.h"
+
+@protocol GBApplicationSettingsProviding;
/** The base class for all output generators.
@@ -15,27 +18,120 @@
@interface GBOutputGenerator : NSObject
///---------------------------------------------------------------------------------------
-/// @name Templates handling
+/// @name Initialization & disposal
///---------------------------------------------------------------------------------------
-/** Copies all files from the given templates path to the given output path, replicating the directory structure.
+/** Returns autoreleased generator that work with the given `GBApplicationSettingsProvider` implementor.
- The method uses `outputPath` to determine the source and destination subdirectories relative to the given paths. It then copies all files from template path to the output path, including the whole directory structure. If any special template file is found at source path, it is not copied! Template files are identified by having a `-template` suffix followed by optional extension. For example `object-template.html`. As this message prepares the ground for actual generation, it should be sent before any other messages.
+ @param settingsProvider Application-wide settings provider to use for checking parameters.
+ @return Returns initialized instance or `nil` if initialization fails.
+ @exception NSException Thrown if the given application is `nil`.
+ */
++ (id)generatorWithSettingsProvider:(id)settingsProvider;
+
+/** Initializes the generator to work with the given `GBApplicationSettingsProvider` implementor.
- If copying fails, the error is logged to console and `NO` is returned.
+ This is the designated initializer.
- @param sourcePath The source path to copy from.
- @param destPath The destination path to copy to.
+ @param settingsProvider Application-wide settings provider to use for checking parameters.
+ @return Returns initialized instance or `nil` if initialization fails.
+ @exception NSException Thrown if the given application is `nil`.
+ */
+- (id)initWithSettingsProvider:(id)settingsProvider;
+
+///---------------------------------------------------------------------------------------
+/// @name Generation handling
+///---------------------------------------------------------------------------------------
+
+/** Copies all files from the templates path to the output path as defined in assigned `settings`, replicating the directory structure and stores all detected template files to `templateFiles` dictionary.
+
+ The method uses `outputPath` to determine the source and destination subdirectories relative to the common template and output paths. It then copies all files from template path to the output path, including the whole directory structure. If any special template file is found at source path, it is not copied! Template files are identified by having a `-template` suffix followed by optional extension. For example `object-template.html`. As this message prepares the ground for actual generation, it should be sent before any other messages (i.e. before `generateOutput:`).
+
+ To further aid subclasses, the method reads out all template files in templates path and stores them to `templateFiles` dictionary. Each template file is stored with a key correspoding to it's filename, including the subdirectory within the base template path and extension.
+
+ @warning *Note:* This message is intended to be sent from higher-level generator objects. Although it would present no error to run it several times, in most circumstances subclasses don't need to send it manually. If copying fails, a warning is logged and copying is stopped. Depending of type of failure, the method either returns `YES` or `NO`. If copying of all files is succesful, but reading or clearing template or ignored files fails, the operation is still considered succesful, so `YES` is returned. However if replicating the directory structure or copying files fails, this is considered an error and `NO` is returned. In such case, clients should abort further processing.
+
+
+ @param error If copying fails, error description is returned here.
@return Returns `YES` if all files were succesfully copied, `NO` otherwise.
+ @see generateOutputWithStore:error:
+ */
+- (BOOL)copyTemplateFilesToOutputPath:(NSError **)error;
+
+/** Generates the output at the proper subdirectory of the output path as defined in assigned `settings`.
+
+ This is the most important method of the `GBOutputGenerator` class. It generates all required output. It is intended to be overriden in subclasses. Default implementation assigns the given store and returns YES. Subclasses must call super class implementation before anything else!
+
+ @param store The `GBStoreProviding` object that holds the store with all parsed and processed data.
+ @param error If generation fails, error description is returned here.
+ @see copyTemplateFilesToOutputPath:
+ @see writeString:toFile:error:
+ @see store
+ */
+- (BOOL)generateOutputWithStore:(id<GBStoreProviding>)store error:(NSError **)error;
+
+/** Writes the given string to the given path, creating all necessary directories if they don't exist.
+
+ This method is intended to be used from subclass, in most cases from `generateOutputWithStore:error:`.
+
+ @param string The string to write.
+ @param path The path and filename to write to.
+ @param error If writting fails, error description is returned here.
+ @return Returns `YES` is writting succeds, `NO` otherwise.
+ @see generateOutputWithStore:error:
+ */
+- (BOOL)writeString:(NSString *)string toFile:(NSString *)path error:(NSError **)error;
+
+///---------------------------------------------------------------------------------------
+/// @name Subclass parameters and helpers
+///---------------------------------------------------------------------------------------
+
+/** The dictionary of all template files detected within `copyTemplateFilesToOutputPath:`.
+
+ Each object has a key of template file name and relative path from `templateUserPath`. The keys are mapped to `GBTemplateHandler` instances associated with the template.
+
+ This is intended to be used within subclasses only. Dictionary contents are automatically updated and should not be changed by subclasses.
+
+ @see copyTemplateFilesToOutputPath:
+ */
+@property (readonly) NSMutableDictionary *templateFiles;
+
+/** Returns user-friendly template path string including `outputSubpath`.
+
+ This uses the same string as entered by the user when starting the application. Send `stringByStandardizingPath` message to the returned value before using it!
+
+ @see outputUserPath
+ */
+@property (readonly) NSString *templateUserPath;
+
+/** Returns the output path including `outputSubpath`.
+
+ This uses the same string as entered by the user when starting the application. Send `stringByStandardizingPath` message to the returned value before using it!
+
+ @see templateUserPath
+ */
+@property (readonly) NSString *outputUserPath;
+
+/** The store as assigned to `generateOutput`.
+
+ @see generateOutputWithStore:error:
*/
-- (BOOL)copyTemplateFilesFromPath:(NSString *)sourcePath toPath:(NSString *)destPath;
+@property (readonly, retain) id<GBStoreProviding> store;
+
+///---------------------------------------------------------------------------------------
+/// @name Generation parameters
+///---------------------------------------------------------------------------------------
/** Returns the path relative to main output path, where all generated data is stored.
At the same this, this also defines the path relative to main templates path, where all template files for this output generator are stored. Default implementation simply returns empty string, each subclass is supposed to override and return prover value.
- @see copyTemplateFilesFromPath:toPath:
+ @see copyTemplateFilesToOutputPath:
*/
@property (readonly) NSString *outputSubpath;
+/** The `GBApplicationSettingsProviding` object that provides application-wide settings for this session.
+ */
+@property (retain) id<GBApplicationSettingsProviding> settings;
+
@end
View
138 Generating/GBOutputGenerator.m
@@ -6,12 +6,16 @@
// Copyright 2010 Gentle Bytes. All rights reserved.
//
+#import "GBApplicationSettingsProviding.h"
+#import "GBTemplateHandler.h"
#import "GBOutputGenerator.h"
@interface GBOutputGenerator ()
+- (GBTemplateHandler *)templateHandlerFromTemplateFile:(NSString *)filename error:(NSError **)error;
- (BOOL)isPathRepresentingTemplateFile:(NSString *)path;
- (BOOL)isPathRepresentingIgnoredFile:(NSString *)path;
+@property (readwrite, retain) id<GBStoreProviding> store;
@end
@@ -21,59 +25,84 @@ @implementation GBOutputGenerator
#pragma mark Initialization & disposal
-- (id)init {
- if ((self = [super init])) {
- }
- return self;
++ (id)generatorWithSettingsProvider:(id)settingsProvider {
+ return [[[self alloc] initWithSettingsProvider:settingsProvider] autorelease];
}
-#pragma mark Templates handling
+- (id)initWithSettingsProvider:(id)settingsProvider {
+ NSParameterAssert(settingsProvider != nil);
+ NSParameterAssert([settingsProvider conformsToProtocol:@protocol(GBApplicationSettingsProviding)]);
+ GBLogDebug(@"Initializing output generator with settings provider %@...", settingsProvider);
+ self = [super init];
+ if (self) {
+ self.settings = settingsProvider;
+ }
+ return self;
+}
-- (BOOL)copyTemplateFilesFromPath:(NSString *)sourcePath toPath:(NSString *)destPath {
- GBLogVerbose(@"Copying template files from '%@' to '%@'...", sourcePath, destPath);
- NSError *error = nil;
+#pragma mark Generation handling
+- (BOOL)generateOutputWithStore:(id<GBStoreProviding>)store error:(NSError **)error {
+ GBLogVerbose(@"%@ is generating output...", [self className]);
+ self.store = store;
+ return YES;
+}
+
+- (BOOL)copyTemplateFilesToOutputPath:(NSError **)error {
+ // Remove all previous template files.
+ [self.templateFiles removeAllObjects];
+
// Prepare source and destination paths.
- NSString *sourceUserPath = [sourcePath stringByAppendingPathComponent:self.outputSubpath];
- NSString *destUserPath = [destPath stringByAppendingPathComponent:self.outputSubpath];
- sourcePath = [sourceUserPath stringByStandardizingPath];
- destPath = [destUserPath stringByStandardizingPath];
+ NSString *sourceUserPath = self.templateUserPath;
+ NSString *destUserPath = self.outputUserPath;
+ NSString *sourcePath = [sourceUserPath stringByStandardizingPath];
+ NSString *destPath = [destUserPath stringByStandardizingPath];
+ GBLogVerbose(@"Copying template files from '%@' to '%@'...", sourceUserPath, destUserPath);
// Remove destination path if it exists. Exit if we fail.
if ([self.fileManager fileExistsAtPath:destPath]) {
GBLogDebug(@"Removing output at '%@'...", destUserPath);
- if (![self.fileManager removeItemAtPath:destPath error:&error]) {
- GBLogNSError(error, @"Failed deleting output path '%@'!", destUserPath);
- return NO;
+ if (![self.fileManager removeItemAtPath:destPath error:error]) {
+ GBLogWarn(@"Failed removing output files at '%@'!", destUserPath);
+ return NO;
}
}
// Copy the whole source directory over to output. Exit if we fail.
GBLogDebug(@"Copying template files from '%@' to '%@'...", sourceUserPath, destUserPath);
- if (![self.fileManager copyItemAtPath:sourcePath toPath:destPath error:&error]) {
- GBLogNSError(error, @"Failed copying templates from '%@' to '%@'!", sourceUserPath, destUserPath);
+ if (![self.fileManager copyItemAtPath:sourcePath toPath:destPath error:error]) {
+ GBLogWarn(@"Failed copying templates from '%@' to '%@'!", sourceUserPath, destUserPath);
return NO;
}
// Remove all ignored files and special template items from output. First enumerate all files. If this fails, report success; this step is only used to verscleanup the destination, we should still have valid output if these files are kept there.
GBLogDebug(@"Removing leftovers from '%@'...", destUserPath);
- NSArray *items = [self.fileManager subpathsOfDirectoryAtPath:destPath error:&error];
- if (error) {
- GBLogNSError(error, @"Failed enumerating template files at '%@'!", destUserPath);
+ NSArray *items = [self.fileManager subpathsOfDirectoryAtPath:destPath error:error];
+ if (!items) {
+ GBLogWarn(@"Failed enumerating template files at '%@'!", destUserPath);
return YES;
- }
-
- BOOL result = YES;
+ }
for (NSString *path in items) {
- if (![self isPathRepresentingIgnoredFile:path] && ![self isPathRepresentingTemplateFile:path]) continue;
- GBLogDebug(@"Cleaning leftover '%@' from output...", path);
- NSString *fullpath = [destPath stringByAppendingPathComponent:path];
- if (![self.fileManager removeItemAtPath:fullpath error:&error]) {
- GBLogNSError(error, @"Can't clean lefover '%@' from '%@'.", path, destUserPath);
+ BOOL delete = NO;
+ if ([self isPathRepresentingIgnoredFile:path]) {
+ delete = YES;
+ } else if ([self isPathRepresentingTemplateFile:path]) {
+ GBTemplateHandler *handler = [self templateHandlerFromTemplateFile:path error:error];
+ if (!handler) return NO;
+ [self.templateFiles setObject:path forKey:path];
+ delete = YES;
+ }
+
+ if (delete) {
+ GBLogDebug(@"Cleaning leftover '%@' from output...", path);
+ NSString *fullpath = [destPath stringByAppendingPathComponent:path];
+ if (![self.fileManager removeItemAtPath:fullpath error:error]) {
+ GBLogWarn(@"Can't clean leftover '%@' from '%@'.", path, destUserPath);
+ }
}
}
- return result;
+ return YES;
}
- (BOOL)isPathRepresentingTemplateFile:(NSString *)path {
@@ -88,8 +117,59 @@ - (BOOL)isPathRepresentingIgnoredFile:(NSString *)path {
return NO;
}
+#pragma mark Helper methods
+
+- (BOOL)writeString:(NSString *)string toFile:(NSString *)path error:(NSError **)error {
+ NSString *standardized = [path stringByStandardizingPath];
+ NSString *directory = [standardized stringByDeletingLastPathComponent];
+ if (![self.fileManager createDirectoryAtPath:directory withIntermediateDirectories:YES attributes:nil error:error]) {
+ GBLogWarn(@"Failed creating directory while writting '%@'!", path);
+ return NO;
+ }
+
+ if (![string writeToFile:standardized atomically:YES encoding:NSUTF8StringEncoding error:error]) {
+ GBLogWarn(@"Failed writting '%@'!", path);
+ return NO;
+ }
+
+ return YES;
+}
+
+- (GBTemplateHandler *)templateHandlerFromTemplateFile:(NSString *)filename error:(NSError **)error {
+ NSString *path = [[self templateUserPath] stringByAppendingPathComponent:filename];
+ GBLogDebug(@"Creating template handler for template file '%@'...", path);
+ GBTemplateHandler *result = [GBTemplateHandler handler];
+ if (![result parseTemplateFromPath:[path stringByStandardizingPath] error:error]) {
+ GBLogWarn(@"Failed parsing template '%@'!", filename);
+ return nil;
+ }
+ return result;
+}
+
+#pragma mark Subclass helpers
+
+- (NSString *)templateUserPath {
+ return [self.settings.templatesPath stringByAppendingPathComponent:self.outputSubpath];
+}
+
+- (NSString *)outputUserPath {
+ return [self.settings.outputPath stringByAppendingPathComponent:self.outputSubpath];
+}
+
+- (NSMutableDictionary *)templateFiles {
+ static NSMutableDictionary *result = nil;
+ if (!result) result = [[NSMutableDictionary alloc] init];
+ return result;
+}
+
+#pragma mark Generation parameters
+
- (NSString *)outputSubpath {
return @"";
}
+#pragma mark Properties
+
+@synthesize settings;
+
@end
View
4 Model/GBCategoryData.m
@@ -49,6 +49,10 @@ - (NSString *)description {
return self.idOfCategory;
}
+- (BOOL)isTopLevelObject {
+ return YES;
+}
+
#pragma mark Properties
- (BOOL)isExtension {
View
4 Model/GBClassData.m
@@ -57,6 +57,10 @@ - (NSString *)description {
return self.nameOfClass;
}
+- (BOOL)isTopLevelObject {
+ return YES;
+}
+
#pragma mark Properties
@synthesize nameOfClass = _className;
View
5 Model/GBModelBase.h
@@ -80,6 +80,11 @@
*/
@property (retain) id parentObject;
+/** Specifies whether this is a top level object or not.
+
+ Top level objects are classes, categories and protocols.
+ */
+@property (readonly) BOOL isTopLevelObject;
///---------------------------------------------------------------------------------------
/// @name Output generation helpers
View
8 Model/GBModelBase.m
@@ -53,7 +53,7 @@ - (void)mergeDataFromObject:(id)source {
if (!self.comment && comment) self.comment = comment;
}
-#pragma mark Source info handling
+#pragma mark Declared files handling
- (void)registerSourceInfo:(GBSourceInfo *)data {
NSParameterAssert(data != nil);
@@ -74,6 +74,12 @@ - (NSArray *)sourceInfosSortedByName {
return [[self.sourceInfos allObjects] sortedArrayUsingSelector:@selector(compare:)];
}
+#pragma Helper methods
+
+- (BOOL)isTopLevelObject {
+ return NO;
+}
+
#pragma mark Properties
@synthesize comment;
View
4 Model/GBProtocolData.m
@@ -45,6 +45,10 @@ - (NSString *)description {
return self.nameOfProtocol;
}
+- (BOOL)isTopLevelObject {
+ return YES;
+}
+
#pragma mark Properties
@synthesize nameOfProtocol = _protocolName;
View
26 Testing/GBApplicationSettingsProviderTesting.m
@@ -14,32 +14,6 @@ @interface GBApplicationSettingsProviderTesting : GHTestCase
@implementation GBApplicationSettingsProviderTesting
-#pragma mark HTML output paths handling
-
-- (void)testCssTemplatePath_shouldReturnProperValue {
- // setup
- GBApplicationSettingsProvider *settings = [GBApplicationSettingsProvider provider];
- // execute & verify
- assertThat(settings.cssClassTemplatePath, is(@"../../styles.css"));
- assertThat(settings.cssCategoryTemplatePath, is(@"../../styles.css"));
- assertThat(settings.cssProtocolTemplatePath, is(@"../../styles.css"));
-}
-
-- (void)testHtmlOutputPathForObject_shouldReturnProperlyPrefixedPath {
- // setup
- GBApplicationSettingsProvider *settings = [GBApplicationSettingsProvider provider];
- settings.outputPath = @"./";
- GBClassData *class = [GBClassData classDataWithName:@"Class"];
- GBCategoryData *category = [GBCategoryData categoryDataWithName:@"Category" className:@"Class"];
- GBCategoryData *extension = [GBCategoryData categoryDataWithName:nil className:@"Class"];
- GBProtocolData *protocol = [GBProtocolData protocolDataWithName:@"Protocol"];
- // execute & verify
- assertThat([settings htmlOutputPathForObject:class], is(@"./html/Classes/Class.html"));
- assertThat([settings htmlOutputPathForObject:category], is(@"./html/Categories/Class(Category).html"));
- assertThat([settings htmlOutputPathForObject:extension], is(@"./html/Categories/Class().html"));
- assertThat([settings htmlOutputPathForObject:protocol], is(@"./html/Protocols/Protocol.html"));
-}
-
#pragma mark HTML href names handling
- (void)testHtmlReferenceNameForObject_shouldReturnProperValueForTopLevelObjects {
View
9 Testing/GBCategoryDataTesting.m
@@ -101,4 +101,13 @@ - (void)testMergeDataFromObject_extensionShouldMergeMethodsAndPreserveSourceData
assertThatInteger([[source.methods methods] count], equalToInteger(2));
}
+#pragma mark Helper methods
+
+- (void)testIsTopLevelObject_shouldReturnYES {
+ // setup & execute
+ GBCategoryData *category = [GBCategoryData categoryDataWithName:@"Category" className:@"Class"];
+ // verify
+ assertThatBool(category.isTopLevelObject, equalToBool(YES));
+}
+
@end
View
9 Testing/GBClassDataTesting.m
@@ -111,4 +111,13 @@ - (void)testMergeDataFromObject_shouldMergeMethodsAndPreserveSourceData {
assertThatInteger([[source.methods methods] count], equalToInteger(2));
}
+#pragma mark Helper methods
+
+- (void)testIsTopLevelObject_shouldReturnYES {
+ // setup & execute
+ GBClassData *class = [GBClassData classDataWithName:@"Class"];
+ // verify
+ assertThatBool(class.isTopLevelObject, equalToBool(YES));
+}
+
@end
View
7 Testing/GBIvarDataTesting.m
@@ -24,4 +24,11 @@ - (void)testMergeDataFromObject_shouldMergeImplementationDetails {
assertThatInteger([original.sourceInfos count], equalToInteger(1));
}
+- (void)testIsTopLevelObject_shouldReturnNO {
+ // setup & execute
+ GBIvarData *ivar = [GBTestObjectsRegistry ivarWithComponents:@"int", @"_name", nil];
+ // verify
+ assertThatBool(ivar.isTopLevelObject, equalToBool(NO));
+}
+
@end
View
7 Testing/GBMethodDataTesting.m
@@ -307,4 +307,11 @@ - (void)testMethodPrefix_shouldReturnProperPrefix {
assertThat(([[GBTestObjectsRegistry classMethodWithNames:@"method", nil] methodPrefix]), is(@"+"));
}
+- (void)testIsTopLevelObject_shouldReturnNO {
+ // setup & execute
+ GBMethodData *method = [GBTestObjectsRegistry instanceMethodWithNames:@"method", nil];
+ // verify
+ assertThatBool(method.isTopLevelObject, equalToBool(NO));
+}
+
@end
View
9 Testing/GBProtocolDataTesting.m
@@ -54,4 +54,13 @@ - (void)testMergeDataFromObject_shouldMergeMethodsAndPreserveSourceData {
assertThatInteger([[source.methods methods] count], equalToInteger(2));
}
+#pragma mark Helper methods
+
+- (void)testIsTopLevelObject_shouldReturnYES {
+ // setup & execute
+ GBProtocolData *protocol = [GBProtocolData protocolDataWithName:@"Protocol"];
+ // verify
+ assertThatBool(protocol.isTopLevelObject, equalToBool(YES));
+}
+
@end
View
1  Testing/GBTemplateVariablesProvider-CommonTesting.m
@@ -23,7 +23,6 @@ - (void)testVariablesForClass_shouldPrepareDefaultVariables {
NSDictionary *vars = [provider variablesForClass:class withStore:[GBTestObjectsRegistry store]];
// verify - just basic tests...
assertThat([vars objectForKey:@"page"], isNot(nil));
- assertThat([vars valueForKeyPath:@"page.cssPath"], isNot(nil));
assertThat([vars valueForKeyPath:@"page.title"], isNot(nil));
assertThat([vars valueForKeyPath:@"page.specifications"], isNot(nil));
assertThat([vars objectForKey:@"object"], is(class));
View
8 appledoc.xcodeproj/project.pbxproj
@@ -82,6 +82,7 @@
733EA2E2122C00370060CBDE /* GBParagraphItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 733EA2E0122C00370060CBDE /* GBParagraphItem.m */; };
7340F02811FCC63100E712A4 /* NSFileManager+GBFileManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 7340F02511FCC63100E712A4 /* NSFileManager+GBFileManager.m */; };
7340F02911FCC63100E712A4 /* NSObject+GBObject.m in Sources */ = {isa = PBXBuildFile; fileRef = 7340F02711FCC63100E712A4 /* NSObject+GBObject.m */; };
+ 73473D3112A38B730011336C /* GBHTMLOutputGenerator.m in Sources */ = {isa = PBXBuildFile; fileRef = 73473D3012A38B730011336C /* GBHTMLOutputGenerator.m */; };
7359B149129A5A0700F67AD1 /* GRBoolean.m in Sources */ = {isa = PBXBuildFile; fileRef = 7359B120129A5A0600F67AD1 /* GRBoolean.m */; };
7359B14A129A5A0700F67AD1 /* GRBoolean.m in Sources */ = {isa = PBXBuildFile; fileRef = 7359B120129A5A0600F67AD1 /* GRBoolean.m */; };
7359B14B129A5A0700F67AD1 /* GRMustache.m in Sources */ = {isa = PBXBuildFile; fileRef = 7359B123129A5A0600F67AD1 /* GRMustache.m */; };
@@ -152,7 +153,6 @@
73CF822D122D79AB005B7E26 /* GBParagraphListItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 73CF822C122D79AB005B7E26 /* GBParagraphListItem.m */; };
73CF822E122D79AB005B7E26 /* GBParagraphListItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 73CF822C122D79AB005B7E26 /* GBParagraphListItem.m */; };
73D2524612A2ED610024F9F9 /* GBOutputGenerator.m in Sources */ = {isa = PBXBuildFile; fileRef = 73D2524512A2ED610024F9F9 /* GBOutputGenerator.m */; };
- 73D2524712A2ED610024F9F9 /* GBOutputGenerator.m in Sources */ = {isa = PBXBuildFile; fileRef = 73D2524512A2ED610024F9F9 /* GBOutputGenerator.m */; };
73D54C6211F8CE6D00CCDDB0 /* appledoc.m in Sources */ = {isa = PBXBuildFile; fileRef = 73D54C6111F8CE6D00CCDDB0 /* appledoc.m */; };
73D54CA911F8D27F00CCDDB0 /* GBLog.m in Sources */ = {isa = PBXBuildFile; fileRef = 73D54CA811F8D27F00CCDDB0 /* GBLog.m */; };
73D54D1E11F8D53E00CCDDB0 /* DDCliApplication.m in Sources */ = {isa = PBXBuildFile; fileRef = 73D54D1611F8D53E00CCDDB0 /* DDCliApplication.m */; };
@@ -228,6 +228,8 @@
7340F02511FCC63100E712A4 /* NSFileManager+GBFileManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSFileManager+GBFileManager.m"; sourceTree = "<group>"; };
7340F02611FCC63100E712A4 /* NSObject+GBObject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+GBObject.h"; sourceTree = "<group>"; };
7340F02711FCC63100E712A4 /* NSObject+GBObject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+GBObject.m"; sourceTree = "<group>"; };
+ 73473D2F12A38B730011336C /* GBHTMLOutputGenerator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GBHTMLOutputGenerator.h; sourceTree = "<group>"; };
+ 73473D3012A38B730011336C /* GBHTMLOutputGenerator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GBHTMLOutputGenerator.m; sourceTree = "<group>"; };
7359B11F129A5A0600F67AD1 /* GRBoolean.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GRBoolean.h; path = GRMustache/GRBoolean.h; sourceTree = "<group>"; };
7359B120129A5A0600F67AD1 /* GRBoolean.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GRBoolean.m; path = GRMustache/GRBoolean.m; sourceTree = "<group>"; };
7359B121129A5A0600F67AD1 /* GRMustache_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GRMustache_private.h; path = GRMustache/GRMustache_private.h; sourceTree = "<group>"; };
@@ -650,6 +652,8 @@
73AA9F721253BF4000074152 /* GBGenerator.m */,
73D2524412A2ED610024F9F9 /* GBOutputGenerator.h */,
73D2524512A2ED610024F9F9 /* GBOutputGenerator.m */,
+ 73473D2F12A38B730011336C /* GBHTMLOutputGenerator.h */,
+ 73473D3012A38B730011336C /* GBHTMLOutputGenerator.m */,
7321D0E512944CF500796DEC /* GBTemplateHandler.h */,
7321D0E612944CF500796DEC /* GBTemplateHandler.m */,
73734616129668340046D6B8 /* GBDictionaryTemplateLoader.h */,
@@ -1189,7 +1193,6 @@
73F568C212A22A7900A72BB2 /* DDFileLogger.m in Sources */,
73F568C412A22A7900A72BB2 /* DDLog.m in Sources */,
73F568C612A22A7900A72BB2 /* DDTTYLogger.m in Sources */,
- 73D2524712A2ED610024F9F9 /* GBOutputGenerator.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -1267,6 +1270,7 @@
73F568C312A22A7900A72BB2 /* DDLog.m in Sources */,
73F568C512A22A7900A72BB2 /* DDTTYLogger.m in Sources */,
73D2524612A2ED610024F9F9 /* GBOutputGenerator.m in Sources */,
+ 73473D3112A38B730011336C /* GBHTMLOutputGenerator.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Please sign in to comment.
Something went wrong with that request. Please try again.