Tuesday, November 30, 2010

rexml notes

REXML::Document.write is deprecated


rexml printing can be done with something like:
#!/usr/bin/env ruby
require 'rexml/document'
include REXML

doc = Document.new
doc << Element.new('root')
doc.elements['root'] << Element.new('child')

output = ''
doc.write output
puts output
#<root><child/></root>

output, indent = '', 1
doc.write output, indent
puts "with indent:"
puts output
=begin
with indent:
<root>
 <child/>
</root>
=end


new hotness


but that's deprecated. so use REXML::Formatters instead.

#!/usr/bin/env ruby
require 'rexml/document'
include REXML
doc = Document.new
doc << Element.new('root')
doc.elements['root'] << Element.new('child')

output, indent = '', 1
Formatters::Pretty.new(indent).write doc, output
puts output
=begin
<root>
 <child/>
</root>
=end


there are 2 other formatters availabe: default, and transitive. transitive does its best to preserve content nodes, including whitespace, but it will insert whitespace in non-content areas to improve formatting. so between attributes, tagnames, but not in text nodes.

more pretty print, please


pretty print also has a width, and a compact. compact will fit nodes with only text children onto 1 line if it can be done smaller than the width. large sections of text are always wrapped at 80 chars and indented 1 level further than their parent.
#!/usr/bin/env ruby
require 'rexml/document'
include REXML

doc = Document.new
doc << Element.new('root')
child = Element.new('child')
child << Text.new(<<-EOT)
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
EOT
small = Element.new('small')
small << Text.new(' little
text ')
doc.elements['root'] << child
doc.elements['root'] << small


output, indent = '', 20
pretty = Formatters::Pretty.new(indent)
pretty.width = 75
pretty.compact = true
pretty.write doc, output
puts output
=begin
<root>
<child>
Lorem ipsum dolor sit amet, consectetur
adipisicing elit, sed do eiusmod tempor
incididunt ut labore et dolore magna
aliqua. Ut enim ad minim veniam, quis
nostrud exercitation ullamco laboris
nisi ut aliquip ex ea commodo consequat.
Duis aute irure dolor in reprehenderit
in voluptate velit esse cillum dolore eu
fugiat nulla pariatur. Excepteur sint
occaecat cupidatat non proident, sunt in
culpa qui officia deserunt mollit anim
id est laborum.
</child>
<small> little text </small>
</root>
=end


side note


i used this line for printing the above stuff escaping the characters, but preserving the whitespace:
File.open(__FILE__){ |f| puts Text.new(f.readlines.join, true) }

adding children on child creation


some child elements can have their parent node set on creation. works for Document, and Element nodes, but this doesn't work for text nodes, as far as i can tell.
#!/usr/bin/env ruby
require 'rexml/document'
include REXML

doc = Document.new
root = Element.new('root', doc)
child = Element.new('child', root)
Text.new('Lorem ipsum', child) #doesn't work, why?
small = Element.new('small', root)
little = Text.new(' little
text ')
small << little # adding text nodes like this works


output, indent = '', 20
pretty = Formatters::Pretty.new(indent)
pretty.width = 75
pretty.compact = true
pretty.write doc, output
puts output
=begin
<root>
<child/>
<small> little text </small>
</root>
=end

Monday, July 26, 2010

ruby Array#uniq uses eql? and hash

this one kept me busy for 20 minutes


if you create an array of objects and wish to call uniq on that array, the objects of the array must supply both the eql? and hash methods. the hash method must return an integer; it's not enough that the return value be constant, it must be a Fixnum.

an example


here's some code to demonstrate that you need both to be defined.

class Foo
attr_reader :s
def initialize s
@s = s
end
def inspect
"#{self.class}: @s=#{@s.inspect}"
end
alias :to_s :inspect
end
foo_array = ["a","b","b"].map{|e|Foo.new e}
puts "initially:"
puts " #{foo_array.inspect}"
puts " #{foo_array.uniq.join ', '}"
#initially:
# [Foo: @s="a", Foo: @s="b", Foo: @s="b"]
# Foo: @s="a", Foo: @s="b", Foo: @s="b"

class Foo
alias :old_hash :hash
def hash
@s.hash
end
end
puts "with hash defined:"
puts " #{foo_array.inspect}"
puts " #{foo_array.uniq.join ', '}"
#with hash defined:
# [Foo: @s="a", Foo: @s="b", Foo: @s="b"]
# Foo: @s="a", Foo: @s="b", Foo: @s="b"

class Foo
undef hash
alias :hash :old_hash
def eql? o
@s == o.s
end
end
puts "with eql? defined:"
puts " #{foo_array.inspect}"
puts " #{foo_array.uniq.join ', '}"
#with eql? defined:
# [Foo: @s="a", Foo: @s="b", Foo: @s="b"]
# Foo: @s="a", Foo: @s="b", Foo: @s="b"

class Foo
def hash
@s.hash
end
end
puts "with eql? and hash defined:"
puts " #{foo_array.inspect}"
puts " #{foo_array.uniq.join ', '}"
#with eql? and hash defined:
# [Foo: @s="a", Foo: @s="b", Foo: @s="b"]
# Foo: @s="a", Foo: @s="b"

=begin
# ruby asdf.rb
initially:
[Foo: @s="a", Foo: @s="b", Foo: @s="b"]
Foo: @s="a", Foo: @s="b", Foo: @s="b"
with hash defined:
[Foo: @s="a", Foo: @s="b", Foo: @s="b"]
Foo: @s="a", Foo: @s="b", Foo: @s="b"
with eql? defined:
[Foo: @s="a", Foo: @s="b", Foo: @s="b"]
Foo: @s="a", Foo: @s="b", Foo: @s="b"
with eql? and hash defined:
[Foo: @s="a", Foo: @s="b", Foo: @s="b"]
Foo: @s="a", Foo: @s="b"
=end

references


this forum finally led me to the idea of overriding hash and eql?. but it also says that the only requirement for hash is that it be constant for a given state of an object. well it also needs to be a number, it turns out. otherwise you get something like this:

asdf.rb:26:in `uniq': can't convert String into Integer (TypeError)
from asdf.rb:26:in `
'

infuriating. i had no idea what integer it was talking about. i see no integer! AAUGH! so it turns out that someone in a thread on the internet wasn't completely correct.

Monday, April 5, 2010

gitweb serving gitosis repos via apache2 (on ubuntu karmic)

set up gitosis

install packages


sudo aptitude install gitweb apache2

configure gitweb


modify /etc/gitweb.conf:

# path to git projects (.git)
#$projectroot = "/var/cache/git";
$projectroot = "/srv/gitosis/repositories";

# directory to use for temp files
$git_temp = "/tmp";

# target of the home link on top of all pages
#$home_link = $my_uri || "/";

# html text to include at home page
$home_text = "indextext.html";

# file with project list; by default, simply scan the projectroot dir.
#$projects_list = $projectroot;
$projects_list = "/srv/gitosis/gitosis/projects.list";

# stylesheet to use
#$stylesheet = "/gitweb.css";
$stylesheet = "/git/gitweb.css";

# logo to use
#$logo = "/git-logo.png";
$logo = "/git/git-logo.png";

# the 'favicon'
#$favicon = "/git-favicon.png";
$favicon = "/git/git-favicon.png";

configure apache2


create /etc/apache2/conf.d/git with this content:

<Directory /var/www/git>
Allow from all
AllowOverride all
Order allow,deny
Options ExecCGI
<Files gitweb.cgi>
SetHandler cgi-script
</Files>
</Directory>
DirectoryIndex gitweb.cgi
SetEnv GITWEB_CONFIG /etc/gitweb.conf

make files visible to apache2

sudo mkdir /var/www/git
sudo cp /usr/share/gitweb/* /var/www/git
sudo cp /usr/lib/cgi-bin/gitweb.cgi /var/www/git
sudo /etc/init.d/apache2 reload
chmod 0755 /srv/gitosis/repositories/test_repo.git

configure gitosis



git clone gitosis@localhost:gitosis-admin.git
cd gitosis-admin

modify gitosis.conf, add the following:

[repo test_repo]
gitweb = yes
description = one line test repo description
owner = first_name last_name

save the configuration and push it back to gitosis

git add gitosis.conf
git ci -m "publishing test_repo via gitweb"
git push origin master

try it out


open http://localhost/git and see test_repo.git in column titled "Project"

references


Wednesday, March 3, 2010

git submodule moving

how do you update a git submodule? say the submodule used to be at gitosis@example.com:submodule_a.git, but the developers switched hosts and now it's at gitosis@example.net:submodule_a.git. turns out all you have to do is change .git/config.

in superproject/.git/config


before:
[submodule "submodule_a"]
url = gitosis@example.com:submodule_a.git

after:
[submodule "submodule_a"]
url = gitosis@example.net:submodule_a.git


now git submodule update will use example.net instead of example.com. but that doesn't change the content of the repository, just the configuration for your local copy of the superproject. if someone clones your repo, they'll try to reach the submodule at gitosis@example.com:submodule_a.git. to avoid this, update .gitmodules to point at the new location as well.

in superproject/.gitmodules


before:
[submodule "submodule_a"]
path = submodule_a
url = gitosis@example.com:submodule_a.git

after:
[submodule "submodule_a"]
path = submodule_a
url = gitosis@example.net:submodule_a.git

when git submodule init reads the .gitmodules file (in a newly cloned repo), it will get the new location for the submodule and setup the .git/config file accordingly.

helpful, but not what i wanted


places i went looking for this info, but didn't find it:
http://book.git-scm.com/5_submodules.html
http://progit.org/book/ch6-6.html
http://pitupepito.homelinux.org/?p=24
http://gaarai.com/2009/04/20/git-submodules-adding-using-removing-and-updating/
http://www.hackido.com/2009/01/quick-tip-remove-git-submodule.html

Tuesday, March 2, 2010

gitosis on ubuntu karmic 9.10

quickstart


as some_user@example.com:


on the server that will host the git repos,

sudo aptitude install gitosis
sudo -H -u gitosis gitosis-init < ~/.ssh/id_rsa.pub
git clone gitosis@localhost:gitosis-admin.git
cd gitosis-admin
scp newuser@example.net:.ssh/id_rsa.pub keydir/newuser@example.net.pub
vim gitosis.conf #:tabnew /usr/share/doc/gitosis/examples/example.conf
#add newuser@example.net to project_foo_group, make project_foo writable, close
git add .
git ci -m "add newuser@example.net to project_foo_group, make project_foo writable"
git push origin master

example gitosis.conf pushed back to gitosis-admin.git:

[gitosis]

[group gitosis-admin]
writable = gitosis-admin
members = some_user@example.com

[group project_foo_group]
members = some_user@example.com new_user@example.net
writable = project_foo

then as newuser at example.net:



cd project_foo
git remote add origin gitosis@example.com:project_foo.git
git push origin master

again, but cluttered with explanations


as some_user@example.com


sudo aptitude install gitosis
turns out there's a gitosis package in ubuntu. but it was unclear what installing it did. after reading some docs, such as /usr/share/doc/gitosis/README.rst.gz (installed with the gitosis package) and a blog post about hosting git, i was ready to guess. i found that the gitosis user had already been set up. finding its home directory was a matter of sudo su gitosis -;cd;pwd. this let me know first, that there was indeed a user named gitosis and second, that its home directory was at /srv/gitosis. this turns out to be irrelevant because the gitosis package already knows this. that directory was empty, iirc. so i guessed that i needed to pick up at gitosis-init.

sudo -H -u gitosis gitosis-init < ~/.ssh/id_rsa.pub

tell gitosis whom to trust for its administration. make the user gitosis run gitosis-init and give it the public key of whomever will admin the gitosis server (in this case it's the public rsa key of the current user). gitosis-init takes care of putting this file in the right place with the right name(/srv/gitosis/repositories/gitosis-admin.git/gitosis-export/keydir/some_user@example.com.pub). the exact place doesn't matter. it just matters that gitosis knows about it and will refer to it to verify requests, like cloning:

git clone gitosis@localhost:gitosis-admin.git

the current user (some_user@example.com) wants to clone from the user gitosis at localhost the gitosis-admin git repository. the user gitosis sees this request and needs to verify that it should fulfill that request. it uses the key (some_user@example.com.pub) that was just set up in the gitosis-init step. ssh magic happens here. the user gitosis trusts the incoming request and pushes out the repository to the requester, some_user@example.com. now some_user@example.com can modify the repository and write it back (all of these permissions and configurations are set up by gitosis-init.

cd gitosis-admin

when you clone the repo, its contents are put into a folder in the current directory, so go into it.

scp newuser@example.net:.ssh/id_rsa.pub keydir/newuser@example.net.pub

copy yet another public key into the key directory. this is the public key of newuser at example.net. copy it to the directory keydir with the filename user@host.pub. gitosis knows to look in this directory for files named like this for the public key.

vim gitosis.conf #:tabnew /usr/share/doc/gitosis/examples/example.conf
#add newuser@example.net to project_foo_group, make project_foo writable, close

modify gitosis.conf to add the relevant sections. scroll up a bit to see gitosis.conf. there is an example configuration at the location specified. in my example i create a group called new_project_group for the repository new_project. it says that the users some_user@example.com and newuser@example.net can write (which implies read and write) to the project new_project.

git add .
git ci -m "add newuser@example.net to project_foo_group, make project_foo writable"

save those changes! the public key and the configuration change! very important. but remember, this is just saving locally. you haven't told anyone else about this.

git push origin master

put those changes back up to the repository that gitosis@example.com knows about! gitosis will check its current configuration and see that it accepts changes from some_user@example.com to the gitosis-admin.git repository. gitosis has some kind of hook magic going on here. i'm not sure if it's a post-commit, or post-applypatch or what. but something black box happens here. the result of the black box is that changes you just pushed back to the gitosis-admin repo are now reflected by gitosis@example.com. it now knows about a new_project_group group, and the public key of newuser@example.net (for more ssh magic), and about the new_project repository.

as newuser@example.net


so now that the server has been set up and configured and doodadded, it's time to go put some content in it, other than the content that determines the configuration and the doodadding.

cd project_foo

newuser@example.net already has a project with some commits in it. so go into that directory.

git remote add origin gitosis@example.com:project_foo.git

tell that git repo about the new git server! gitosis on example.com knows about the project_foo git repo from the configuration file pushed to it earlier.

git push origin master

and the finish! this will push the master branch to the location nicknamed origin. origin is the nickname for gitosis@example.com:new_project.git. gitosis receives this request, does more ssh magic by looking at the public key pushed in gitosis-admin/keydir/newuser@example.net.pub. hooray! gitosis@example.com will see that the repo doesn't already exist, and accept the push, creating and populating the repo in one swoop! hooray!

Friday, February 5, 2010

slightly better kludge for suppressing ruby soap warnings

code i ended up with, followed by a description of the journey:

#monkeypatch kludge to get Parser to suppress "ignored element" warnings
#generated when mumboe-soap4r consumes wsdl that asp.net serves
#TODO: find a cleaner way to do this
class WSDL::Parser
old_initialize = instance_method(:initialize)
define_method(:initialize) do |*args, &block|
old_initialize.bind(self).call(*args, &block)

#set up QNames to ignore
names = ["binding", "operation", "body", "header", "address"]
namespace = "http://schemas.xmlsoap.org/wsdl/soap12/"
ignored = names.inject({}) do |ignored, name|
elename = XSD::QName.new(namespace, name)
ignored.update(elename => elename)
end

instance_variable_set(:@ignored, ignored)
end
end

how i got there


yesterday i implemented a kludge to suppress these warnings, but it was a bit broad. it would suppress everything printed to stderr during decode_tag. i refined that method to get to the code above. to make yesterday's kludge, i had to read some of the code in /var/lib/gems/1.9.0/gems/mumboe-soap4r-1.5.8.3/lib/wsdl/parser.rb. i noticed the use of @ignored to prevent duplicate warnings. i figured i could populate it with the things i don't care about before doing any wsdl consumption. turns out that worked.

what warnings to suppress


so i needed to print out the contents of @ignored. at first, i tried monkeypatching decode_tag to print out stuff as it was added to @ignored but that resulted in many duplicates. ironically, avoiding this duplication is exactly the reason for having @ignored. instead i went to the place where decode_tag was first called and monkeypatched that method (start_element).

deeper


i found that @ignored was populated with XSD::QNames. so i went to .../lib/xsd/qname.rb to find out how to create them and how they were compared. QName creation requires a namespace and a name. so i needed to have each key and value in @ignore print its namespace and name. fortunately these were readable attributes, so i just had to modify start_element in wsdl/parser.rb

back to wsdl/parser.rb


so i landed on this monkeypatch, which is the slickness:

class WSDL::Parser
def parse(string_or_readable)
@parsestack = []
@lastnode = nil
@textbuf = ''
@parser.do_parse(string_or_readable)
puts
puts "ignored = {"
@ignored.each do |key, value|
puts "XSD::QName.new(\"#{key.namespace}\", \"#{key.name}\") =>"
puts "XSD::QName.new(\"#{value.namespace}\", \"#{value.name}\"),"
end
puts "}"
@lastnode
end
end

that's right, it prints out the information i need formatted as ruby code so i just copy-pasta'd the output back into my code and massaged it a little.

kinda sorta almost


this result was almost what i wanted. here's the code:

class WSDL::Parser
old_initialize = instance_method(:initialize)
define_method(:initialize) do |*args, &block|
old_initialize.bind(self).call(*args, &block)
ignored = {
XSD::QName.new("http://schemas.xmlsoap.org/wsdl/soap12/", "binding") =>
XSD::QName.new("http://schemas.xmlsoap.org/wsdl/soap12/", "binding"),
XSD::QName.new("http://schemas.xmlsoap.org/wsdl/soap12/", "operation") =>
XSD::QName.new("http://schemas.xmlsoap.org/wsdl/soap12/", "operation"),
XSD::QName.new("http://schemas.xmlsoap.org/wsdl/soap12/", "body") =>
XSD::QName.new("http://schemas.xmlsoap.org/wsdl/soap12/", "body"),
XSD::QName.new("http://schemas.xmlsoap.org/wsdl/soap12/", "header") =>
XSD::QName.new("http://schemas.xmlsoap.org/wsdl/soap12/", "header"),
XSD::QName.new("http://schemas.xmlsoap.org/wsdl/soap12/", "address") =>
XSD::QName.new("http://schemas.xmlsoap.org/wsdl/soap12/", "address"),
}
instance_variable_set(:@ignored, ignored)
end
end

this was nearly good enough for me to stop. it suppressed exactly what i knew about. nothing more, nothing less. but that's a lot of text. and a lot of it is repepetetetivive. and every QName is created twice, so technically it wasn't populating @ignored in exactly the same way as decode_tag does. and what if the namespace needs changing, or i want to add another name? i felt this would be annoying to maintain.

another step


so i used a variable to store the namespace and used a variable to store each QName.
here's that code:

class WSDL::Parser
old_initialize = instance_method(:initialize)
define_method(:initialize) do |*args, &block|
old_initialize.bind(self).call(*args, &block)
namespace = "http://schemas.xmlsoap.org/wsdl/soap12/"
binding = XSD::QName.new(namespace, "binding")
operation = XSD::QName.new(namespace, "operation")
body = XSD::QName.new(namespace, "body")
header = XSD::QName.new(namespace, "header")
address = XSD::QName.new(namespace, "address")
ignored = {
binding => binding,
operation => operation,
body => body,
header => header,
address => address,
}
instance_variable_set(:@ignored, ignored)
end
end

all right. better. but ... augh, i can't let it be that way. i mean, come on! for every ignored thing, the name of the ignored thing shows up 4 times, on 2 different lines that aren't right next to each other. still not DRY enough. i figured i could create an array with symbols and do something with that. turns out that 3 of the 4 times a name showed up, it was just a temporary variable and the true essence could be summed up more generically so that any particular name was only given once! so i messed with Enumerable#each and finally landed on a workable solution using Enumberable#inject.

final code again (so you don't have to scroll up):



#monkeypatch kludge to get Parser to suppress "ignored element" warnings
#generated when mumboe-soap4r consumes wsdl that asp.net serves
#TODO: find a cleaner way to do this
class WSDL::Parser
old_initialize = instance_method(:initialize)
define_method(:initialize) do |*args, &block|
old_initialize.bind(self).call(*args, &block)

#set up QNames to ignore
names = ["binding", "operation", "body", "header", "address"]
namespace = "http://schemas.xmlsoap.org/wsdl/soap12/"
ignored = names.inject({}) do |ignored, name|
elename = XSD::QName.new(namespace, name)
ignored.update(elename => elename)
end

instance_variable_set(:@ignored, ignored)
end
end

now when someone wants to add a new ignored name, they just add it to the list of names that get ignored! one place! <comma> <space> <quotation mark> <name> <quotation mark>. the rest of the work is done generically, automatically. and i think reading this is easier than reading the intermediate steps. there's less to read. less dense. easier to update. a good kludge.
so i'm glad that's done. ... or is it?

it's still a kludge


ultimately, this is neat and all, but it's still a kludge. i need to figure out why these warnings are generated at all. i don't want to have to read a wsdl, or read more code involved in the process of parsing a wsdl. a brief look tells me it must be that parent.parse_element(elename) returns nil in some case. i don't know what type parent is and i don't feel like searching any further right now.

Thursday, February 4, 2010

kludge: suppress mumboe-soap4r warnings

first the code, then the explanation:

#monkeypatch kludge to get Parser to suppress "ignored element" warnings
#generated when mumboe-soap4r consumes wsdl that asp.net serves
#TODO: find a cleaner way to do this
require 'stringio'
class WSDL::Parser
private
old_decode_tag = instance_method(:decode_tag)
ignored_string = StringIO.new
define_method(:decode_tag) do |*args, &block|
old_stderr, $stderr = $stderr, ignored_string
result = old_decode_tag.bind(self).call(*args, &block)
$stderr = old_stderr
result
end
end

situation


recently i've been dealing with soap. T_T. but i'm getting paid. ^_^. i'm writing the client in ruby 1.9 using mumboe-soap4r lib (gem1.9 install -r mumboe-soap4r). asp.net serves up the web services. the wsdl that asp.net serves has elements that the ruby lib ignores. mumboe-soap4r generates warnings printed to stderr.
the warnings (modified to be more generic):

ignored element: {http://schemas.xmlsoap.org/wsdl/soap12/}binding : #<WSDL::Binding:0xd2c490 {http://example.com/ExampleServices}ExampleSoap12>
ignored element: {http://schemas.xmlsoap.org/wsdl/soap12/}operation : #<WSDL::OperationBinding:0xd21a54 CheckLogOnCredentials>
ignored element: {http://schemas.xmlsoap.org/wsdl/soap12/}body : #<WSDL::Param:0xd20a78 >
ignored element: {http://schemas.xmlsoap.org/wsdl/soap12/}header : #<WSDL::Param:0xd20a78 >
ignored element: {http://schemas.xmlsoap.org/wsdl/soap12/}address : #<WSDL::Port:0x1083bfc {http://example.com/ExampleServices}ExampleSoap12>

motivation


this was printed out to stderr every time the client grabbed the wsdl and parsed it. reading unit test results was quite annoying. and running the client was annoying. i could just redirect stderr to /dev/null, but then i wouldn't get any errors at all. this code diagnoses problems and uses stderr to report those problems. so i need something more precise.

solution


so instead i redirect stderr but only for the duration of the method that actually generates warnings. the file /var/lib/gems/1.9.0/gems/mumboe-soap4r-1.5.8.3/lib/wsdl/xmlSchema/parser.rb (on ubuntu 9.10) defines the decode_tag method. i found it like this:

% gem1.9 environment|grep INSTALLATION
- INSTALLATION DIRECTORY: /var/lib/gems/1.9.0
% cd /var/lib/gems/1.9.0/
% ls
% cd mumboe-soap4r-1.5.8.3/lib
% grep -r "ignored element" *
wsdl/parser.rb
wsdl/xmlSchema/parser.rb

then i went back to a blog post i read earlier: jay fields alias method alternative. i also noticed there a link to another post by martin on method replacing. then i did a bit of trial and error to get it working for me in this case, where the class is defined in a module. once i was able to replace the method with my code, written in my file, i just needed to write code that would suppress stderr, call the original method, then resurrect stderr. temporarily redirecting stdout (or stderr) is common enough that someone had written nearly exactly what i needed. i didn't need to search for long.

Wednesday, January 27, 2010

ruby 1.9 libs, openssl

yet another problem solved:

ledZeppelin% ./minimal.rb
/usr/lib/ruby/1.9.0/net/smtp.rb:197:in `default_ssl_context': uninitialized constant Net::SMTP::OpenSSL (NameError)
from /usr/lib/ruby/1.9.0/net/smtp.rb:342:in `enable_starttls'
from ./minimal.rb:24:in `_send_email'
from ./minimal.rb:33:in `
'
ledZeppelin% #futz around with gem1.9 looking for the obvious ssl package
ledZeppelin% aptitude search ssl|grep -i ruby
p libopenssl-ruby - OpenSSL interface for Ruby
i A libopenssl-ruby1.8 - OpenSSL interface for Ruby 1.8
p libopenssl-ruby1.9 - OpenSSL interface for Ruby 1.9
p libopenssl-ruby1.9.1 - OpenSSL interface for Ruby 1.9.1
ledZeppelin% sudo aptitude install libopenssl-ruby1.9
[sudo] password for danny:
Reading package lists... Done
Building dependency tree
Reading state information... Done
Reading extended state information
Initializing package states... Done
The following NEW packages will be installed:
libopenssl-ruby1.9
0 packages upgraded, 1 newly installed, 0 to remove and 15 not upgraded.
Need to get 133kB of archives. After unpacking 979kB will be used.
Writing extended state information... Done
Get:1 http://us.archive.ubuntu.com karmic/universe libopenssl-ruby1.9 1.9.0.5-1ubuntu1 [133kB]
Fetched 133kB in 1s (121kB/s)
Selecting previously deselected package libopenssl-ruby1.9.
(Reading database ... 212364 files and directories currently installed.)
Unpacking libopenssl-ruby1.9 (from .../libopenssl-ruby1.9_1.9.0.5-1ubuntu1_amd64.deb) ...
Setting up libopenssl-ruby1.9 (1.9.0.5-1ubuntu1) ...
Reading package lists... Done
Building dependency tree
Reading state information... Done
Reading extended state information
Initializing package states... Done
Writing extended state information... Done
ledZeppelin% uname -a
Linux ledZeppelin 2.6.31-17-generic #54-Ubuntu SMP Thu Dec 10 17:01:44 UTC 2009 x86_64 GNU/Linux
ledZeppelin% lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 9.10
Release: 9.10
Codename: karmic
ledZeppelin% ./minimal.rb
ledZeppelin% #email in my inbox, yay


and that was it

be careful with ruby include

turns out the problem i've been having over the last few days has been that i included some libs in the top level of a ruby file. so the module's definition of "read" overrode the kernel's version. as you might suspect, changing ruby's idea of "read" around leads to errors. in my case, the first code to hit this was inside a soap library, which called an http library. so i thought the problem might be in there. i spent yesterday and today tracking that error down. i did other things during that time, but still that was lots of time.

i tracked it down by creating a minimal test case for the soap portion, iprofiletools.rb, creating a class out of it and having a small driver at the end of the class definition file: if $0 == __FILE__ then driver_code end.
when that worked, but using the class in the project still yielded errors, i knew it wasn't the soap code that was bad. i suspected global variables or constants set in the module might be messing with some setting used somewhere down the line.
so i tried putting in all the includes from vue_monitor.rb to the top of iprofiletools.rb.
this reproduced the errors i saw when running vue_monitor.rb. since i didn't actually need any of those "require"s and "include"s, i started removing them and rerunning iprofiletools until i had a minimal set that created the error. that led me to SimpleCommons
i looked in SimpleCommons for globals and constants and found nothing obvious. so i started commenting out large portions and rerunning iprofiletools. eventually i narrowed it down to the read function.
that's when i realized that i was "include"ing SimpleCommons at the top level. all i needed to do was include it within the class, instead of at the top level, directly into Kernel.
that's a better model of what i want anyways. i want the stuff in SimpleCommons to be a part of the Vue_Monitor class, not a part of Kernel.

i'm not sure how this problem didn't come up earlier. git bisect would have been a better approach to this. i could have seen the diff that started this all. but the problem only appeared on one machine and not on my laptop. so i initially started with the suspicion that the installed library was the culprit. i'm still not sure why errors only produced on one machine and not the other.

anywho. i've fixed it. i understand part of what was wrong. i know now to be careful with includes.

Where am I?