SystemOrganization addCategory: #'Gofer-Core'! SystemOrganization addCategory: #'Gofer-Test'! Notification subclass: #GoferVersionCache instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Gofer-Core'! !GoferVersionCache class methodsFor: 'private' stamp: 'lr 10/2/2009 09:41'! basicVersionsIn: aRepository | versions | versions := OrderedCollection new. aRepository allVersionNames do: [ :each | versions addLast: (GoferVersionReference name: each repository: aRepository) ]. ^ versions! ! !GoferVersionCache class methodsFor: 'public' stamp: 'lr 9/29/2009 21:47'! during: aBlock | cache | cache := Dictionary new. ^ aBlock on: self do: [ :notification | notification resume: cache ]! ! !GoferVersionCache class methodsFor: 'public' stamp: 'lr 9/29/2009 21:45'! versionsIn: aRepository ^ self signal at: aRepository ifAbsentPut: [ self basicVersionsIn: aRepository ]! ! !GoferVersionCache methodsFor: 'accessing' stamp: 'lr 9/28/2009 23:38'! defaultAction ^ Dictionary new! ! Object subclass: #Gofer instanceVariableNames: 'references repository' classVariableNames: '' poolDictionaries: '' category: 'Gofer-Core'! !Gofer commentStamp: 'lr 9/21/2009 13:44' prior: 0! : Gofer, a person who runs errands. Origin 1960s: from go for, i.e. go and fetch. : ''The New Oxford American Dictionary'' Gofer is a small tool on top of Monticello that loads, updates, merges, diffs, reverts, commits, recompiles and unloads groups of Monticello packages. Contrary to existing tools Gofer makes sure that these operations are performed as clean as possible: - Gofer treats packages from one or more repository in one operation. - Gofer works with fixed versions or tries to find the "latest" version using a given name prefix. - Gofer automatically assigns repositories to all packages, so that the other tools are ready to be used on individual packages. - Gofer makes sure that there is only one repository instance registered for a single physical location. - Gofer works with Monticello dependencies and uniformly treats them like the primary package. - Gofer cleans up after Monticello, no empty class categories and no empty method protocols are to be expected. To get started with Gofer in Pharo use the following script: == ScriptLoader new installGofer To use Gofer to load the "latest" Seaside 2.8 together with its prerequisites and the Scriptaculous package one would write and evaluate the following code: == Gofer new == squeaksource: 'KomHttpServer'; == addAll: #( 'DynamicBindings' 'KomServices' 'KomHttpServer' ); == squeaksource: 'Seaside'; == addAll: #( 'Seaside2.8a' 'Scriptaculous' ); == load However, that's only the beginning. Developers might want to keep the Gofer specification around to perform other actions on the specified set of packages: == gofer := Gofer new. == gofer == squeaksource: 'KomHttpServer'; == addAll: #( 'DynamicBindings' 'KomServices' 'KomHttpServer' ); == squeaksource: 'Seaside'; == addAll: #( 'Seaside2.8a' 'Scriptaculous' ). Now the following expressions can be used at any time: | ==gofer load== | Load all packages. | ==gofer update== | Update all packages. | ==gofer merge== | Merge all packages into their working copies. | ==gofer diff== | Display the difference between the working copy and the base version of all packages. | ==gofer commit== | Commit all modified packages. | ==gofer revert== | Revert all packages to their base version. | ==gofer recompile== | Recompile all packages. | ==gofer unload== | Unload all packages. Some of the operations have additional parameters, for example you can specify a commit message using ==gofer message: 'fixes issue 123'==.! !Gofer class methodsFor: 'examples' stamp: 'lr 10/2/2009 10:17'! gofer "Create a Gofer instance of Gofer." ^ self new renggli: 'flair'; addPackage: 'Gofer'; yourself! ! !Gofer class methodsFor: 'examples' stamp: 'lr 10/2/2009 10:18'! komanche "Create a Gofer instance of Komanche." ^ self new squeaksource: 'KomHttpServer'; addPackage: 'DynamicBindings'; addPackage: 'KomServices'; addPackage: 'KomHttpServer'; yourself! ! !Gofer class methodsFor: 'examples' stamp: 'lr 10/2/2009 10:18'! magritte "Create a Gofer instance of Magritte." ^ self new renggli: 'magritte'; addPackage: 'Magritte-Model'; addPackage: 'Magritte-Tests'; addPackage: 'Magritte-Seaside'; addPackage: 'Magritte-Morph'; yourself! ! !Gofer class methodsFor: 'instance creation' stamp: 'lr 8/20/2009 09:54'! new ^ self basicNew initialize! ! !Gofer class methodsFor: 'examples' stamp: 'lr 10/2/2009 10:18'! omnibrowser "Create a Gofer instance of OmniBrowser." ^ self new renggli: 'omnibrowser'; addPackage: 'OmniBrowser'; addPackage: 'OB-Standard'; addPackage: 'OB-Morphic'; addPackage: 'OB-Refactory'; addPackage: 'OB-Regex'; addPackage: 'OB-SUnitIntegration'; yourself! ! !Gofer class methodsFor: 'examples' stamp: 'lr 10/2/2009 10:18'! pier "Create a Gofer instance of Pier." ^ self new renggli: 'pier'; addPackage: 'Pier-Model'; addPackage: 'Pier-Tests'; addPackage: 'Pier-Seaside'; addPackage: 'Pier-Blog'; addPackage: 'Pier-Security'; addPackage: 'Pier-Squeak-Persistency'; yourself! ! !Gofer class methodsFor: 'examples' stamp: 'lr 10/2/2009 10:19'! pierAddons "Create a Gofer instance of Pier Addons." ^ self new renggli: 'pieraddons'; addPackage: 'Pier-Design'; addPackage: 'Pier-Documents'; addPackage: 'Pier-EditorEnh'; addPackage: 'Pier-Google'; addPackage: 'Pier-Links'; addPackage: 'Pier-Randomizer'; addPackage: 'Pier-TagCloud'; addPackage: 'Pier-Slideshow'; addPackage: 'Pier-Setup'; yourself! ! !Gofer class methodsFor: 'examples' stamp: 'lr 10/2/2009 10:19'! refactoring "Create a Gofer instance of the refactoring tools." ^ self new squeaksource: 'AST'; addPrefix: 'AST-lr'; squeaksource: 'RefactoringEngine'; addPrefix: 'Refactoring-Core-lr'; addPrefix: 'Refactoring-Spelling-lr'; yourself! ! !Gofer class methodsFor: 'examples' stamp: 'lr 10/2/2009 10:19'! seaside28 "Create a Gofer instance of Seaside 2.8." ^ self new squeaksource: 'Seaside'; addPrefix: 'Seaside2.8a1-lr'; addPrefix: 'Scriptaculous-lr'; addPrefix: 'Comet-lr'; squeaksource: 'rsrss'; addPackage: 'RSRSS2'; yourself! ! !Gofer class methodsFor: 'examples' stamp: 'lr 10/2/2009 10:19'! tools "Create a Gofer instance of several development tools." ^ self new renggli: 'unsorted'; addPackage: 'Shout'; addPackage: 'RoelTyper'; addPackage: 'ECompletion'; addPackage: 'ECompletionOmniBrowser'; yourself! ! !Gofer methodsFor: 'adding' stamp: 'lr 10/2/2009 10:43'! add: aReference "Add aReference ot the list of packages." aReference isString ifTrue: [ self notify: 'Please note that adding package references as strings (such as ' , aReference printString , ') is no longer supported, because Gofer cannot guess your naming conventions. Adapt your code to either call #addPackage: (for full package names e.g. Gofer), #addVersion: (for complete version names, e.g. Gofer-lr.54) or #addPrefix: (for incomplete version names, e.g. Gofer-lr). This lets Gofer know what exactly you want, and in return it will more likely do what you expect.'. ^ self addPrefix: aReference ]. ^ self references addLast: aReference! ! !Gofer methodsFor: 'adding' stamp: 'lr 10/2/2009 10:43'! addPackage: aString "Add the package aString to the receiver." ^ self add: (GoferPackageReference name: aString repository: self repository)! ! !Gofer methodsFor: 'adding' stamp: 'lr 10/2/2009 10:43'! addPrefix: aString "Add the package with the prefix aString to the receiver." ^ self add: (GoferPrefixReference name: aString repository: self repository)! ! !Gofer methodsFor: 'adding' stamp: 'lr 10/2/2009 10:43'! addVersion: aString "Add the version aString to the receiver." ^ self add: (GoferVersionReference name: aString repository: self repository)! ! !Gofer methodsFor: 'actions' stamp: 'lr 10/2/2009 10:08'! commit "Commit the specified packages." ^ self execute: GoferCommit! ! !Gofer methodsFor: 'actions' stamp: 'lr 10/2/2009 10:09'! commit: aString "Commit the specified packages with the given commit message aString." ^ self execute: GoferCommit do: [ :operation | operation message: aString ]! ! !Gofer methodsFor: 'repositories' stamp: 'lr 7/10/2009 16:27'! croquet: aString self url: 'http://hedgehog.software.umn.edu:8888/' , aString! ! !Gofer methodsFor: 'actions' stamp: 'lr 8/20/2009 10:14'! diff "Display the differences between the working copy and the base of the specified packages." ^ self execute: GoferDiff! ! !Gofer methodsFor: 'private' stamp: 'lr 10/2/2009 10:11'! execute: anOperationClass ^ self execute: anOperationClass do: nil! ! !Gofer methodsFor: 'private' stamp: 'lr 10/2/2009 10:11'! execute: anOperationClass do: aBlock | operation | ^ GoferVersionCache during: [ operation := anOperationClass on: self. aBlock isNil ifFalse: [ aBlock value: operation ]. operation execute ]! ! !Gofer methodsFor: 'repositories' stamp: 'lr 7/10/2009 16:27'! impara: aString self url: 'http://source.impara.de/' , aString! ! !Gofer methodsFor: 'initialization' stamp: 'lr 10/2/2009 10:09'! initialize references := OrderedCollection new! ! !Gofer methodsFor: 'actions' stamp: 'lr 8/20/2009 10:14'! load "Load the specified packages." ^ self execute: GoferLoad! ! !Gofer methodsFor: 'actions' stamp: 'lr 8/20/2009 10:14'! merge "Merge the specified packages." ^ self execute: GoferMerge! ! !Gofer methodsFor: 'copying' stamp: 'lr 10/2/2009 10:09'! postCopy super postCopy. references := references copy! ! !Gofer methodsFor: 'actions' stamp: 'lr 8/20/2009 11:44'! recompile "Recompile the specified packages." ^ self execute: GoferRecompile! ! !Gofer methodsFor: 'accessing' stamp: 'lr 10/1/2009 21:08'! references "Answer a list of references." ^ references! ! !Gofer methodsFor: 'repositories' stamp: 'lr 7/10/2009 16:25'! renggli: aString self url: 'http://source.lukas-renggli.ch/' , aString! ! !Gofer methodsFor: 'accessing' stamp: 'lr 10/2/2009 10:08'! repository "Answer a current repository or nil." ^ repository! ! !Gofer methodsFor: 'accessing' stamp: 'lr 10/2/2009 10:21'! repository: aRepository "Set the repository aRepository as the location for the following package additions." MCRepositoryGroup default addRepository: aRepository. repository := MCRepositoryGroup default repositories detect: [ :each | each = aRepository ] ifNone: [ self error: 'Internal error' ]. repository copyFrom: aRepository! ! !Gofer methodsFor: 'actions' stamp: 'lr 8/20/2009 10:15'! revert "Revert the specified packages to the currently loaded version." ^ self execute: GoferRevert! ! !Gofer methodsFor: 'repositories' stamp: 'lr 7/10/2009 16:29'! saltypickle: aString self url: 'http://squeak.saltypickle.com/' , aString! ! !Gofer methodsFor: 'repositories' stamp: 'lr 7/10/2009 16:28'! squeakfoundation: aString self url: 'http://source.squeakfoundation.org/' , aString! ! !Gofer methodsFor: 'repositories' stamp: 'lr 7/10/2009 16:28'! squeaksource: aString self url: 'http://www.squeaksource.com/' , aString! ! !Gofer methodsFor: 'actions' stamp: 'lr 9/3/2009 11:57'! unload "Update the specified packages, this is the same as loading." ^ self execute: GoferUnload! ! !Gofer methodsFor: 'actions' stamp: 'lr 9/18/2009 18:12'! update "Update the specified packages." ^ self execute: GoferUpdate! ! !Gofer methodsFor: 'repositories' stamp: 'lr 9/7/2009 20:11'! url: aString "Set the repository URL aString as the location for the following package additions." self url: aString username: String new password: String new! ! !Gofer methodsFor: 'repositories' stamp: 'lr 9/7/2009 20:12'! url: aString username: aUsernameString password: aPasswordString "Set the repository URL aString as the location for the following package additions." self repository: (MCHttpRepository location: aString user: aUsernameString password: aPasswordString)! ! !Gofer methodsFor: 'repositories' stamp: 'lr 7/10/2009 16:26'! wiresong: aString self url: 'http://source.wiresong.ca/' , aString! ! Object subclass: #GoferOperation instanceVariableNames: 'model' classVariableNames: '' poolDictionaries: '' category: 'Gofer-Core'! GoferOperation subclass: #GoferLoad instanceVariableNames: 'versions repositories' classVariableNames: '' poolDictionaries: '' category: 'Gofer-Core'! !GoferLoad methodsFor: 'private' stamp: 'lr 10/2/2009 09:57'! addReference: aReference | version | version := aReference versionReference version. version withAllDependenciesDo: [ :dependency | versions addLast: dependency. (repositories at: dependency ifAbsentPut: [ Set new ]) addAll: aReference repositories ]. model addVersion: version! ! !GoferLoad methodsFor: 'private' stamp: 'lr 9/3/2009 11:00'! defaultModel ^ MCVersionLoader new! ! !GoferLoad methodsFor: 'running' stamp: 'lr 10/1/2009 09:22'! execute self model hasVersions ifTrue: [ self model load ]. self updateRepositories. self updateCategories! ! !GoferLoad methodsFor: 'initialization' stamp: 'lr 9/3/2009 11:00'! initialize super initialize. versions := OrderedCollection new. repositories := Dictionary new! ! !GoferLoad methodsFor: 'initialization' stamp: 'lr 10/2/2009 10:14'! initializeOn: aGofer super initializeOn: aGofer. aGofer references do: [ :each | self addReference: each ]! ! !GoferLoad methodsFor: 'private' stamp: 'lr 9/20/2009 13:43'! updateCategories "This method makes sure that the categories are ordered in load-order and as specified in the packages." | categories | categories := OrderedCollection new. versions do: [ :version | version snapshot definitions do: [ :definition | definition isOrganizationDefinition ifTrue: [ definition categories do: [ :category | (categories includes: category) ifFalse: [ categories addLast: category ] ] ] ] ]. (MCOrganizationDefinition categories: categories) postloadOver: nil! ! !GoferLoad methodsFor: 'private' stamp: 'lr 9/3/2009 10:51'! updateRepositories "This code makes sure that all packages have a propre repository assigned, including the dependencies." repositories keysAndValuesDo: [ :version :collection | collection do: [ :repository | version workingCopy repositoryGroup addRepository: repository ] ]! ! !GoferOperation class methodsFor: 'instance creation' stamp: 'lr 9/3/2009 10:28'! new self shouldNotImplement! ! !GoferOperation class methodsFor: 'instance creation' stamp: 'lr 8/20/2009 12:01'! on: aGofer ^ self basicNew initializeOn: aGofer! ! !GoferOperation methodsFor: 'private' stamp: 'lr 8/19/2009 14:01'! defaultModel ^ nil! ! !GoferOperation methodsFor: 'running' stamp: 'lr 8/17/2009 14:40'! execute "Execute the receiving action." self subclassResponsibility! ! !GoferOperation methodsFor: 'initialization' stamp: 'lr 8/19/2009 14:01'! initialize model := self defaultModel! ! !GoferOperation methodsFor: 'initialization' stamp: 'lr 10/2/2009 10:12'! initializeOn: aGofer self initialize! ! !GoferOperation methodsFor: 'accessing' stamp: 'lr 8/20/2009 10:13'! model "Answer the Monticello model of this operation." ^ model! ! GoferOperation subclass: #GoferWorking instanceVariableNames: 'workingCopies' classVariableNames: '' poolDictionaries: '' category: 'Gofer-Core'! GoferWorking subclass: #GoferCommit instanceVariableNames: 'repositories message' classVariableNames: '' poolDictionaries: '' category: 'Gofer-Core'! !GoferCommit methodsFor: 'private' stamp: 'lr 10/2/2009 10:05'! addReference: aPackage requiredCopy: aWorkingCopy repositories: anArray super addReference: aPackage requiredCopy: aWorkingCopy repositories: anArray. repositories at: aWorkingCopy put: anArray! ! !GoferCommit methodsFor: 'running' stamp: 'lr 9/24/2009 17:32'! execute self workingCopies do: [ :workingCopy | workingCopy needsSaving ifTrue: [ self execute: workingCopy ] ]! ! !GoferCommit methodsFor: 'running' stamp: 'lr 10/2/2009 10:08'! execute: aWorkingCopy | version | repositories := repositories at: aWorkingCopy. repositories isEmpty ifFalse: [ self error: 'No repository found for ' , aWorkingCopy packageName printString ]. version := [ aWorkingCopy newVersion ] on: MCVersionNameAndMessageRequest do: [ :notifcation | self message isNil ifTrue: [ message := notifcation outer last ]. notifcation resume: (Array with: notifcation suggestedName with: self message) ]. (repositories at: aWorkingCopy) first storeVersion: version! ! !GoferCommit methodsFor: 'initialization' stamp: 'lr 8/20/2009 12:14'! initialize super initialize. repositories := Dictionary new! ! !GoferCommit methodsFor: 'accessing' stamp: 'lr 10/2/2009 10:12'! message ^ message! ! !GoferCommit methodsFor: 'accessing' stamp: 'lr 10/2/2009 10:12'! message: aString message := aString! ! GoferWorking subclass: #GoferDiff instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Gofer-Core'! !GoferDiff methodsFor: 'private' stamp: 'lr 10/2/2009 10:25'! addReference: aReference requiredCopy: aWorkingCopy repositories: anArray | source target patch | super addReference: aReference requiredCopy: aWorkingCopy repositories: anArray. source := aWorkingCopy package. target := aReference versionReference version. patch := source snapshot patchRelativeToBase: target snapshot. aWorkingCopy modified: patch isEmpty not. self model operations addAll: patch operations! ! !GoferDiff methodsFor: 'private' stamp: 'lr 8/19/2009 14:02'! defaultModel ^ MCPatch operations: OrderedCollection new! ! !GoferDiff methodsFor: 'running' stamp: 'lr 8/19/2009 14:06'! execute self model isEmpty ifFalse: [ self model browse ]! ! GoferWorking subclass: #GoferRecompile instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Gofer-Core'! !GoferRecompile methodsFor: 'running' stamp: 'lr 8/20/2009 11:44'! execute self workingCopies do: [ :copy | self recompile: copy ]! ! !GoferRecompile methodsFor: 'running' stamp: 'lr 8/20/2009 11:47'! recompile: aWorkingCopy aWorkingCopy packageInfo methods do: [ :each | each actualClass recompile: each methodSymbol ]! ! GoferWorking subclass: #GoferUnload instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Gofer-Core'! !GoferUnload methodsFor: 'private' stamp: 'lr 8/19/2009 14:01'! defaultModel ^ MCMultiPackageLoader new! ! !GoferUnload methodsFor: 'running' stamp: 'lr 9/3/2009 11:15'! execute self workingCopies do: [ :copy | self unloadClasses: copy; unloadPackage: copy ]. self model load. self workingCopies do: [ :copy | self cleanup: copy; unregister: copy ]! ! !GoferUnload methodsFor: 'unloading' stamp: 'lr 8/19/2009 13:50'! unloadClasses: aWorkingCopy aWorkingCopy packageInfo classes do: [ :class | (class selectors includes: #unload) ifTrue: [ class unload ] ]! ! !GoferUnload methodsFor: 'unloading' stamp: 'lr 8/19/2009 14:00'! unloadPackage: aWorkingCopy self model unloadPackage: aWorkingCopy package! ! !GoferUnload methodsFor: 'unregistering' stamp: 'lr 8/19/2009 13:49'! unregister: aWorkingCopy self unregisterWorkingCopy: aWorkingCopy. self unregisterRepositories: aWorkingCopy. self unregisterPackageInfo: aWorkingCopy! ! !GoferUnload methodsFor: 'unregistering' stamp: 'lr 8/19/2009 13:50'! unregisterPackageInfo: aWorkingCopy PackageOrganizer default unregisterPackage: aWorkingCopy packageInfo! ! !GoferUnload methodsFor: 'unregistering' stamp: 'lr 8/19/2009 13:50'! unregisterRepositories: aWorkingCopy aWorkingCopy repositoryGroup repositories allButFirst do: [ :repository | MCWorkingCopy allManagers do: [ :copy | (copy repositoryGroup includes: repository) ifTrue: [ ^ self ] ]. MCRepositoryGroup default removeRepository: repository ]! ! !GoferUnload methodsFor: 'unregistering' stamp: 'lr 8/20/2009 11:54'! unregisterWorkingCopy: aWorkingCopy aWorkingCopy unregister! ! GoferWorking subclass: #GoferUpdate instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Gofer-Core'! GoferUpdate subclass: #GoferMerge instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Gofer-Core'! !GoferMerge methodsFor: 'private' stamp: 'lr 8/19/2009 14:01'! defaultModel ^ MCVersionMerger new! ! !GoferMerge methodsFor: 'running' stamp: 'lr 9/24/2009 17:39'! execute [ [ self model merge ] on: MCMergeResolutionRequest do: [ :request | request merger conflicts isEmpty ifTrue: [ request resume: true ] ifFalse: [ request pass ] ] ] valueSupplyingAnswers: #(('No Changes' true)). self workingCopies do: [ :each | self cleanup: each ]! ! GoferUpdate subclass: #GoferRevert instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Gofer-Core'! !GoferRevert methodsFor: 'running' stamp: 'lr 9/19/2009 13:15'! execute self workingCopies do: [ :each | each modified: false ]. super execute! ! !GoferRevert methodsFor: 'private' stamp: 'lr 10/2/2009 10:30'! findVersion: aReference workingCopy: aWorkingCopy repositories: anArray ^ (MCRepositoryGroup withAll: anArray) versionWithInfo: aWorkingCopy ancestors first! ! !GoferUpdate methodsFor: 'private' stamp: 'lr 10/2/2009 10:30'! addReference: aReference workingCopy: aWorkingCopy repositories: anArray super addReference: aReference workingCopy: aWorkingCopy repositories: anArray. self model addVersion: (self findVersion: aReference workingCopy: aWorkingCopy repositories: anArray)! ! !GoferUpdate methodsFor: 'private' stamp: 'lr 9/18/2009 18:13'! defaultModel ^ MCVersionLoader new! ! !GoferUpdate methodsFor: 'running' stamp: 'lr 9/29/2009 22:19'! execute self model hasVersions ifTrue: [ self model load ]. self workingCopies do: [ :each | self cleanup: each ]! ! !GoferUpdate methodsFor: 'private' stamp: 'lr 10/2/2009 10:27'! findVersion: aReference workingCopy: aWorkingCopy repositories: anArray ^ aReference versionReference version! ! !GoferWorking methodsFor: 'private' stamp: 'lr 10/2/2009 10:22'! addReference: aReference self addReference: aReference workingCopy: aReference workingCopy repositories: aReference repositories! ! !GoferWorking methodsFor: 'private' stamp: 'lr 10/2/2009 10:04'! addReference: aPackage requiredCopy: aWorkingCopy repositories: anArray (workingCopies includes: aWorkingCopy) ifTrue: [ ^ self ]. workingCopies addLast: aWorkingCopy. aWorkingCopy requiredPackages reverseDo: [ :each | self addReference: aPackage requiredCopy: each workingCopy repositories: anArray ]! ! !GoferWorking methodsFor: 'private' stamp: 'lr 10/2/2009 10:06'! addReference: aPackage workingCopy: aWorkingCopy repositories: anArray self addReference: aPackage requiredCopy: aWorkingCopy repositories: anArray! ! !GoferWorking methodsFor: 'cleaning' stamp: 'lr 8/19/2009 13:50'! cleanup: aWorkingCopy self cleanupCategories: aWorkingCopy. self cleanupProtocols: aWorkingCopy! ! !GoferWorking methodsFor: 'cleaning' stamp: 'lr 8/19/2009 14:50'! cleanupCategories: aWorkingCopy aWorkingCopy packageInfo systemCategories do: [ :category | (SystemOrganization classesInCategory: category) isEmpty ifTrue: [ SystemOrganization removeSystemCategory: category ] ]! ! !GoferWorking methodsFor: 'cleaning' stamp: 'lr 8/19/2009 14:50'! cleanupProtocols: aWorkingCopy aWorkingCopy packageInfo foreignClasses do: [ :class | (aWorkingCopy packageInfo foreignExtensionCategoriesForClass: class) do: [ :category | (class organization listAtCategoryNamed: category) isEmpty ifTrue: [ class organization removeCategory: category ] ] ]! ! !GoferWorking methodsFor: 'initialization' stamp: 'lr 8/19/2009 13:14'! initialize super initialize. workingCopies := OrderedCollection new! ! !GoferWorking methodsFor: 'initialization' stamp: 'lr 10/2/2009 10:12'! initializeOn: aGofer super initializeOn: aGofer. aGofer references do: [ :each | self addReference: each ]! ! !GoferWorking methodsFor: 'accessing' stamp: 'lr 9/24/2009 16:55'! workingCopies "Answer the working copies to be operated on." ^ workingCopies! ! Object subclass: #GoferReference instanceVariableNames: 'name repository' classVariableNames: '' poolDictionaries: '' category: 'Gofer-Core'! GoferReference subclass: #GoferPackageReference instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Gofer-Core'! !GoferPackageReference methodsFor: 'accessing' stamp: 'lr 10/1/2009 21:03'! versionReference | versions | versions := self findVersions: [ :each | each packageName = self name ]. ^ versions isEmpty ifTrue: [ self error: 'No versions for package ' , self name printString , ' found.' ] ifFalse: [ versions last ]! ! !GoferPackageReference methodsFor: 'accessing' stamp: 'lr 10/1/2009 20:19'! workingCopy ^ MCWorkingCopy allManagers detect: [ :each | each packageName = self name ] ifNone: [ nil ]! ! GoferReference subclass: #GoferPrefixReference instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Gofer-Core'! !GoferPrefixReference methodsFor: 'accessing' stamp: 'lr 10/2/2009 09:45'! versionReference "Answer the 'latest' version that matches the given prefix. If there are multiple packages matching the given prefix, only consider the package with the shortest name." | versions packageNames | versions := self findVersions: [ :each | each name beginsWith: self name ]. versions isEmpty ifTrue: [ self error: 'No version with prefix ' , self name printString , ' found.' ]. packageNames := versions collect: [ :each | each packageName ]. packageNames := packageNames asSet asSortedCollection: [ :a :b | a size < b size ]. packageNames size > 1 ifTrue: [ versions := versions select: [ :each | each packageName = packageNames first ] ]. ^ versions last! ! !GoferPrefixReference methodsFor: 'accessing' stamp: 'lr 10/1/2009 20:15'! workingCopy "Answer the working copy that is a prefix to the given name. If there are multiple matches answer the shortest package name." | workingCopies | workingCopies := MCWorkingCopy allManagers select: [ :each | self name beginsWith: each packageName ]. workingCopies isEmpty ifTrue: [ ^ nil ]. workingCopies := workingCopies asSortedCollection: [ :a :b | a packageName size < b packageName size ]. ^ workingCopies first! ! !GoferReference class methodsFor: 'instance-creation' stamp: 'lr 10/1/2009 17:06'! name: aString ^ self basicNew initializeName: aString! ! !GoferReference class methodsFor: 'instance-creation' stamp: 'lr 10/2/2009 09:41'! name: aString repository: aRepository ^ (self name: aString) setRepository: aRepository! ! !GoferReference methodsFor: 'comparing' stamp: 'lr 10/1/2009 19:47'! = aReference ^ self species = aReference species and: [ self name = aReference name ]! ! !GoferReference methodsFor: 'utilities' stamp: 'lr 10/1/2009 19:37'! findVersions: aBlock "Answer a sorted array of versions that match aBlock." | versions | versions := SortedCollection new. self repositories do: [ :repo | (GoferVersionCache versionsIn: repo) do: [ :version | (aBlock value: version) ifTrue: [ versions add: version ] ] ]. ^ versions asArray! ! !GoferReference methodsFor: 'comparing' stamp: 'lr 10/1/2009 19:47'! hash ^ self name hash! ! !GoferReference methodsFor: 'initialization' stamp: 'lr 10/1/2009 17:06'! initializeName: aString name := aString ! ! !GoferReference methodsFor: 'accessing' stamp: 'lr 10/1/2009 21:33'! name ^ name! ! !GoferReference methodsFor: 'printing' stamp: 'lr 10/1/2009 20:22'! printOn: aStream super printOn: aStream. aStream nextPutAll: ' name: '; print: self name ! ! !GoferReference methodsFor: 'accessing' stamp: 'lr 10/1/2009 21:33'! repositories "Answer an ordered collection of repositories." | repositories | repositories := OrderedCollection new. self repository isNil ifFalse: [ self repository isRepositoryGroup ifFalse: [ repositories add: self repository ] ifTrue: [ repositories addAll: self repository repositories ] ]. repositories := repositories select: [ :each | each isValid and: [ each ~= MCCacheRepository default ] ]. ^ repositories! ! !GoferReference methodsFor: 'accessing' stamp: 'lr 10/1/2009 21:33'! repository ^ repository! ! !GoferReference methodsFor: 'initialization' stamp: 'lr 10/1/2009 19:39'! setRepository: aRepository repository := aRepository! ! !GoferReference methodsFor: 'accessing' stamp: 'lr 10/1/2009 20:05'! versionReference "Answer a version reference that can be directly operated on." self subclassResponsibility! ! !GoferReference methodsFor: 'accessing' stamp: 'lr 10/2/2009 09:52'! workingCopy "Answer a working copy of the receiver, or nil if the package is not loaded." self subclassResponsibility! ! GoferReference subclass: #GoferVersionReference instanceVariableNames: 'packageName authorName versionNumber' classVariableNames: '' poolDictionaries: '' category: 'Gofer-Core'! !GoferVersionReference methodsFor: 'comparing' stamp: 'lr 10/1/2009 21:02'! <= aVersion ^ self packageName = aVersion packageName ifFalse: [ self packageName <= aVersion packageName ] ifTrue: [ self versionNumber <= aVersion versionNumber ]! ! !GoferVersionReference methodsFor: 'accessing' stamp: 'lr 10/1/2009 21:01'! authorName "Answer the author name of this version." ^ authorName! ! !GoferVersionReference methodsFor: 'initialization' stamp: 'lr 10/1/2009 21:01'! initializeName: aString | fullName | super initializeName: aString. fullName := aString last isDigit ifTrue: [ aString ] ifFalse: [ (aString copyUpToLast: $.) copyUpTo: $( ]. packageName := fullName copyUpToLast: $-. authorName := (fullName copyAfterLast: $-) copyUpTo: $.. versionNumber := ((fullName copyAfterLast: $-) copyAfter: $.) asInteger! ! !GoferVersionReference methodsFor: 'accessing' stamp: 'lr 10/1/2009 21:01'! packageName "Answer the package name of this version." ^ packageName! ! !GoferVersionReference methodsFor: 'accessing' stamp: 'lr 10/1/2009 20:23'! version "Answer the monticello version of the receiver." ^ self repository loadVersionFromFileNamed: self name , '.mcz'! ! !GoferVersionReference methodsFor: 'accessing' stamp: 'lr 10/1/2009 21:02'! versionNumber "Answer the version number of this version." ^ versionNumber! ! !GoferVersionReference methodsFor: 'accessing' stamp: 'lr 10/1/2009 19:56'! versionReference ^ self! ! !GoferVersionReference methodsFor: 'accessing' stamp: 'lr 10/1/2009 20:19'! workingCopy ^ MCWorkingCopy allManagers detect: [ :each | each packageName = packageName ] ifNone: [ nil ]! ! !MCRepository methodsFor: '*gofer-testing' stamp: 'lr 9/24/2009 17:02'! isRepositoryGroup ^ false! ! !MCRepositoryGroup class methodsFor: '*gofer-core' stamp: 'lr 10/2/2009 10:29'! withAll: anArray ^ anArray inject: self new into: [ :group :repo | group addRepository: repo ]! ! !MCRepositoryGroup methodsFor: '*gofer-testing' stamp: 'lr 9/24/2009 17:01'! isRepositoryGroup ^ true! ! TestCase subclass: #GoferTest instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Gofer-Test'! GoferTest subclass: #GoferApiTest instanceVariableNames: 'gofer' classVariableNames: '' poolDictionaries: '' category: 'Gofer-Test'! !GoferApiTest methodsFor: 'running' stamp: 'lr 10/1/2009 21:59'! setUp super setUp. gofer := Gofer new! ! !GoferApiTest methodsFor: 'testing-repositories' stamp: 'lr 10/1/2009 21:59'! testCroquet gofer croquet: 'Hermes'. self assert: gofer repository locationWithTrailingSlash = 'http://hedgehog.software.umn.edu:8888/Hermes/'! ! !GoferApiTest methodsFor: 'testing-repositories' stamp: 'lr 10/1/2009 21:59'! testImpara gofer impara: 'Tweak'. self assert: gofer repository locationWithTrailingSlash = 'http://source.impara.de/Tweak/'! ! !GoferApiTest methodsFor: 'testing' stamp: 'lr 10/1/2009 22:03'! testInitialized self assert: gofer repository isNil. self assert: gofer references isEmpty! ! !GoferApiTest methodsFor: 'testing-repositories' stamp: 'lr 10/1/2009 21:59'! testRenggli gofer renggli: 'pier'. self assert: gofer repository locationWithTrailingSlash = 'http://source.lukas-renggli.ch/pier/'! ! !GoferApiTest methodsFor: 'testing-accessing' stamp: 'lr 10/1/2009 21:59'! testRepository gofer repository: MCDirectoryRepository new. self assert: (gofer repository isKindOf: MCDirectoryRepository)! ! !GoferApiTest methodsFor: 'testing-repositories' stamp: 'lr 10/1/2009 21:59'! testSaltypickle gofer saltypickle: 'GraphViz'. self assert: gofer repository locationWithTrailingSlash = 'http://squeak.saltypickle.com/GraphViz/'! ! !GoferApiTest methodsFor: 'testing-repositories' stamp: 'lr 10/1/2009 21:59'! testSqueakfoundation gofer squeakfoundation: '39a'. self assert: gofer repository locationWithTrailingSlash = 'http://source.squeakfoundation.org/39a/'! ! !GoferApiTest methodsFor: 'testing-repositories' stamp: 'lr 10/1/2009 21:59'! testSqueaksource gofer squeaksource: 'Seaside29'. self assert: gofer repository locationWithTrailingSlash = 'http://www.squeaksource.com/Seaside29/'! ! !GoferApiTest methodsFor: 'testing-accessing' stamp: 'lr 10/1/2009 21:59'! testUrl gofer url: 'http://source.lukas-renggli.ch/pier'. self assert: (gofer repository isKindOf: MCHttpRepository). self assert: (gofer repository locationWithTrailingSlash = 'http://source.lukas-renggli.ch/pier/'). self assert: (gofer repository user isEmpty). self assert: (gofer repository password isEmpty)! ! !GoferApiTest methodsFor: 'testing-accessing' stamp: 'lr 10/1/2009 21:59'! testUrlUsernamePassword gofer url: 'http://source.lukas-renggli.ch/pier' username: 'foo' password: 'bar'. self assert: (gofer repository isKindOf: MCHttpRepository). self assert: (gofer repository locationWithTrailingSlash = 'http://source.lukas-renggli.ch/pier/'). self assert: (gofer repository user = 'foo'). self assert: (gofer repository password = 'bar')! ! !GoferApiTest methodsFor: 'testing-repositories' stamp: 'lr 10/1/2009 21:59'! testWiresong gofer wiresong: 'ob'. self assert: gofer repository locationWithTrailingSlash = 'http://source.wiresong.ca/ob/'! ! GoferTest subclass: #GoferReferenceTest instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'Gofer-Test'! !GoferReferenceTest methodsFor: 'testing' stamp: 'lr 10/2/2009 09:43'! testPackageShouldFindLatestVersion | packageReference versionReference | packageReference := GoferPackageReference name: 'Gofer' repository: self goferRepository. versionReference := packageReference versionReference. self assert: versionReference packageName = 'Gofer'. self assert: versionReference versionNumber > 55! ! !GoferReferenceTest methodsFor: 'testing' stamp: 'lr 10/2/2009 09:48'! testPackageShouldFindWorkingCopy | packageReference workingCopy | packageReference := GoferPackageReference name: 'Gofer'. workingCopy := packageReference workingCopy. self assert: workingCopy packageName = 'Gofer'! ! !GoferReferenceTest methodsFor: 'testing' stamp: 'lr 10/2/2009 09:48'! testPrefixShouldFindLatestVersion1 | packageReference versionReference | packageReference := GoferPrefixReference name: 'Gofer-lr' repository: self goferRepository. versionReference := packageReference versionReference. self assert: versionReference packageName = 'Gofer'. self assert: versionReference authorName = 'lr'. self assert: versionReference versionNumber > 55! ! !GoferReferenceTest methodsFor: 'testing' stamp: 'lr 10/2/2009 09:47'! testPrefixShouldFindLatestVersion2 | packageReference versionReference | packageReference := GoferPrefixReference name: 'Bogus' repository: self bogusRepository. versionReference := packageReference versionReference. self assert: versionReference packageName = 'Bogus'. self assert: versionReference versionNumber > 10! ! !GoferReferenceTest methodsFor: 'testing-accessing' stamp: 'lr 10/2/2009 10:32'! testShouldRememberName | package | package := GoferPackageReference name: 'Gofer'. self assert: package name = 'Gofer'. package := GoferVersionReference name: 'Gofer-lr.34'. self assert: package name = 'Gofer-lr.34'! ! !GoferReferenceTest methodsFor: 'testing-accessing' stamp: 'lr 10/2/2009 10:33'! testShouldRememberRepository | package | package := GoferPackageReference name: 'Gofer' repository: self goferRepository. self assert: package repository locationWithTrailingSlash = 'http://source.lukas-renggli.ch/flair/'! ! !GoferReferenceTest methodsFor: 'testing' stamp: 'lr 10/2/2009 09:51'! testVersionShouldFindLatestVersion | versionReference otherReference | versionReference := GoferVersionReference name: 'Gofer-lr.18' repository: self goferRepository. otherReference := versionReference versionReference. self assert: versionReference packageName = 'Gofer'. self assert: versionReference authorName = 'lr'. self assert: versionReference versionNumber = 18. self assert: otherReference = versionReference. self assert: otherReference == versionReference! ! !GoferReferenceTest methodsFor: 'testing' stamp: 'lr 10/2/2009 09:51'! testVersionShouldFindWorkingCopy | versionReference workingCopy | versionReference := GoferVersionReference name: 'Gofer-lr.18' repository: self goferRepository. workingCopy := versionReference workingCopy. self assert: workingCopy packageName = 'Gofer'! ! GoferTest subclass: #GoferScenarioTest instanceVariableNames: 'gofer' classVariableNames: '' poolDictionaries: '' category: 'Gofer-Test'! !GoferScenarioTest methodsFor: 'assertions' stamp: 'lr 8/20/2009 20:58'! assertClass: aClassSymbol self assert: (Smalltalk hasClassNamed: aClassSymbol)! ! !GoferScenarioTest methodsFor: 'assertions' stamp: 'lr 8/20/2009 21:04'! assertClass: aClassSymbol selector: aMethodSymbol self assertClass: aClassSymbol. self assert: ((Smalltalk at: aClassSymbol) includesSelector: aMethodSymbol)! ! !GoferScenarioTest methodsFor: 'utilities' stamp: 'lr 8/20/2009 21:03'! compile: aClassSelector method: aString self assertClass: aClassSelector. (Smalltalk at: aClassSelector) compile: aString.! ! !GoferScenarioTest methodsFor: 'utilities' stamp: 'lr 8/20/2009 21:04'! evaluate: aClassSelector selector: aMethodSelector self assertClass: aClassSelector selector: aMethodSelector. ^ (Smalltalk at: aClassSelector) new perform: aMethodSelector! ! !GoferScenarioTest methodsFor: 'utilities' stamp: 'lr 8/20/2009 21:14'! hasPackage: aString | package | package := MCWorkingCopy allManagers detect: [ :each | each packageName = aString ] ifNone: [ nil ]. ^ package notNil! ! !GoferScenarioTest methodsFor: 'running' stamp: 'lr 10/2/2009 09:53'! setUp gofer := Gofer new. gofer wiresong: 'ob'; addPackage: 'BogusInfo'! ! !GoferScenarioTest methodsFor: 'running' stamp: 'lr 8/20/2009 21:12'! tearDown [ gofer unload ] on: Error do: [ :err | "assume it is not there" ]! ! !GoferScenarioTest methodsFor: 'testing' stamp: 'lr 9/28/2009 23:39'! testCommit "dunno how to test yet"! ! !GoferScenarioTest methodsFor: 'testing' stamp: 'lr 9/28/2009 23:39'! testDiff "dunno how to test yet"! ! !GoferScenarioTest methodsFor: 'testing' stamp: 'lr 8/20/2009 21:15'! testLoad self shouldnt: [ gofer load ] raise: Error. self assert: (self hasPackage: 'Bogus'); assertClass: #BogusA. self assert: (self hasPackage: 'BogusExt'); assertClass: #BogusA selector: #isFake. self assert: (self hasPackage: 'BogusInfo'); assertClass: #BogusInfo! ! !GoferScenarioTest methodsFor: 'testing' stamp: 'lr 9/28/2009 23:40'! testMerge "dunno how to test yet"! ! !GoferScenarioTest methodsFor: 'testing' stamp: 'lr 8/20/2009 21:13'! testRecompile gofer load. self shouldnt: [ gofer recompile ] raise: Error! ! !GoferScenarioTest methodsFor: 'testing' stamp: 'lr 8/20/2009 21:09'! testRevert gofer load. self assert: (self evaluate: #BogusA selector: #isFake). self compile: #BogusA method: 'isFake ^ false'. self deny: (self evaluate: #BogusA selector: #isFake). self shouldnt: [ gofer revert ] raise: Error. self assert: (self evaluate: #BogusA selector: #isFake)! ! !GoferScenarioTest methodsFor: 'testing' stamp: 'lr 8/20/2009 21:15'! testUnload gofer load. self shouldnt: [ gofer unload ] raise: Error. self deny: (self hasPackage: 'Bogus'). self deny: (self hasPackage: 'BogusExt'). self deny: (self hasPackage: 'BogusInfo')! ! !GoferScenarioTest methodsFor: 'testing' stamp: 'lr 9/19/2009 14:13'! testUpdate gofer load. self shouldnt: [ gofer update ] raise: Error. self assert: (self hasPackage: 'Bogus'). self assert: (self hasPackage: 'BogusExt'). self assert: (self hasPackage: 'BogusInfo')! ! !GoferTest class methodsFor: 'testing' stamp: 'lr 10/1/2009 22:00'! isAbstract ^ self name = #GoferTest! ! !GoferTest class methodsFor: 'accessing' stamp: 'lr 10/1/2009 21:53'! packageNamesUnderTest ^ #('Gofer')! ! !GoferTest methodsFor: 'accessing' stamp: 'lr 10/1/2009 21:58'! bogusRepository ^ MCHttpRepository location: 'http://source.wiresong.ca/ob' user: '' password: ''! ! !GoferTest methodsFor: 'accessing' stamp: 'lr 10/1/2009 21:58'! goferRepository ^ MCHttpRepository location: 'http://source.lukas-renggli.ch/flair' user: '' password: ''! ! !GoferTest methodsFor: 'running' stamp: 'lr 10/1/2009 21:59'! runCase GoferVersionCache during: [ super runCase ]! !