Bundling unpacked gems into an application jar can be tricky
Note: This is related to using rawr.
Basic points:
- You need to unpack the gem into lib/ruby
- You should strip off any version info from the gem directory name
- Do not attempt to adjust the relative location of files just unpacked however tempting that may be
- In your manifest, alter the part at the top of the file that executes a Dir.glob and populates $LOAD_PATH
- Be sure it is recursing into nested directories (this may be a bug in some versions of Monkeybars)
- Add code to exclude any directory that has your unpacked gem files
- In the
when Monkeybars::Resolver::IN_JAR_FILEsection, add thelibdirectories of the gems to the load path
The key is that many gems make assumptions about the load path. In particular, they assume that gemname/lib is there, but not the subdirectories. Gems often use a gem root-level file to manage the loading of additional files; messing up the expected load path will bring sadness and pain.
An example:
Suppose we want to add the builder gem.
Unpack the gem
cd lib/ruby jruby -S gem unpack builder # Now rename the builder directory to remove any version info.
Your lib/ruby directory should have this:
|-- builder | `-- lib | |-- blankslate.rb | |-- builder | | |-- blankslate.rb | | |-- xchar.rb | | |-- xmlbase.rb | | |-- xmlevents.rb | | `-- xmlmarkup.rb | `-- builder.rb
In manifest.rb:
Dir.glob(File.expand_path(File.dirname(__FILE__) + '/**/*').gsub('%20', ' ')).each do |directory|
next if directory =~ /\/builder\//
$LOAD_PATH << directory unless directory =~ /\.\w+$/ #File.directory? is broken in current JRuby for dirs inside jars
end
Then, later in the file:
case Monkeybars::Resolver.run_location when Monkeybars::Resolver::IN_FILE_SYSTEM # assorted stuff omitted when Monkeybars::Resolver::IN_JAR_FILE add_to_load_path "builder/lib" end
Now, in your application code, you should be able to do this:
require 'builder'
with smiles all around!
Note: You may need to be really careful with how you exclude gems from the the bulk loading of the load path.
If, as in the example, you are using a pattern, be sure it only matches what you really want to exclude.
Problems with gems that require rubygems
Some gems explicitly require the rubygems library and use the gem method to load their resources. This does not typically work when running on a foreign system that does not have Rubygems installed (although progress is being made to let gems load out of jars). A solution is to load the unpacked libraries manually and to mock out the functionality of gem so that you will not get an exception. To do this, create a file named rubygems.rb and make sure that file is on your load path.
rubygems.rb Object.send(:remove_const, :Gem) if Object.const_defined? :Gem Kernel.send(:remove_method, :gem) if Kernel.respond_to? :gem module Gem end module Kernel def gem(*args); end end






