Well it's inevitable that in the not so distant future I will have to
deal with standalonifying an application again.
However this time around it's a Rucola application and so it's time to
deal with this important matter.
Now although I was very delighted that Jonathan Paisley took the
effort to write standalonify.rb for RubyCocoa
a while back, it does have it's problems and thing that can be done
better IMO.
I have some ideas about what should be done differently, but I thought
I'd throw it out here for (possible) discussion first.
First of all for those of you who do not know how standalonify works,
a simplified rundown:
- standalonify.rb first overrides OSX.NSApplicationMain(), so that any
call to that function will not really start your application.
- Then it loads your rb_main.rb file, which resides in the source root
in a regular RubyCocoa app, which in it's turn
will "require" all the libraries needed by your application. And
eventually halt the evaluation when it reaches the call to
OSX.NSApplicationMain()
- Then it goes through all the required libraries (loaded features)
and checks whether or not they're gems and where they are on the
filesystem.
- It will then copy gems into MyApp.app/Contents/Resources/Gems and
non-gem libraries into MyApp.app/Contents/Resources/ThirdParty
and alter the rb_main.rb file to reset the load paths at the top of
the file so that a regular require will first look in the ThirdParty
directory
and RubyGems will look in the Gems directory.
Now this works most of the times, but in my opinion it's not clean
enough.
First of all, all the source is evaluated, which *could* lead to weird
side effects.
Second, if I've bundled everything I actually would like it to not use
RubyGems at all.
Not that I don't think RubyGems is a great system, but it does add a
lot of overhead
which is simply not needed in a Release environment.
So configuration-wise my idea would look more like Merb does it.
Eg, in one file config/environment.rb (maybe change the filename to
dependencies or something along that lines?)
and not scattering "require" statements all over the place (other
files):
Rucola::Initializer.run do |config|
# regular libraries
config.dependency 'rexml'
# gems
config.dependency 'git'
config.dependency 'active_support', '> 2.0.1'
end
Then we would have a rake task like: build:bundle,
maybe even :all / :gems tasks depending on if you want to be somewhat
ruby version independent.
This task will only have to run the configuration for Initializer (the
file above)
and it will then know all of it's dependencies, so no more evaluating
all the source.
It should then still load all those libraries to be able to figure out
which libraries they in turn rely on,
but that should not give any problems since they are libraries and are
not supposed to actually run any code.
Then however I would like to copy all the needed libraries to eg
vendor/external (suggestions for a better name?).
And not make any distinctions anymore between gem and non-gem libraries.
Now I have no idea if this is even possible, but in my naive idea this
would be ideal.
Since then we don't have to start RubyGems anymore for loading libs
that are bundled,
thus improving startup time and no more messing with gem paths anymore.
So to recap:
In the Debug environment, the config.dependency method will simply
delegate to "require" and rubygems's "require".
In Release mode however config.dependency will first look in vendor/
externals and after that optionally use rubygems's require.
I hope that my email was clear enough to understand.
If not please do let me know any points that are unclear,
because this is IMO one of the most challenging parts of deploying
your app
and something every RubyCocoa developer will run into sooner or later
so I want to do this as well informed as possible.
Cheers,
Eloy
You Ain't Gonna Need It. :)
>
>
>> So I'll put it on the TODO as nice to have.
>> Of course anyone is free to implement this :)
>
> Will think about whether it's really necessary.
>
Cool.
Eloy