PackageDir
By Sam Phillips - Posted on January 21st, 2008
Tagged:
I've become spoiled with Python's module and package system. So here I give
you a half baked (but mostly working) alternative package loader.
The usage is pretty simple. Given a directory "MyPackage" with some *.st files
and a directory "MyPackage/Subdir" with more *.st files:
"Load the PackageDirLoader and PackageDir classes" st> FileStream fileIn: 'PackageDir.st' FileStream "Load all of the *.st files in Directory "MyPackage" into Namespace Package" st> PackageDirLoader loadPackage: 'MyPackage' Package "Load all of the *.st files in Directory "MyPackage/Subdir" into Namespace Package.Subdir" st> PackageDirLoader loadPackage: 'MyPackage.Subdir'
So Enjoy!
Object subclass: PackageDirLoader [
Loaded := nil.
SearchPaths := nil.
PackageDirLoader class >> initialize [
Loaded := Set new.
SearchPaths := OrderedCollection new.
]
PackageDirLoader class >> loadPackage: aString [
^ self loadPackageSpec: (self packageSpecFor: aString)
]
PackageDirLoader class >> packageSpecFor: aString [
^ (aString subStrings: $.) collect: [:comp |
comp asSymbol
]
]
PackageDirLoader class >> loadPackageSpec: packageSpec [
| package |
package := self findPackageDir: packageSpec.
^ package load
]
PackageDirLoader class >> pathForPackage: packageList [
^ (packageList collect: [:s | s asString ]) join: '/'
]
PackageDirLoader class >> searchPaths [
^ { Directory working }, SearchPaths
]
PackageDirLoader class >> addSearchPath: aDir [
SearchPaths add: aDir
]
PackageDirLoader class >> findPackageDir: packageSpec [
| path |
path := self pathForPackage: packageSpec.
self searchPaths do: [:p || dir |
dir := Directory name: (p, '/', path).
dir exists ifTrue: [
^ PackageDir onDir: dir name: packageSpec
]
].
self error: 'Package not found'
]
PackageDirLoader class >> loaded: packageSpec [
Loaded add: packageSpec
]
PackageDirLoader class >> hasLoaded: packageSpec [
^ Loaded includes: packageSpec
]
]
Object subclass: PackageDir [
| dir packageSpec |
PackageDir class >> onDir: dir name: packageSpec [
^ self new
dir: dir;
packageSpec: packageSpec
yourself
]
dir [
^ dir
]
dir: anObject [
dir := anObject
]
packageSpec [
^ packageSpec
]
packageSpec: anObject [
packageSpec := anObject
]
packageNs [
| ns |
ns := Namespace current.
packageSpec do: [:n |
ns := ns addSubspace: n
].
^ ns
]
load [
| origNs ns |
ns := self packageNs.
(PackageDirLoader hasLoaded: packageSpec) ifTrue: [
^ self packageNs.
].
origNs := Namespace current.
[
Namespace current: ns.
dir namesMatching: '*.st' do: [:f |
('Loading file ', f) displayNl.
FileStream fileIn: f.
].
PackageDirLoader loaded: self packageSpec.
] ensure: [ Namespace current: origNs ].
^ ns
]
]
Eval [
PackageDirLoader initialize
]

Note that you could just say
Though that transition hasn't happened yet in kernel code.
Also, you may want to add Directory kernel, '../', Smalltalk version and a few others (involving Directory kernel, Directory userBase and/or Directory image) to the default search paths.
I wasn't sure about the class var notation. Thanks for the tip.
I was pretty sure that there would need to be more directories scanned
that's why I added, PackageDirLoader class >> addSearchPath:. :)