2008
Building Binary Ruby Gems for Windows (2008-10-12)
I have two C extension based gems—Hitimes and Amalgalite—that I think would be useful to Windows users. This weekend I sat down, did the research and figured out a way to integrate building binary gems for Windows into my current setup.
Probably the biggest issue for developing binary gems for Windows is that the official Windows on Ruby builds are built built using VC6. This compiler is basically impossible to track down, and Luis has a great explanation of the issues developing Ruby for Windows.
I could have used the new One Click
Dev-Kit, and it does
look tempting. I already have rake scripts setup for all my building, testing,
and distributing and I want to be able to say rake dist:rubyforge and all
versions of the gem in question are pushed up to rubyforge in one fell swoop.
Fortunately Mauricio Fernandez had a great blog post about 2 years ago on Cross-compiling Ruby extensions for win32: rcovrt. His approach integrates better with my current setup and I was able to learn about cross-compilers in the process, which are something I’ve been meaning to experiment with for a while. Although the rcovrt blog post is 2 years old, it is still relevant and was able to point me in the right direction.
Install a Cross-Compiler
The first item we need is a cross compiler.
Right now I’m developing on a Mac so I need the mingw32 cross compiler so I can build binaries that will be compatible with the official VC6 built Ruby. Luckily the i386-mingw cross compiler is available in MacPorts.
sudo port install i386-mingw32-gcc
Unfortunately there is a problem with the i386-mingw32-binutils
port. If you see an error relating to
makeinfo in your build, use the patch I submitted to the
ticket.
Once binutils is all happy and you’ve had a cup of coffee or two while the mingw32 tool-chain builds installs we can build our gem for Windows.
Building Ruby Using the MinGW Cross-Compiler
This piece was straight from Cross-compiling Ruby extensions for win32:
rcovrt. I used Mauricio’s
cross-compile.sh as a base and altered it to work on OS X.
% cat cross-compile.sh
#!/bin/sh
env ac_cv_func_getpgrp_void=no \
ac_cv_func_setpgrp_void=yes \
rb_cv_negative_time_t=no \
ac_cv_func_memcmp_working=yes \
rb_cv_binary_elf=no \
./configure \
--host=i386-mingw32 \
--target=i386-mingw32 \
--build=i686-darwin9.2.2 \
--prefix=${HOME}/ruby-mingw32
make ruby
make rubyw.exe
make install
Stick this in a freshly extracted ruby source
distribution and run it.
This builds an i386-mingw32 version of ruby and installs it in
${HOME}/ruby-mingw32.
The whole purpose of this little exercise is a side effect. I don’t really need to run this build of ruby, I only need it to exist so I may build against it.
Building a Windows Gem
Now that I have a mingw32 build of ruby I can build my extensions against it.
Normally when building an extensions I do rake ext:build. Which under the
covers does:
cd ext/ ruby extconf.rb make
For a Windows build, we need to have the extconf.rb produced Makefile build
against the i386-mingw32 ruby. This is done by taking the rbconfig.rb file
from the ruby-mingw32 installation and putting it in the ext/ directory. I
keep a copy of this as ext/rbconfig-mingw.rb. The build process then becomes:
cd ext cp rbconfig-mingw.rb rbconfig.rb ruby -I. extconf.rb make rm -f rbconfig.rb
Using -I. forces the current working directory to the front of the
$LOAD_PATH and mkmf will therefore load the rbconfig.rb in the ext/
directory instead of the one from the global ruby installation. This forces all
the Config::CONFIG accesses in mkmf to use the environment of the mingw
build and make a i386-mingw Makefile.
The make command will then build against the i386-mingw32 ruby installation
and the final loadable library can be shipped in a platform dependent gem.
Packaging the Gem
I have a top level gemspec.rb file in all of my projects which holds the
global Gem Specification for the project. For the Windows binary gem I need an
additional spec that is almost the same as the default spec, without the
extensions. Here is the snippet I added for
Amalgalite.
# create a new spec based upon the normal spec Amalgalite::GEM_SPEC_WIN = Amalgalite::GEM_SPEC.clone # set the platform to be compatible with the official # Windows release of Ruby win_platform = ::Gem::Platform.new( "i386-mswin32_60" ) Amalgalite::GEM_SPEC_WIN.platform = win_platform # turn off the extensions, since this is a binary release Amalgalite::GEM_SPEC_WIN.extensions = [] # add the binary extension to the normal file list Amalgalite::GEM_SPEC_WIN.files += ["lib/amalgalite3.so"]
The gem is then packaged using an additional rake gem packaging task.
desc "package the windows gem" task :package_win => "ext:build_win" do cp "ext/amalgalite3.so", "lib", :verbose => true Gem::Builder.new( Amalgalite::GEM_SPEC_WIN ).build mv Dir["*.gem"].first, "pkg" end
This all culminates in the ability to publish a new version of a gem for my
supported platforms to rubyforge with a simple rake dist:rubyforge
Clone the Amalgalite repo or Hitimes repo and look around. Let me know if you have any questions or comments.
Managing a Gem Repository with Stickler (2008-10-09)
One of the items Fernand and I mentioned in our Lone Star talk was Stickler. This was a project I had originally started on back in June and it lay dormant for a little while.
The first public version (0.1.0) is now released.
Why should I use Stickler?
One of the problems that we have had at work is inadvertently upgrading a gem on
a production system. For example, you are adding a gem to a system that
depends on activerecord, >= 2.0.2. This is great, until active_record 2.1.1
comes out and on your production system you type gem install somegem and it
updates your active record installation, as a side effect. Yes, you can just
quickly uninstall it, but it is annoying.
Another issue that some installations have, is production boxes are unable to talk directly to the Internet. This is a problem when doing a gem installation using rubygems. You either have to run your own gem server, or install the gems locally.
Installation and Setup
Stickler currently has a basic workflow. First, install and setup your Stickler repository.
jeremy@aramis:~ % sudo gem install stickler
============================================================
Thank you for installing Stickler!
* Create a new stickler repository:
stickler setup /path/to/repo
* Look at the help:
stickler help
============================================================
Successfully installed stickler-0.1.0
1 gem installed
Installing ri documentation for stickler-0.1.0...
Installing RDoc documentation for stickler-0.1.0...
jeremy@aramis:~ % stickler setup /tmp/stickler
created repository root /tmp/stickler
created directory /tmp/stickler/gems
created directory /tmp/stickler/log
created directory /tmp/stickler/specifications
created directory /tmp/stickler/dist
created directory /tmp/stickler/cache
copied in default configuration to /tmp/stickler/stickler.yml
Setting up sources
* loading http://gems.rubyforge.org/
A Stickler repository is a specific directory that holds all the gem files that
are managed by Stickler for your personal distribution. In this case, Stickler
is now installed as a gem and we have setup a repository in /tmp/stickler.
Populating the Repository
A repository is great, and completely useless unless it is populated with gems
to distribute. So that is what we will do. The best way to operate on a
Stickler repository is to be in it. You can also use the --directory
commandline option on every command, but that can get old. Therefore, we’ll
operate from the root of the repository.
cd /tmp/stickler
info command.
jeremy@aramis:/tmp/stickler % stickler info
Setting up sources
* loading http://gems.rubyforge.org/
Upstream Sources
----------------
http://gems.rubyforge.org/ : 16651 gems available
: 0 gems existing
Configured gems (in stickler.yml)
---------------------------------
Existing gems
-------------
merb, = 0.9.8 to our repository
jeremy@aramis:/tmp/stickler % stickler add gem merb Setting up sources * loading http://gems.rubyforge.org/ You need to pick the merb Requirement to configure Stickler. This involves picking one of the following Requirement operators See http://docs.rubygems.org/read/chapter/16#page74 for operator info. You need to (1) pick an operator and (2) pick a requirement. The most common operators are >=, > and ~> 1. = Equals version 2. != Not equal to version 3. > Greater than version 4. < Less than version 5. >= Greater than or equal to 6. <= Less than or equal to 7. ~> Approximately greater than (1) Pick an operator ? 1 Now to pick a requirement. Based upon your chosen operator '=', These are the available version of the merb gem. 1. = 0.9.8 7. = 0.9.2 13. = 0.4.1 19. = 0.3.0 25. = 0.0.6 2. = 0.9.7 8. = 0.5.3 14. = 0.4.0 20. = 0.2.0 26. = 0.0.5 3. = 0.9.6 9. = 0.5.2 15. = 0.3.7 21. = 0.1.0 27. = 0.0.4 4. = 0.9.5 10. = 0.5.1 16. = 0.3.4 22. = 0.0.9 28. = 0.0.3 5. = 0.9.4 11. = 0.5.0 17. = 0.3.3 23. = 0.0.8 29. = 0.0.2 6. = 0.9.3 12. = 0.4.2 18. = 0.3.1 24. = 0.0.7 30. = 0.0.1 (2) Pick a requirement ? 1 Resolving gem dependencies for merb (= 0.9.8, runtime) ... Adding ParseTree-2.1.1-x86-mswin32 Adding ruby2ruby-1.1.9 Adding RubyInline-3.7.0 Adding ParseTree-2.2.0 Adding merb-action-args-0.9.8 Adding merb-assets-0.9.8 Adding highline-1.4.0 Adding diff-lcs-1.1.2 Adding templater-0.3.2 Adding merb-gen-0.9.8 Adding haml-2.0.3 Adding merb-haml-0.9.8 Adding builder-2.1.2 Adding merb-builder-0.9.8 Adding mailfactory-1.4.0 Adding merb-mailer-0.9.8 Adding merb-parts-0.9.8 Adding merb-cache-0.9.8 Adding merb-slices-0.9.8 Adding merb-jquery-0.9.8 Adding extlib-0.9.7 Adding abstract-1.0.0 Adding erubis-2.6.2 Adding json_pure-1.1.3 Adding rubyforge-1.0.0 Adding rake-0.8.3 Adding hoe-1.8.0 Adding rspec-1.1.8 Adding rack-0.4.0 Adding mime-types-1.15 Adding hpricot-0.6.161-java Adding hpricot-0.6.161 Adding hpricot-0.6-x86-mswin32 Adding thor-0.9.6 Adding merb-core-0.9.8 Adding merb-helpers-0.9.8 Adding merb-more-0.9.8 Adding mongrel-1.1.5-java Adding mongrel-1.1.1-java Adding daemons-1.0.10 Adding fastthread-1.0.1 Adding fastthread-1.0.1-x86-mswin32 Adding mongrel-1.1.5 Adding mongrel-1.1.3-x86-mswin32 Adding mongrel-1.1.5-x86-mswin32-60 Adding mongrel-1.1.2-x86-mswin32 Adding gem_plugin-0.2.3 Adding cgi_multipart_eof_fix-2.5.0 Adding mongrel-1.1.5-x86-mingw32 Adding merb-0.9.8
gem install merb. Stickler assumes you are
redistributing gems, and as such gets every available platform for a gem when
you add it to the repository. And then it recurses through all runtime and
development dependencies. And now when we look at the repository state we see:
Setting up sources
* loading http://gems.rubyforge.org/
Upstream Sources
----------------
http://gems.rubyforge.org/ : 16652 gems available
: 50 gems existing
Configured gems (in stickler.yml)
---------------------------------
merb : = 0.9.8
Existing gems
-------------
ParseTree-2.1.1-x86-mswin32
ParseTree-2.2.0
RubyInline-3.7.0
abstract-1.0.0
builder-2.1.2
cgi_multipart_eof_fix-2.5.0
daemons-1.0.10
diff-lcs-1.1.2
erubis-2.6.2
extlib-0.9.7
fastthread-1.0.1
fastthread-1.0.1-x86-mswin32
gem_plugin-0.2.3
haml-2.0.3
highline-1.4.0
hoe-1.8.0
hpricot-0.6-x86-mswin32
hpricot-0.6.161
hpricot-0.6.161-java
json_pure-1.1.3
mailfactory-1.4.0
merb-0.9.8
merb-action-args-0.9.8
merb-assets-0.9.8
merb-builder-0.9.8
merb-cache-0.9.8
merb-core-0.9.8
merb-gen-0.9.8
merb-haml-0.9.8
merb-helpers-0.9.8
merb-jquery-0.9.8
merb-mailer-0.9.8
merb-more-0.9.8
merb-parts-0.9.8
merb-slices-0.9.8
mime-types-1.15
mongrel-1.1.1-java
mongrel-1.1.2-x86-mswin32
mongrel-1.1.3-x86-mswin32
mongrel-1.1.5
mongrel-1.1.5-java
mongrel-1.1.5-x86-mingw32
mongrel-1.1.5-x86-mswin32-60
rack-0.4.0
rake-0.8.3
rspec-1.1.8
ruby2ruby-1.1.9
rubyforge-1.0.0
templater-0.3.2
thor-0.9.6
There is a difference between configured gems and existing gems. Configured gems are those you have specifically requested to be in the repository. Existing gems are those that are added to support the configured gems and the configured gems themselves.
Adding Additional Sources
Part of the reason for Stickler is to merge upstream gem repositories into your own internal repository. Say you use the github repository and want to add a gem or two from it to your internal system.
jeremy@aramis:/tmp/stickler % stickler add source http://gems.github.com Setting up sources * loading http://gems.rubyforge.org/ * loading http://gems.github.com/ http://gems.github.com added to sources
jeremy@aramis:/tmp/stickler % stickler add gem aniero-tire_swing Setting up sources * loading http://gems.rubyforge.org/ * loading http://gems.github.com/ You need to pick the aniero-tire_swing Requirement to configure Stickler. This involves picking one of the following Requirement operators See http://docs.rubygems.org/read/chapter/16#page74 for operator info. You need to (1) pick an operator and (2) pick a requirement. The most common operators are >=, > and ~> 1. = Equals version 2. != Not equal to version 3. > Greater than version 4. < Less than version 5. >= Greater than or equal to 6. <= Less than or equal to 7. ~> Approximately greater than (1) Pick an operator ? = Now to pick a requirement. Based upon your chosen operator '=', These are the available version of the aniero-tire_swing gem. 1. = 0.0.3 2. = 0.0.2 (2) Pick a requirement ? 1 Resolving gem dependencies for aniero-tire_swing (= 0.0.3, runtime) ... Adding polyglot-0.2.3 Adding treetop-1.2.4 Adding attributes-5.0.1 Adding activesupport-2.1.1 Adding aniero-tire_swing-0.0.3
aneiro-tire_swing was added from github, and it also
resolved the dependencies to gems that reside on rubyforge. Looking at info
shows us:
jeremy@aramis:/tmp/stickler % stickler info
Setting up sources
* loading http://gems.rubyforge.org/
* loading http://gems.github.com/
Upstream Sources
----------------
http://gems.rubyforge.org/ : 16652 gems available
: 54 gems existing
http://gems.github.com/ : 2463 gems available
: 1 gems existing
Configured gems (in stickler.yml)
---------------------------------
aniero-tire_swing : = 0.0.3
merb : = 0.9.8
...
Batch Addition
It can get repetitive to manually add gem after gem from the commandline. When
you know exactly what you need, crack open the stickler.yml file and add a few
gems. For instance, lets say that we are in the process of updating all our
rails apps to 2.1. In the meantime we want to make sure we do not inadvertently
update our production systems to 2.1. They are still at 2.0.2. So we add all
the rails gems at the 2.0.2 level to the stickler.yml file in the repository.
--- downstream_source: http://gems.example.com/ sources: - http://gems.rubyforge.org/ - http://gems.github.com/ gems: aniero-tire_swing: - = 0.0.3 merb: - = 0.9.8 activerecord: = 2.0.2 activesupport: = 2.0.2 actionmailer: = 2.0.2 actionpack: = 2.0.2 activeresource: = 2.0.2 rails: = 2.0.2
We can now use the sync command to add all of them in at once.
jeremy@aramis:/tmp/stickler % stickler sync Setting up sources * loading http://gems.rubyforge.org/ * loading http://gems.github.com/ Making sure that all gems listed in configuration are available Resolving gem dependencies for rails (= 2.0.2, runtime) ... Adding activerecord-2.0.2 Adding actionpack-2.0.2 Adding actionmailer-2.0.2 Adding activesupport-2.0.2 Adding activeresource-2.0.2 Adding rails-2.0.2 Resolving gem dependencies for activerecord (= 2.0.2, runtime) ... Resolving gem dependencies for activeresource (= 2.0.2, runtime) ... Resolving gem dependencies for aniero-tire_swing (= 0.0.3, runtime) ... Resolving gem dependencies for actionpack (= 2.0.2, runtime) ... Resolving gem dependencies for actionmailer (= 2.0.2, runtime) ... Resolving gem dependencies for merb (= 0.9.8, runtime) ... Resolving gem dependencies for activesupport (= 2.0.2, runtime) ...
You can see that it added in all the gems in and then it also made sure that
aneiro-tire_swing and merb were also synced up. If you need to rebuild the
entire repository from scratch, use stickler sync --rebuild and it will wipe
out the current gems and specifications in the repo and download them again.
Distribution of Gems
Populating the repository is only good if you can get your internal systems to
utilize the repo. This is where the generate commands come into play. The
generate index command does exactly what a gem generate_index does, but it
uses your repository as the base.
Setting up sources * loading http://gems.rubyforge.org/ * loading http://gems.github.com/ Generating rubygems index in /private/tmp/stickler/dist Loading 61 gems from /private/tmp/stickler/dist ...............WARNING: Skipping misnamed gem: /private/tmp/stickler/dist/gems/fastthread-1.0.1-x86-mswin32.gem => fastthread-1.0.1-x86-mswin32 (fastthread-1.0.1-mswin32) .....WARNING: Skipping misnamed gem: /private/tmp/stickler/dist/gems/hpricot-0.6-x86-mswin32.gem => hpricot-0.6-x86-mswin32 (hpricot-0.6-mswin32) WARNING: Skipping misnamed gem: /private/tmp/stickler/dist/gems/hpricot-0.6.161-java.gem => hpricot-0.6.161-java (hpricot-0.6.161-jruby) ..................WARNING: Skipping misnamed gem: /private/tmp/stickler/dist/gems/mongrel-1.1.1-java.gem => mongrel-1.1.1-java (mongrel-1.1.1-jruby) WARNING: Skipping misnamed gem: /private/tmp/stickler/dist/gems/mongrel-1.1.2-x86-mswin32.gem => mongrel-1.1.2-x86-mswin32 (mongrel-1.1.2-mswin32) WARNING: Skipping misnamed gem: /private/tmp/stickler/dist/gems/mongrel-1.1.3-x86-mswin32.gem => mongrel-1.1.3-x86-mswin32 (mongrel-1.1.3-i386-mswin32) ....WARNING: Skipping misnamed gem: /private/tmp/stickler/dist/gems/ParseTree-2.1.1-x86-mswin32.gem => ParseTree-2.1.1-x86-mswin32 (ParseTree-2.1.1-i386-mswin32) ............ Loaded all gems Generating quick index gemspecs for 54 gems ...................................................... Complete Generating specs index Generating latest specs index Generating quick index Generating latest index Generating Marshal master index Generating YAML master index for 54 gems (this may take a while) ...................................................... Complete Compressing indicies
dist sub directory in your repository. This directory can
be rsynced to a web server in your infrastructure, or run a web server on this
machine and point its document root, or a directory alias to the dist
directory.
An extra item you may do is globally configure your rubygems installations to automatically use your internal gem server as the canonical source instead of http://gems.rubyforge.org/. Stickler provides a quick command to generate this top level configuration, and the file contains the information on where to put it.
jeremy@aramis:/tmp/stickler % stickler generate sysconfig > gemrc Setting up sources * loading http://gems.rubyforge.org/ * loading http://gems.github.com/ Generating configuration to stdout jeremy@aramis:/tmp/stickler % cat gemrc # # This is the system wide configuration to be used by # rubygem clients that install gems from the repository # located at : # # http://gems.example.com/ # # On Unix like machines install in # # /etc/gemrc # # On Windows machines install in # # C:\Documents and Settings\All Users\Application Data\gemrc # --- :sources: - http://gems.example.com/
Conclusions
Hopefully you can find Stickler useful in your infrastructure. Please let me know about features and bugs.
Hitimes are upon us (2008-09-14)
Saturday afternoon at the Lone Star Ruby Conf I was discussing something, I forget exactly what, with Bruce and he mentioned that he was looking for a really fast way to measure elapsed time of a piece of code.
The naive approach would be call Time.now before and after the call, or maybe
use the
Time.elapse method
from Facets. Both of those approaches are
perfectly acceptable, and return results down to the µsecond. Can we do
better? Yes.
We have a goal of being faster than 2 successive Time.now calls. Each
operating system has a different way accessing a high-resolution timer.
- Unixes – the POSIX calls to
clock_gettime() - OS X –
UpTime() - Windows –
QueryPerformanceCounter()
Unify the C level interface to all of those, wrap it up in a Ruby extension and the result is the just-released Hitimes, a high-resolution timer library in Ruby for when you want to do measuring at the nanosecond level.
gem install hitimes
Interval
class. This does one really simple thing. It measures an interval of time.
You can measure code in a block:
require 'hitimes' require 'open-uri' duration = Hitimes::Interval.measure do bruce = open("http://www.codefluency.com/").read end puts duration # => 0.261841618
interval = Hitimes::Interval.new # do something interval.start # some code to measure duration = interval.stop
Interval, and a
Timer
class for measuring series of intervals and reporting statistics.
Post Lone Star Talk Thoughts (2008-09-08)

Fernand and I presented a talk at Lone Star Ruby Conf last Friday entitled Building and Managing a Ruby Infrastructure. It appears we hit upon a subject that people are starting encounter. Over the next couple of weeks I’ll post a few more articles explaining in more detail a few of the key concepts. For now, a few links to things that we mentioned in the talk.
- Bones—A great project templating tool, extendible to use your own proect templates.
- Rational Versioning Policy—in
which
~>a.k.a “Twiddle Wakka” is explained. - Stickler—organize and maintain an internal gem distribution server. Still in planning stages.
- take a look at the
gem indexandgem servercommands
Amalgalite Pthread Error (2008-07-05)
If you happen to get an error like the following when running amalgalite, it was a bug in how amalgalite was built. I wasn’t matching the thread support in amalgalite with the threadsupport of the installed ruby.
amalgalite-0.2.0/ext/amalgalite3.so: undefined symbol: pthread_mutexattr_init
For future reference this is how you can see how ruby was compiled on my iMac
>> puts "#{RUBY_VERSION} #{RUBY_PATCHLEVEL} (#{RUBY_RELEASE_DATE}) #{RUBY_PLATFORM}" 1.8.6 114 (2008-03-03) i686-darwin9.2.2 >> require 'rbconfig' >> puts Config::CONFIG['configure_args'] '--prefix=/opt/local' '--enable-shared' '--mandir=/opt/local/share/man' '--enable-pthread' '--without-tk' 'CC=/usr/bin/gcc-4.0' 'CFLAGS=-O2' 'LDFLAGS=-L/opt/local/lib' 'CPPFLAGS=-I/opt/local/include' 'CPP=/usr/bin/cpp-4.0'
versus my OpenBSD server:
>> puts "#{RUBY_VERSION} #{RUBY_PATCHLEVEL} (#{RUBY_RELEASE_DATE}) #{RUBY_PLATFORM}" 1.8.6 110 (2007-09-23) x86_64-openbsd4.2 >> require 'rbconfig' >>puts Config::CONFIG['configure_args'] '--enable-shared' '--enable-ipv6' '--with-dbm-type=bogus' '--with-opt-dir=/usr/local' '--with-tcl-include=/usr/local/include/tcl8.4' '--with-tk-include=/usr/local/include/tk8.4' '--with-X11-dir=/usr/X11R6' '--prefix=/usr/local' '--sysconfdir=/etc' '--mandir=/usr/local/man' '--infodir=/usr/local/info' 'CC=cc' 'CFLAGS=-O2 -pipe' 'CPPFLAGS=-DOPENSSL_NO_STATIC_ENGINE' 'YACC=yacc'
You can see that on my iMac ruby was compiled with --enable-pthread but on
my OpenBSD machine that configuration option is missing. I didn’t take this
into account when building Amalgalite.
Version 0.2.1 was just released which fixes this bug.
gem install amalgalite
Amalgalite 0.2.0 Released (2008-07-04)
Now with more examples!
SQLite has the ability to do incremental IO on items that are BLOB’s. I just finished adding support for this in Amalgalite. This means you can SELECT a blob, and from the result set, transfer the data directly to an IO stream.
A quick example. Assume we store files in filestore.db that has the
following schema:
CREATE TABLE files( id INTEGER PRIMARY KEY AUTOINCREMENT, path VARCHAR UNIQUE data BLOB )
You can then write all the files in this table to a different tree with this piece of code. In this case, the files are not loaded into memory, the are copied directly from the pages in the database for the BLOBs to the output files.
new_root_dir = "/tmp" db = Amalgalite::Database.new( "filestore.db" ) db.execute( "SELECT * FROM files" ) do |row| # unsafe path expansion dest_path = File.expand_path( File.join( new_root_dir, row['path']) ) row['data'].write_to_file( dest_path ) end db.close
To get this behavior you need to make sure that column that is SQLite’s rowid
column is part of the result set. If one of your columns is
INTEGER PRIMARY KEY it is that one. Otherwise you must
specifically select ROWID, OID. The code above will still work if the
rowid column is not specified, but Amalgalite will fully read it into memory
before being written to the file.
Take a look at the Amalgalite::Blob api and the example.
Amalgalite 0.1.0 Released (2008-06-21)
I’ve been a fan of SQLite for a long time. Many years ago I considered writing an SQLite extension for Ruby. Jamis beat me to it, and I’ve been happily using ruby-sqlite since it arrived on the scene.
Since then, SQLite itself has greatly improved. There are new features, an evolution in the API, and most of all, The SQLite Amalgamation. This is the entire SQLite database engine as a single C source code file.
Perfect! Why not make a ruby extension that embeds SQLite directly in the extension?
Amalgalite is born.
Install as a gem
gem install amalgalite
Or grab from github
git clone git://github.com/copiousfreetime/amalgalite.git
Give it a whirl and let me know what you think.
require 'rubygems' require 'amalgalite' puts "using SQLite v#{Amalgalite::SQLite3::Version}" # => "using SQLite v3.5.9"
Current Functionality
I would consider Amalgalite beta right now. It is fully usable; but it does not contain all the features I want yet.
The normal stuff:
- open or create databases
- run SQL in single statements or in batches.
- use prepared statements with parameters
- access resultsets by index or by column name
- transactions
- type conversion between database types and Ruby types
The fun/happy/cool stuff, and not available in sqlite3-ruby:
- trace information → Amalgalite::TraceTap
- profile information → Amalgalite::ProfileTap
- read only databases
- a type conversion protocol → * Amalgalite::TypeMaps
Future plans
- ActiveRecord driver
- DataMapper driver
- Sequel driver
- progress and authorizer callbacks
amalgalitecommand line tool similar to thesqlite3commandline tool- utf-16 support
- blob support
- and more