Fun with bundle (BUNDLE_DISABLE_SHARED_GEMS)

So, I removed rvm and installed rbenv. All in all straight forward. Until I started it to use with an older project, that is requiring REE. Set a .ruby-version and checked the ruby. All good. Sweet. Bundle and off I should go. Nope… Besides some special dependencies (e.g. Nokogiri lately requiring ruby > 1.9.2), which I solved by installing it separately, bundle did keep complaining about not being able to install Nokogiri. Why trying to install again? It’s there, I installed it myself!

But no, a “gem list” just gave me a short list of gems, way shorter than what already had been installed by bundler. I kept checking for other Gem paths, other weird dependencies or rvm leftovers. Nothing. Until I did the following:

daniel@daniels-mac ~/w/s/platform> cat .bundle/config                                                                                                                                                                            1 dh-fixes!?
---
BUNDLE_PATH: rake
BUNDLE_DISABLE_SHARED_GEMS: "1"
daniel@daniels-mac ~/w/s/platform> rm -rf .bundle

Bundle was configured not to share the gems and therefore never picked up the ones installed. Weird behavior, but deleting the .bundle in my project folder did the trick. See some infos on Stackoverflow

NoMethodError: undefined method `[]‘ for nil:NilClass error on heroku

It’s been a while since I’ve updated my app on heroku. In between the mongoid version updated and configuration file (mongoid.yml) did change. That for me was the most obvious change, but then as well, there were a couple of things under the hood, that only showed up once I tried to deploy on heroku. The above error appeared and left me clueless for a moment, until I figured out, I had to define the ruby version 1.9.3 in my Gemfile:

source 'http://rubygems.org'

ruby '1.9.3'

gem 'rails', '~>3.2'

gem 'jquery-rails'
gem 'mongoid'

This change made the error disappear. For some more background information see Mongoid Tips, this blog post on heroku and this Stackoverflow post.

For who is interested, the new mongoid.yml looks like this in my project:



# set these environment variables on your prod server
production:
  sessions:
    default:
      uri: <%= ENV['MONGOLAB_URL'] %>
      options:
        skip_version_check: true
        safe: true

heroku command line basics

Just as a reminder to myself, as I don’t use it regularly:

  • adding an environment variable: heroku config:add GITHUB_USERNAME=joesmith
  • Running the Rails console: heroku run console
  • get the currently used ruby version: heroku run ‘ruby -v’

Boostrap Typeahead with object

The Bootstrap Framework is an awesome base for my applications, Styling is good and easy. It also brings some nice components into the play, one of them the Typeahead. You simply register it with your input and it will return matching values. Unfortunately it does so only for Strings, but I needed a solution to return object and work of them to also populate other fields in the same form. This did not work out of the box, but luckily I found a nice gist that did add these capabilities. Go, have a look and enjoy the possibilities.

Since the version gisted is based on an older Boostrap version, I did update it to work with v2.2.2.

To add the patched version, add it to a suitable location to be loaded, in my Rails 3.2 project I placed it under vendor/assets/javascripts/twitter/bootstrap and added it to my application.js:


//= require twitter/bootstrap/bootstrap-typeahead

You are now able to use objects as return values and specify the property that should be used as display value in the list. The onselect callback allows for further manipulations, I for example set the value to the food name, update a select box with the retrieved list of serving_sizes and set an hidden field to the internal id (recipe.js.coffee):


typeahead_config =
  source: (query, process) ->
    $.get "/foods",
      name_like: query
      format: 'json'
    , (data) ->
      process data
  property: 'name'
  onselect: (obj) ->
    food = JSON.parse(obj)
    number_regex = /\d+/
    id = number_regex.exec(this.$element[0].id)
    $('#' + this.$element[0].id).val(food.name)
    $('#' + 'recipe_ingredients_attributes_' + id + '_internal_reference').val(food._id)
    if food.serving_sizes.length > 0
      servings = $('#' + 'recipe_ingredients_attributes_' + id + '_serving_size')
      servings.empty()
      for e in food.serving_sizes
        servings.append($("<option></option>").attr("value",e.name).text(e.name))
  minLength: 3

Parsing german dates from unformatted input

In a recent case I needed to combine many date values out of a bunch of XForms documents. The forms had changed over time in a way that there was no date input at the beginning, so date values could be typed in freely. That ended up in values like “07.07.2012″, “7.7.2012″, “07. 07.2012″, “07.07.12″ and so on. Then there were also erroneously typed spaces. Later on, the input was marked as date type and standard input selectors were used, dates from that moment on were saved as “2012-07-07T12:00:00″ (UTC Time Format as specified in ISO 8601).

So, what I needed was a way to get the date formatted in a standard way as “dd.MM.yyyy” (first of the examples above) or the original value back for further error handling. I did not want to include any further libraries as the project is distributed via web and I want to keep the dependencies as little as possible. So I came up with a method to parse the dates from a combination of SimpleDateFormat, that involve the different possibilities:


private String tryConvertDate(String date) {
        String value = "";
        if (date != null) {
            value = date;
            // remove spaces
            date = date.replaceAll(" ", "");
            // remove time zone information at the end of the string as we are only in one timezone and interested in the date
            date = date.replace("\\+.{5}$","");
            List<SimpleDateFormat> dfs = new ArrayList<SimpleDateFormat>();
            // add english date format as first, as it comes from date input and is strictly defined and therefore needs to be parsed first
            dfs.add(new SimpleDateFormat("yyyy-MM-dd"));
            // iterate the different possible formats for each part of the date and finally create a SimpleDateFormat for it
            for (String day : Arrays.asList("d", "dd")) {
                for (String month : Arrays.asList("M", "MM")) {
                    for (String year : Arrays.asList("yy", "yyyy")) {
                        for (String devider : Arrays.asList(".", "-", "")) {
                            dfs.add(new SimpleDateFormat(day + devider + month + devider + year)); // german style dates
                        }
                    }
                }
            }
            Date tempDate = null;
            // iterate over the create DateFormats and test the date. If DateFormat fits, set tempDate to the parsed Date
            for (SimpleDateFormat df : dfs) {
                if (tempDate == null) {
                    try {
                        tempDate = df.parse(date);
                    } catch (ParseException e) {
                    }
                }
            }
            // if we found a value, format the correct Date String
            if (tempDate != null) {
                SimpleDateFormat outputFormat = new SimpleDateFormat("dd.MM.yyyy");
                value = outputFormat.format(tempDate);
            }
        }
        return value;
    }

May that be of use to someone else or as a starting point for your similar problem.

Tapestry to Rails 1: Migrate User authentication from Spring Security to Devise

So, here I am, migrating an application from Tapestry to Rails. First issue I ran into is how to authenticate existing users. The Spring Security setup I used wasn’t to advanced, but still I had to figure out all of the defaults and how to implement them in Ruby.

But first things first, I needed a hash to test against. I setup an user using a simple password (“password”). The resulting hash is “94fd377436677640be814ac7b2d98b219f1f96d4″, encoded by the password and a salt. Ok, for the password, what’s missing is the digest to use and the salt.

To figure out the used encoder, I injected it and output it simply via: 


System.out.println(encoder);
//org.springframework.security.authentication.encoding.ShaPasswordEncoder

The same I did with the saltSource to figure out which Salt is used all over the system (I did not set up a custom salt, nor user-based salt, shame on me): 


System.out.println(saltSource.getSalt(user));
// DEADBEEF

I ended up with the information, that the Encoder used is the SHAPasswordEncoder and a system wide salt of “DEADBEEF”. Looking at the source, the line of interest can be found in BasePasswordEncoder#mergePasswordAndSalt(String password, Object salt, boolean strict):

 return password + "{" + salt.toString() + "}";

That can be easily written in Ruby (irb or rails console) as:


require 'digest'
Digest::SHA1.new.hexdigest "password{DEADBEEF}"
=> "94fd377436677640be814ac7b2d98b219f1f96d4"

To be able to use Devise with our old passwords, the solution is to override the “valid_password?” method, as suggested in this Wiki entry.
The above code used, it is now possible to authenticate existing users with Devise and still use all the nifty new features coming with it.

Running a Dart example on MacOS

So, as explained in my previous post I did install Dart on my Machine. And of course I was curios to run one of the examples of the dartlang.org website. To do so, is actually quite simple:


cd ~/workspace/dart/dart/client python ./tools/htmlconverter.py \ > samples/slider/slider_sample.html -o out/

This compiles the slider example to the out folder. To run, you call your Google Chrome Browser with the newly created html file. Make sure Chrome is not currently running as this will create a conflict accessing the profile


/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome \  > out/client/samples/slider/slider_sample-js.html

This opens Chrome with the sample application running. For those who just want to try out some basic, I do recommend the Tutorials and the Try-Dart Interactive Shell on Google Apps

Getting Googles Dart installed on MacOS X

So, today it finally became available for the public. And since it sounds really promising, I thought it might be good to get it going right away. Oh, btw, in my opinion the possibility to switch from a quick-and-dirty approach with dynamically typed variables at the beginning for prototyping to statically typed variables as the project evolves sounds just right. No need to decide upfront whether to go for the big-overhead-framework or the simplest possible approach as this simply develops as needed by adding libraries or adding domain specific features. Sounds great, let’s see if they can get some grip soon. For those interested in finding out more, read the introduction to Dart.

On their pages they do also explain how to get Dart up and running, I will follow those instructions and will summarize the steps necessary:

  1. Install XCode >= 3.2 (I assume you have this already, if not, install it from your MacOS X installation DVD)
  2. Install the depot_tools and add them to your path:
    cd ~/workspace/dart
    svn co http://src.chromium.org/svn/trunk/tools/depot_tools
    export PATH="$PATH":`~/workspace/dart/depot_tools
  3. checkout the Dart sources (this may take a while depending on your connection):
    gclient config http://dart.googlecode.com/svn/trunk/deps/all.deps
    gclient sync
  4. at the end of the sync I get a message that DumpRenderTree could not be downloaded and I need to execute another command. So I do:
    third_party/gsutil/20110627/gsutil config
  5. This tool prompts for a lot of setup and configuration, such as setting up your Google Account for use with Dart. I won’t repeat all the steps here as they may not be applicable to you. But you will need a Google Account and the Cloud Storage activated.
  6. Ok, building Dart now:
    ./tools/build.py --arch=ia32
    
  7. That is just what I want to see:
    === BUILD AGGREGATE TARGET All OF PROJECT dart WITH CONFIGURATION Debug_ia32 ===
    Check dependencies
    ** BUILD SUCCEEDED **
    
  8. Now I’m running the tests to check whether all is fine:
    ./tools/test.py --arch=ia32,dartc,chromium
  9. This did not go through completely and it seems DumpRenderTree has not been installed properly. I got to check this out, but hope you’ve been more successful so far. I will be back. Nevertheless I can run the other tests separately:
    ./tools/test.py --arch=ia32,dartc
    

Turnkey Linux – getting my app running on Tomcat the easy way

Deploying applications is not my favorite task. I don’t do it often and every time I’m not really sure what the bloody command was. So, here I go, taking down notes on what I need to do to get my application up and running from scratch.

First off, I decided to use a Turnkey Linux Tomcat on Apache appliance as it contains all I need and provides some nifty and easy to use tools. Not to forget, it’s the minimal configuration and configured to check for updates automatically to keep security risks to a minimum. But the main point for Turnkey Linux is the ability to deploy their appliances onto Amazons Web Services. Sweet, that will allow me to familiarize with just one installation and simply follow these steps on an EC2 instance. It even gives me the ability to backup and restore from local to cloud instances.

The specific steps involved to get it working locally:

  1. Download from http://www.turnkeylinux.org/tomcat-apache. In my case it came as version 11.1
  2. Unzip and run using VMWare Player or Workstation (or use one of the other formats)
  3. Follow the initial steps to set passwords for the different accounts used
  4. Call up the manager at https://[SERVERIP]/manager
  5. Deploy a WAR file (created for example by using “mvn package”) using the “WAR file to deploy” Form. A new application will be created from that WAR and deployed automatically
  6. The one and only tricky part was the mapping configuration. Since Apache is running in front of Tomcat, it will answer all incoming requests. To have it forward these requests to your application, the Apache-Tomcat-Connector has to know about that. Edit the configuration as follows:On your console (of the VM): nano /etc/tomcat6/mod_jk.confAdd the following to lines to the configuration:

    JkMount /yourapplication ajp13_worker
    JkMount /yourapplication/* ajp13_worker

    Note: Yourapplication is the path your application is listed under in the Tomcat Manager.

    Save and close the file with CTL-X and answering YES to the question whether to save the changes

  7. Restart apache with /etc/init.d/apache2 restart
  8. Voila, that’s it, you can find your application running now under https://[SERVERIP]/yourapplication

Apple Store update: my new MacBook is arriving in store

I really like that sight when calling up the Apple Store to buy a MacBook Pro as it says

We are busy updating the store for you and will be back shortly.

Why is that so great, another Fanboy waiting to get his toy? Nope, not in my case, but still were looking at getting at least one Mac to be able to build iOS software. Unlike other products you have to have MacOS to be able to to do so. Second point my beloved Dell XPS M1330 is now getting a little to slow for all my work, so instead of investing money into upgrading it I decided to get a new one. Boys and their toys. Was about time… :)

So, soon I’ll be able to compare current Mac and OS X to my previous experiences with Windows (from NT till 7), Ubuntu, Debian and earlier versions of OS X. Maybe it’s interesting enough to post here about experiences from a relative neutral point of view.

Anyway, I’m looking forward to having a new toy at hands soon. By the looks of it, just a couple of minutes away.