Auto set perlbrew with bash and git
I’ve started using the lib feature of perlbrew with all of my perl projects. I create a local::lib for each project and switch using ‘perlbrew use’ accordingly.
The following bash script aliases ‘cd’ so that when I switch to a git directory the correct perlbrew lib is automatically set.
To accomplish this I set two custom git config parameters:
mydata.perlbrew.perl
mydata.perlbrew.lib
If I want to use perl version 5.14.2 and a local::lib called ‘myproject’
git config mydata.perlbrew.perl perl-5.14.2
git config mydata.perlbrew.lib myproject
I use git config mostly out of laziness. The git command already knows how to figure out when you are in a repository at any directory level so I save having to write that code in shell script.
function cd_func () {
builtin cd "$@"
wanted_perl=$(git config mydata.perlbrew.perl 2>/dev/null)
if ([ -n "$wanted_perl" ]); then
wanted_lib=$(git config mydata.perlbrew.lib 2>/dev/null)
perlbrew_cmd="perlbrew use $wanted_perl@$wanted_lib"
if ([ -n "$wanted_lib" ]); then
if ([ -n "$PERLBREW_PERL" ] && [ -n "$PERLBREW_LIB" ]); then
if [ $PERLBREW_PERL != $wanted_perl ] || [ $PERLBREW_LIB != $wanted_lib ]; then
${perlbrew_cmd}
fi
else
${perlbrew_cmd}
fi
fi
elif ([ -n "$PERLBREW_PERL" ] && [ -n "$PERLBREW_LIB" ]); then
perlbrew_cmd="perlbrew use perl-5.14.2@myperl"
${perlbrew_cmd}
fi
}
alias cd=cd_func
You will notice that I have a base lib called ‘myperl’ that I switch to whenever I am not working in a Git repository. It will only set this value if PERLBREW_PERL and PERLBREW_LIB are not already set. This allows me to use perlbrew freely outside of git repositories.
Cygwin paths and perlbrew
I’ve been messing around with perlbrew lately and decided to give it a whirl on Cygwin. Right off the bat I ran into a problem with my $PATH.
PATH=/usr/local/bin:/usr/bin:/usr/local/bin:/usr/bin:/cygdrive/c/Program Files/Common Files/Microsoft Shared/Windows Live:/cygdrive/c/Program Files (x86)/Common Files/Microsoft Shared/Windows Live:/cygdrive/c/Program Files (x86)/AMD APP/bin/x86_64:/cygdrive/c/Program Files (x86)/AMD APP/bin/x86:/cygdrive/c/Windows/system32:/cygdrive/c/Windows:/cygdrive/c/Windows/System32/Wbem:/cygdrive/c/Windows/System32/WindowsPowerShell/v1.0:/cygdrive/c/Program Files/Intel/WiFi/bin:/cygdrive/c/Program Files/Common Files/Intel/WirelessCommon:/cygdrive/c/Program Files (x86)/ATI Technologies/ATI.ACE/Core-Static:/cygdrive/c/Program Files (x86)/Intel/Services/IPT:/cygdrive/c/Program Files (x86)/Symantec/VIP Access Client:/cygdrive/c/Program Files (x86)/Sony/VAIO Startup Setting Tool:/cygdrive/c/Program Files (x86)/Common Files/Roxio Shared/10.0/DLLShared:/cygdrive/c/Program Files (x86)/Common Files/Roxio Shared/DLLShared:/cygdrive/c/Program Files (x86)/Windows Live/Shared
Spaces everywhere, which the perl installer did not take kindly to.
I had two options at this point:
- Set $PATH to only include what was needed to install and be done with it
- Spend a good chunk of time Googling how to convert the paths to something perl wouldn’t complain about.
I of course, took option 2.
I wanted my default paths to work with whatever I needed to do. $PATH is given to Cygwin by Windows, so I’d like to keep it that way for consistency. The necessity of doing this can be debated.
I needed:
/cygdrive/c/Program Files/Common Files/Microsoft Shared/Windows Live
to become:
/cygdrive/c/PROGRA~1/COMMON~1/MICROS~1/WINDOW~1
cygpath
I found cygpath and thought it would be all I needed.
I was wrong.
The command has options to display unix style paths, windows style paths and short paths, but it does not allow you to combine the short style with the unix style.
Unix Style Path:
/cygdrive/c/Program Files/Common Files/Microsoft Shared/Windows Live
Mixed Style Path:
$ cygpath -m "/cygdrive/c/Program Files/Common Files/Microsoft Shared/Windows Live"
C:/Program Files/Common Files/Microsoft Shared/Windows Live
Short Style Path:
$ cygpath -m -s "/cygdrive/c/Program Files/Common Files/Microsoft Shared/Windows Live"
C:/PROGRA~1/COMMON~1/MICROS~1/WINDOW~1
Fixing $PATH
I would need to create a shell script to combine the mixed and short style paths into something I could use.
# First we need to split $PATH
# on ':' and put each line into
# an array
OLD_IFS=$IFS
IFS=$'\n'
paths=(`echo $PATH | tr -s ":" "\n"`)
IFS=$OLD_IFS
# The paths supplied from Windows
# may not actually exist. These
# paths will cause cygpath to error
# so we will skip them and only
# process actual directories.
valid_paths=();
for path in "${paths[@]}"; do
if [ -d "${path}" ]; then
# Save the cygdrive we are parsing
cygdrive_save=$(echo $path | grep -oEi "/cygdrive/[a-z]+")
if [[ $cygdrive_save ]]; then
# Get the short version of the path from cygpath
# We can't have ':' in our path so only grab the
# path following C: (or other drive letter)
no_space_path=$(cygpath -a -m -s "${path}" | cut -d : -f 2);
# Use the saved cygdrive from above and
# prepend it to the short path
valid_paths=("${valid_paths[@]}" "${cygdrive_save}${no_space_path}")
else
valid_paths=("${valid_paths[@]}" $path)
fi
fi
done
# Join the valid_paths array with ':'
# and set $PATH
OLD_IFS=$IFS
IFS=":"
PATH="${valid_paths[*]}"
IFS=$OLD_IFS
Add to .bash_profile and now my $PATH looks like this:
$ printenv PATH
/home/Kevin/perl5/perlbrew/bin:/usr/local/bin:/usr/bin:/usr/local/bin:/usr/bin:/cygdrive/c/PROGRA~1/COMMON~1/MICROS~1/WINDOW~1:/cygdrive/c/PROGRA~2/COMMON~1/MICROS~1/WINDOW~1:/cygdrive/c/PROGRA~2/AMDAPP~1/bin/x86_64:/cygdrive/c/Windows/system32:/cygdrive/c/Windows:/cygdrive/c/Windows/System32/Wbem:/cygdrive/c/Windows/System32/WINDOW~1/v1.0:/cygdrive/c/PROGRA~1/Intel/WiFi/bin:/cygdrive/c/PROGRA~1/COMMON~1/Intel/WIRELE~1:/cygdrive/c/PROGRA~2/ATITEC~1/ATI.ACE/CORE-S~1:/cygdrive/c/PROGRA~2/Intel/Services/IPT:/cygdrive/c/PROGRA~2/Symantec/VIPACC~1:/cygdrive/c/PROGRA~2/Sony/VAIOST~1:/cygdrive/c/PROGRA~2/COMMON~1/ROXIOS~1/10.0/DLLSHA~1:/cygdrive/c/PROGRA~2/COMMON~1/ROXIOS~1/DLLSHA~1:/cygdrive/c/PROGRA~2/WIC4A1~1/Shared
Now the Perl install (and anything else that is space sensitive) works:
$ perlbrew list
perl-5.14.2
$ perlbrew exec perl -v
perl-5.14.2
==========
This is perl 5, version 14, subversion 2 (v5.14.2) built for cygwin-thread-multi-64int
Copyright 1987-2011, Larry Wall
Perl may be copied only under the terms of either the Artistic License or the
GNU General Public License, which may be found in the Perl 5 source kit.
Complete documentation for Perl, including FAQ lists, should be found on
this system using "man perl" or "perldoc perl". If you have access to the
Internet, point your browser at http://www.perl.org/, the Perl Home Page.
Shell scripting is not my strong suit. I’m guessing there is a better and possibly more efficient solution to this problem. I welcome any suggestions to clean it up.
SQL Developer’s Annoying Theme
I use SQL Developer from Oracle. I like the fact that it is a single download and contains everything I want without the need for an extra client or annoying tsnames.ora file.
What I don’t like is the long load time and the heavy Java interface. After upgrading to 3.0 I ran into another issue, the screen would not re-draw correctly. If I clicked on a icon or had to scroll to see results the whole interface would break down and become unusable.
I found a few sites that recommended adding a few different java options to the call to sqldeveloper.exe, but nothing worked.
Finally I found this post, which describes how to set up SQL Developer for Windows 7. I run XP, but the solution to switch the theme from ‘Oracle’ to ‘Windows’ fixed all my problems. The interface is also more responsive now than it ever was under the previous theme or previous versions of SQL Developer.
Crashing Emails
Working for the “borg” as I like to call it, sometimes I get updates automatically installed on my laptop I’d rather live without. These absolutely necessary additions to my computer have some adverse effects from time to time. Recently after an update my Outlook went haywire. Simply viewing an email would crash Outlook. No obvious error, just a “hey I’m crashing would you like to restart me message”.
The answer, while not the perfect solution, was to enable reading all email in plain text. (actually I know more than a few HTML email haters who might think this is the perfect solution).
I’m posting the following link because from Googling I found a lot of people run into issues with Outlook crashing due to font or HTML rending issues.
http://www.addictivetips.com/microsoft-office/read-email-as-plain-text-in-outlook-2010/
Why have I not been using Gearman for years?
I think I’m done with all the queuing systems I’ve used to manage simple async work. I might look to throw up an example later, but really, how did I not get to this point sooner than I did? Glad to have Gearman up and running with Gearman::XS.
Fast. Simple. Effective.
If you are installing Gearman::XS on a Amazon Linux (used on a EC2 Instance) distribution check out this post about environment settings, you may have to use:
export GEARMAN_INCLUDE=/usr/local/include/
export GEARMAN_LIB=/usr/local/lib/
Adding a Decimal type to SimpleDB::Class
Entered a pull request on Git for SimpleDB::Class
domain_prefix fix
domain_prefix had a default of undef. This actually causes has_domain_prefix to always return true.
From Moose docs:
A predicate method tells you whether or not a given attribute is currently set. Note that an attribute can be explicitly set to undef or some other false value, but the predicate will return true.
This was causing the following warning when creating a domain and using items:
Use of uninitialized value in concatenation (.) or string at /home/kmcgrath/SimpleDB-Class/lib/SimpleDB/Class.pm line 303.
Decimal Type Addition
The new type allows for a number with precision after the decimal point to be used in comparisons and ordering. Good for storing prices, geo coordinates, etc…
The implementation basically keeps the same %015d format Int uses for the characters to the left of the decimal point and allows any number of characters to the right of the decimal point.
Added POD and tests.
Script Using Decimals
On Gist: https://gist.github.com/1159414
twitter-tools
In my “spare” time I like to fancy myself as a sports enthusiast. I live out my unhealthy love for sports as @dcsgKevin, host of the DC Sports Guys.
The show fulfills two needs of mine, venting about local teams and tinkering with different types of code and technology. I’ll focus on the latter here.
After some convincing I was able to get my co-hosts, TJ and Big J. to join Twitter. I was then presented with a problem; how to get multiple people to contribute as one on Twitter? I already had @dcsportsguys running and thought about asking everyone to start tweets with the fist initial of their name. My tweets would look like “K: My tweet.” This is actually pretty common with similar radio show Twitter accounts.
I decided that I didn’t like the prefix idea so I turned @dcsportsguys into a master account that would retweet conversations by @dcsgkevin, @dcsgtj, @dcsgbigj, and @dcsgsteele.
Using a combination of Net::Twitter and AnyEvent::Twitter::Stream I created a daemon that instantly retweets any conversations we are apart of. We tell people to follow one account, @dcsportsguys, and they instantly follow all of us.
Most of this code was written with a brute force mentality. Or maybe better explained as a “I just need this damn thing to work now” attitude. Before moving it to github I’ve started to add comments and bits of documentation. I’m hoping that by posting this I will force myself to make what I’ve done useful for others.
Currently there are two scripts, “retweeter” and “twitterctl”
retweeter
A daemon that can be configured with Config::Any. It will retweet conversations for any users you define.
twitterctl
This is simply a command line tool for Net::Twitter. When messing with what functions do and what they return I found it nice to be able to run this on the command line to get quick dumps of data from Twitter. It will run any method of Net::Twitter and by default dump the response with Data::Dumper. You can configure the output to be anything Any::Renderer can handle.
./twitterctl --config-file ./myOAuthConfig.yaml --followers
./twitterctl --config-file ./myOAuthConfig.yaml --lookup-users --screen-name dcsgkevin
GitHub
https://github.com/kmcgrath/perl5-twitter-tools
Warning
These scripts have a long way to go to become “user friendly”, but again I hope this is at least a good start.
Moving modules to GitHub
Decided to jump on the bandwagon and move the CPAN modules I support to GitHub. This is all part of an effort that includes keeping documentation up to date and fixing some outstanding bugs.
New GitHub Repositories
Any::Renderer::Data::Printer
After getting some positive feedback from Breno G. de Oliveira, the author of Data::Printer, about my post regarding the use of a single interface to control different ‘dumping’ mechanisms, I decided to take a closer look at Any::Renderer.
Following the docs and armed with an excuse to finally use github I whipped up Any::Renderer::Data::Printer
The module does exactly what you expect but there are a couple things about Any::Renderer that are annoying.
- Any::Renderer auto loads any backends (filters) it finds in @INC whether you use them or not.
- The framework allows for a backend to have many available formats and for those formats to be freely named. To load by name Any::Renderer finds all existing format names on creation.
- It would be nice if there was an ‘on-demand’ way to do this so that only what is needed is loaded.
- Not all the dependencies are listed in the manifest.
- None of the pre-requisites for the following modules that ship with Any::Renderer are listed:
- Any::Renderer::Template
- Any::Renderer::XSLT
- Any::Renderer::UrlEncoded
- Any::Renderer::JavaScript
- Any::Renderer::JavaScript::Anon
- The kicker is Any::Renderer doesn’t actually need any of the above modules to run. Without the dependencies for them it throws a bunch of warnings about not being able to load particular backends then proceeds without error.
- If I don’t need the Javascript output I don’t feel like I should have to install all the modules for it.
- Spitting warnings to STDERR for a failed backend that I don’t need is unnecessary. Saving all failed modules in an array for instance, then letting the programmer access them though a failed_backends() function would keep screen noise down. Or implement a debug mode, there are many possible solutions.
- None of the pre-requisites for the following modules that ship with Any::Renderer are listed:
I do plan on sending these ideas to the Any::Renderer module owner after I mock up some solutions for them. I don’t like complaining unless I’m helping out with the solution.
If you don’t want to mess with Any::Renderer and want to roll your own solution, take a look at my previous post which has the basic setup of what you will need.
Data::Printer - a colored pretty printer for Perl
Data::Printer as described by The Onion Stand is a great tool for debugging Perl structures.
My only thought is that with all the different ways to print structures maybe it’s time for a unified interface that makes it easy for developers to switch between debugging outputs. Maybe after viewing a structure with Data::Printer you want to mess with the object itself and need to serialize with Data::Dumper or Data::Dump
Closest module I found after a quick search was Any::Renderer. It was last modified in 2006 which makes me a bit weary, but the concept is in line with what I was looking for.
Below I’ve included a quick mock-up of a custom module that can switch output types easily for debugging purposes:
- Save the code as something like data_renderer.pl
- Run normally to see both Data::Printer and Data::Dumper outputs
- Run with the USE_RENDERER environment variable set to change output.
- USE_RENDERER=’Data::Dumper’ perl data_render.pl
- Notice that setting $MyDataRenderer::USE_RENDERER with local will always use a chosen renderer no matter what the ENV reads.
data_renderer.pl:
package MyDataRenderer;
use Data::Dumper;
use Data::Printer;
use constant FUNCTION_MAP => {
'Data::Printer' => 'p',
'Data::Dumper' => 'Dumper',
};
BEGIN {
our @ISA = qw(Exporter);
our @EXPORT = qw(my_data_renderer);
our $USE_RENDERER = 'Data::Printer';
if ($ENV{USE_RENDERER}) {
$USE_RENDERER = $ENV{USE_RENDERER};
}
}
sub my_data_renderer {
my $f = FUNCTION_MAP->{$USE_RENDERER};
&$f(@_);
}
package main;
import MyDataRenderer;
my $hash_ref = {
foo => 'bar',
bar => ['foo1','foo2','foo3']
};
printf "%s\n", my_data_renderer($hash_ref);
{
local $MyDataRenderer::USE_RENDERER = 'Data::Dumper';
printf "%s\n", my_data_renderer($hash_ref);
}
