Good way to not forget to install gems on a server?

I just broke my wife’s website (my current side project) because i was
using a gem and i forgot to install it on the server before updating all
of the application code and restarting it.

Can anyone suggest a good way, besides simply not being a doofus, to
avoid doing this again?

The app is a Ramaze app in case that’s relevant.

thanks
max

Max W. wrote:

I just broke my wife’s website (my current side project) because i was
using a gem and i forgot to install it on the server before updating all
of the application code and restarting it.

Can anyone suggest a good way, besides simply not being a doofus, to
avoid doing this again?

finger.tie(String) ?

Seriously, something like

gem list | grep -P -o ‘^\S+’

will tell you what you’ve got, so you could write a little script that
does this on both hosts, and diffs to let you know what’s missing. Or
take the output from one host and feed it to gem install on the other…

Joel VanderWerf wrote:

Max W. wrote:

I just broke my wife’s website (my current side project) because i was
using a gem and i forgot to install it on the server before updating all
of the application code and restarting it.

Can anyone suggest a good way, besides simply not being a doofus, to
avoid doing this again?

finger.tie(String) ?

Seriously, something like

gem list | grep -P -o ‘^\S+’

will tell you what you’ve got, so you could write a little script that
does this on both hosts, and diffs to let you know what’s missing. Or
take the output from one host and feed it to gem install on the other…

Hi Joel, thanks!

One problem with the second approach (i like the first approach) is that
i don’t want every gem on my local machine to also be on my server -
just the ones required by the app. So, i guess that whatever the
solution is it has to involve some file(s) in my app where my requires
are listed.

Eg when i do a require, raise a warning if the gem wasn’t loaded. Then,
when i restart the app, i see the warnings and can install the gems. Or
even better, some kind of rake sort of task that installs the gems
listed in the requires, which i run as part of my restart process. or
something…just thinking aloud now really.

Max W. wrote:

One problem with the second approach (i like the first approach) is that
i don’t want every gem on my local machine to also be on my server -
just the ones required by the app. So, i guess that whatever the
solution is it has to involve some file(s) in my app where my requires
are listed.

You could log $" from your program to capture the list of libraries in
use. That will need some parsing to come up with a list of gem names.

Maybe rubygems has a command to list the currently loaded gems?

Max W. wrote:

I could output this into a text file, and have a task which does the
same into a different file, and bitches if a diff between the files
reveals any differences. It’s crazy enough to work, but seems like a
roundabout (and unreliable) sort of an approach.

I meant to say btw that the first file would be created when i start the
app
locally, and would alter a file i have in source control, which gets
pushed up onto the server. Then the server makes a differently named
version of the same file.

Joel VanderWerf wrote:

Max W. wrote:

One problem with the second approach (i like the first approach) is that
i don’t want every gem on my local machine to also be on my server -
just the ones required by the app. So, i guess that whatever the
solution is it has to involve some file(s) in my app where my requires
are listed.

You could log $" from your program to capture the list of libraries in
use. That will need some parsing to come up with a list of gem names.

Maybe rubygems has a command to list the currently loaded gems?

hmmm. eg (this is run in irb inside my app)
puts $".collect{|s|
s.split("/").first.gsub(/…*/,"")}.uniq.sort.join("\n")
English
abbrev
abstract
active_support
activesupport
base64
benchmark
bigdecimal
…etc

I could output this into a text file, and have a task which does the
same into a different file, and bitches if a diff between the files
reveals any differences. It’s crazy enough to work, but seems like a
roundabout (and unreliable) sort of an approach.

Matt N. wrote:

So I’m thinking you could use this, or something like it… m.

This looks great Matt, thanks!

Max W. [email protected] wrote:

Eg when i do a require, raise a warning if the gem wasn’t loaded. Then,
when i restart the app, i see the warnings and can install the gems.

Right, this is why RubyFrontier uses “myrequire” instead of “require”:

def myrequire(*what)

(1) no penalty for failure; we catch the LoadError and we don’t

re-raise

(2) arg can be an array, so multiple requires can be combined in one

line

(3) array element itself can be a pair, in which case second must be

array of desired includes as symbols

that way, we don’t try to perform the includes unless the require

succeeded

and we never say the include as a module, so it can’t fail at

compile time

and if an include fails, that does raise all the way since we don’t

catch NameError
what.each do |thing|
begin
require((t = Array(thing))[0])
Array(t[1]).each {|inc| include self.class.const_get(inc) rescue
puts “Warning: failed to include #{inc.to_s}”}
rescue LoadError
puts “Warning: Require failed”, “This could cause trouble later…
or not. Here’s the error message we got:”
puts $!
end
end
end

The idea here is that by using “myrequire” everywhere, I notify the
user that a require failed, without actually encountering a fatal error.
The fatal error would be encountered only the user tried to use some
code that actually calls something inside one of the required files -
and that might never happen. But at least this way the user gets a list,
up front, of gems that need installing sooner or later.

So I’m thinking you could use this, or something like it… m.

Matt N. wrote:

Max W. [email protected] wrote:

Matt N. wrote:

So I’m thinking you could use this, or something like it… m.

This looks great Matt, thanks!

Cool! Note that the last line of the comment is outdated; I do now catch
the error from “include” and report it as a warning, without failing.

Another thought - if you have code that is outside your control (it uses
“require” and you can’t change that) you could, I suppose, patch
“require” just the way rubygems does. But I don’t have that problem and
I don’t like to go that far, so this utility at top level is good enough
for me… m.

I think this will catch most of the times where i just forget. :slight_smile:

cheers!
max

Funnily enough i just got a mail from LRUG (london ruby users group)
about this gem, bundler, which looks very useful -
http://github.com/wycats/bundler/tree/master

Max W. [email protected] wrote:

Matt N. wrote:

So I’m thinking you could use this, or something like it… m.

This looks great Matt, thanks!

Cool! Note that the last line of the comment is outdated; I do now catch
the error from “include” and report it as a warning, without failing.

Another thought - if you have code that is outside your control (it uses
“require” and you can’t change that) you could, I suppose, patch
“require” just the way rubygems does. But I don’t have that problem and
I don’t like to go that far, so this utility at top level is good enough
for me… m.

Probably the simplest solution is to start up another instance of your
updated application listening on a different port, look for errors on
STDERR, and test it using a web browser. Then you’re less likely to kill
the main site when you roll it out.

The app is a Ramaze app in case that’s relevant.

Yes I’d say it’s relevant.

Rails has a mechanism by which you can declare all the gems used (and
versions, if necessary); then there’s a rake task to install any missing
gems on the target server. However this does rely on you recording all
the gems used. Maybe there’s an equivalent plugin for Ramaze.

A sledgehammer approach is to “vendorize” all the gems your application
uses - that is, stick them in a vendor/ subdirectory or whatever. Then
as long as you don’t do ‘require “rubygems”’ anywhere, then you know for
sure that all the libs you require have been vendorized. (Probably only
works for 1.8, since 1.9 includes rubygems by default I believe)

If you’re developing with git, then you can include all the libraries as
git submodules. Then on the target system:

git pull
git submodule update

will update both your application and the vendorized libraries. This has
the advantage that you can choose any particular revision from each
library that you like - you’re not restricted to officially released gem
versions. However it does rely on the libraries you use being available
from git repos.

Some care is needed when adding, updating or removing submodules in git.

HTH,

Brian.

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs