PBS internal architecture
This page gives a quick description of PBS architecture.
 The file system
The best way to understand PBS architecture is to look at the files under PBS directory: files organization reflects the architecture.
The architecture has respected the following principles:
- Most of the code has to be platform-independent. Every platform-specific code has to be isolated.
- The architecture has to be flexible in order for plugins to be developed easily. MVC (Mode/View/Controller) pattern has been chosen for that purpose.
Following those principles, the architecture has been set as following:
- lib/ directory contains the main platform-independent code, organized in MVC pattern:
- lib/Model/ directory contains the Model part
- lib/Windows/ directory contains the View part
- lib/Controller/ directory contains the Controller part
- lib/Plugins/ directory contains the plugins
- Launch/ directory contains the platform-specific code
- ext/ directory contains external dependencies to PBS and its plugins
- Distribution/ directory contains executables that issue PBS releases
Here are some details about the files and directories ([dev] means that this directory is only present in the source code of PBS: it will not be part of the releases):
This directory contains the main Ruby file that launches PBS: Launcher.rb.
It handles platform specific operations before calling PBS library's entry point.
Each platform that PBS may handle has to declare a specific file that will contain all platform-dependent code in the corresponding sub-directory:
- Platform name/
These directories (1 per platform) contain the following:
- PlatformInfo.rb This file contains the class to be defined for a given platform. See the PlatformInfo API to get more details.
This directory will contain specific programs needed by PBS executable to run correctly. Its content depends on the way PBS has been packaged (check the Distribution/ directory for more info).
- Platform name/ These directories (1 per platform) contain the following:
This directory contains the PBS library. It has an entry point (pbs.rb), and plenty of directories containing PBS code itself.
Normally, every code in this directory is platform-independent. This means that those files should run the same way on Windows, Linux, Mac OS... Platform-dependent considerations were isolated in the Launch/ directory.
PBS is then architectured on an MVC basis (Model/View/Controller). This architecture separates the underlying data objects (Model) from the way they are represented (View) and links everythin with a simple Controller API.
This directory stores the Model used in PBS. It is very simple:
- Shortcut.rb: Class used to handle Shortcuts.
- Tag.rb: Class used to handle Tags.
- MultipleSelection.rb: Class used to handle the selection of several Shortcuts and Tags. This is used for example for Copy/Paste or Drag and Drop.
- MissingDependencies.rb: Class used to store missing libraries and gems dependencies for plugins.
- Common.rb: Some commonly used methods for the Model.
This directory stores every window (dialog, panel...).
- Main.rb: The main PBS window. This file can be seen as the main View. It uses the following files:
- PBSTreeCtrl.rb: Tree view used in the main window. It has been separated as it could be used by other plugins also, and contains quite a load of code.
- AboutDialog.rb: The About dialog.
- OptionsDialog.rb: The Options dialog. It uses the following files:
- BugReportDialog.rb: Dialog used when a bug occurs.
- EditShortcutDialog.rb: Dialog used to edit a Shortcut. It uses the following files:
- ContentMetadataPanel.rb: Panel used to display Shortcuts metadata (name, icon...) and content (URL...).
- EditTagDialog.rb: Dialog used to edit a Tag. It uses the following files:
- TagMetadataPanel.rb: Panel used to display Tags metadata (name, icon...).
- Some common dialogs, used in many places:
- ChooseIconDialog.rb: Dialog used to select an icon.
- DependenciesLoaderDialog.rb: Dialog used to display missing libraries or gems dependencies for plugins. It uses the following files:
- DependenciesGroupWindow.rb: Window used to display a group of libraries or gems dependencies.
- ResolveConflictDialog.rb: Base window used to display a conflict between 2 objects and give options on resolving the conflict. It is specialized in the following files:
- Main.rb: The main PBS window. This file can be seen as the main View. It uses the following files:
This directory stores all files for the Controller part of PBS:
- Controller.rb: The Controller object, handling the Model.
- Readers.rb: The Read-only interface of the Controller: the API to use when we need to access the Model for read (retrieve the list of shortcuts...).
- Actions.rb: The Read-Write interface of the Controller: the API to use when we need to modifiy the Model (change a Tag's name...).
- Notifiers.rb: The notification interface of the Controller: the API to use when we need to notify Views of a specific change (notify that a specific shortcut's icon has changed...).
- GUIHelpers.rb: Another interface of the Controller: the API giving some handy methods to be used in a view (adding a menu item corresponding to a given command...).
- UndoableAtomicOperations.rb: This file is used internally by the Controller. It defines the basic operations that can be done on the Model (CRUD interface: Create/Retrieve/Update/Delete). Normally, every method from the Actions.rb file should use the classes defined in UndoableAtomicOperations.rb to manipulate data. This will ensure Undo/Redo consistency.
This directory stores every image used in PBS.
This directory contains all the files (except images, stored in Graphics/) that define plugins. Each plugin is made of 2 files (at least):
- A description file (extension .desc.rb): giving names, icons, dependencies ... for the plugin
- The plugin file itself (extension .rb), which API depends on the plugin type (importer, exporter, integration...)
Each sub-directory corresponds to a given plugin type. All the plugins stored in one of these sub-directories share the same API, specific to this plugin type.
- Imports/ Directory containing importers (part of the File/Import menu). See Import plugins for details.
Directory containing exporters (part of the File/Export menu). See Export plugins for details.
Directory containing integration plugins (an integration plugin integrates PBS in a part of your OS (Tray...). See Integration plugins for details.
Directory containing commands (a command is an operation that can be put in menus). See Commands plugins for details.
Directory containing Shortcuts types (Shell command, Internet bookmark...). See Types plugins for details.
- Model/ This directory stores the Model used in PBS. It is very simple:
- Distribution/ [dev]
This directory contains all files needed to issue a release of PBS on a given platform. All platform specific code is put in a sub-directory named by the platform.
- Distribution/Release.rb: Main executable script that creates a Release. See the Release system documentation for further details.
- Platform name/
Directory containing platform specific files for releases. Many files can be present in this directory, all specific to the given platform, all of them used to issue correctly a release of PBS on the given platform. Only the following files will always be defined:
- ReleaseInfo.rb: File containing platform specific parts of the Release process.
This directory contains the external libraries (dependencies) needed by PBS to run.
The content of this directory can evolve with plugins and features additions.
Here are the current ones:
- rubygems/ This is a simple installation of the RubyGems library, needed to automatically install gem dependencies of plugins.
The RubyZip gem is used to unzip downloaded libraries needed to some plugins
This directory contains all libraries that are Windows dependent. On Linux distributions, this directory won't be used. Here is its content:
This directory contains dynamic libraries (DLLs) needed. Here is its content:
- sqlite3.dll/ The sqlite3 DLL is needed for some plugins (Firefox and GoogleChrome).
The nokogiri gem, used by the HTML importer/exporter.
The SQLite3/Ruby gem, used by Firefox and GoogleChrome plugins.
The wxRuby gem (the most important one - the GUI layer).
ZLib library, needed by RubyZip gem.
- libs/ This directory contains dynamic libraries (DLLs) needed. Here is its content:
 Packaging concerns
As PBS is meant to be installed on hosts that do not have Ruby (Windows and Linux from the shelf releases), it has been decided to provide a facility to ship Ruby with PBS. Very quickly, this requirement could be easily extended to any dependency PBS or its plugins could have. Dependencies can be of many types: Ruby gems, dynamic libraries, weird dependencies needing extra steps (compilation...). Moreover, systems that already have part of needed dependencies should not have to download all the dependencies.
Therefore it has been decided to create a dynamic, modular and generic way to handle dependencies of a Ruby application using wxRuby. Here are the main requirements of this dependencies loader:
- Being dynamic: people might ship PBS on a USB stick. Depending on the computer it will be run on, PBS will have to adapt and download at startup time the needed dependencies.
- Being modular: dependencies should not be part of a single monolith: people already having some dependencies should not download them anymore. This implies that the dependencies loader should be able to test if a dependency is present in the system, even if it was not part of a previous PBS installation.
- Being generic: dependencies can be of plenty different flavors: gems, libraries to downloads, sources to compile, image files...
 Startup steps
In regards to dependencies handling, here are the startup steps of PBS:
- Test if Ruby is accessible on the platform, and choose either platform's Ruby, or embedded Ruby to execute PBS' launcher (Launch/Launcher.rb)
- Read platform specific parameters (Launch/<Platform>/PlatformInfo.rb)
- Test if wxRuby is accessible on the platform. If not, try to download it (warn user about the download)
- Execute PBS main file (lib/pbs.rb)
- Read every plugin's description (lib/Plugins/<PluginType>/<PluginName>.desc.rb), and get dependencies from it.
- For each dependency, test if it is present, and download it if needed (a dialog is shown, proposing where to install each dependency).
- Execute main wx application loop.
 Dependencies installation
Each dependency (except Ruby and wxRuby) can be installed in many places, depending on the way the user handles its PBS installation. Basically, the following user needs have been considered:
- Install on local PBS installation, as the user might want to take its whole PBS installation on a single USB stick without installing anything an the computers it uses it on. This location is the ext/<PlatformName>/ directory.
- Install on local computer, as the user might know which computers it will use very often, and accessing dependencies from its USB key might be costly (slow USB keys).
- Install on local user account, as the user might have a Read-Only media where PBS is installed (Read-Only USB key) and might not have administrator privileges on the computers it is running on.
For Gems dependencies installation, PBS will first test if RubyGems is part of the platform's environment. If it is not, it will use a local PBS one.
For Ruby dependency, unfortunately this cannot be downloaded dynamically (except using a third-party installation process, not involving Ruby itself). Therefore it has to be shipped in the PBS release downloaded by the user itself.
For wxRuby dependency, there is no way to use wxRuby itself to display the dialog of installation location. Therefore, the platform specific class provides a single method displaying messages on the user's screen, and those messages are used to warn the user about the installation of wxRuby if needed. In this case wxRuby is installed in the local PBS installation.
There are 2 main points to consider in packaging PBS:
- Package Ruby itself for platforms not having it.
- Package PBS.
- Package PBS' dependencies.
 Solutions tried
Please note that the study was performed during June 2009. Things might have changed until now.
Many existing products have been considered:
Unfortunately, clever solutions that propose to ship only useful Ruby libraries to a Ruby program cannot be applicable to a plugins oriented application: People not having Ruby installed on their computer should be able to develop easily plugins for PBS, without adding native Ruby libraries. Therefore packaged Ruby should include all native Ruby libraries (which represent around 4 Mb for Ruby 1.8.5).
A special mention here for the Crate project, which IMHO is the best solution to package Ruby applications: compiling statically Ruby libraries (part or complete), and using an SQLite3 database for Ruby files. However this solution could not be used yet in PBS for the following reasons:
- The only way to make Crate compile Ruby for Windows is through Cygwin. 2 main drawbacks:
- wxRuby does not work under Cygwin
- Final executable has to ship Cygwin dynamic libraries alongside (10 Mb more)
- Plugin Ruby files have to be part of the SQLite3 database. There is no way yet to require using the legacy method once Crate is used. Plugin Ruby files should not be part of the SQLite3 database.
Once those requirements will be met, it will be possible to use Crate.
OCRA could not be used for the following reasons:
- Missing a way to get the directory where the original executable is put. This feature is useful to parse for plugin files. Unfortunately, __FILE__ and Dir.getwd return a temporary path where files were extracted. Note: This has changed with OCRA 1.1.2, with variable OCRA_EXECUTABLE.
- No easy way to put all native Ruby libraries in the distribution.
RubyScript2Exe could not be used for the following reasons:
- Bug with RubyGems 3.1.2, preventing the executable to be generated
Exerb is used to compile a basic Ruby file without libraries. As the documentation was only Japanese, I could not work around this limitation.
 Final solution
Current packaging is made like this:
- Packaging Ruby itself has been done using AllInOneRuby (in Launch/i386-mswin32/bin/rubyw-1.8.5.exe).
- The executable is compiled using Exerb, and just calls either platform's rubyw.exe, or the shipped AllInOneRuby.
- The icon has been added to the executable file using edicon.exe, from OCRA project.
- Everything is packaged in an installer, made using NSIS ()
 Different releases
In order to let users download the minimum dependencies they need, the following flags are applied to releases (In the installer named pbs_<Version><Flags>_setup.exe - pbs_0.0.5RGWE_setup.exe):
- R: Means that Ruby is part of this release.
- G: Means that RubyGems is part of this release.
- W: Means that wxRuby is part of this release.
- E: Means that external dependencies are part of this release (dependencies needed for plugins).