Posts Tagged ruby

JRuby Rake and Ivy

Here’s a neat way of using ivy with jruby, rake & ant.

task :ivy_retrieve do
  ant.taskdef :resource => "org/apache/ivy/ant/antlib.xml" do
    classpath :location => "ivy/ivy-2.2.0.jar"
  end
  ant.configure :file => "ivy/ivysettings.xml"
  ant.resolve :file => "ivy/ivy.xml"
  ant.retrieve :pattern => "lib/[conf]/[type]/[artifact]-[revision].[ext]", :sync => "true"
  puts
end

Still using ant, still angle bracket free (except for ivy, sigh). Read my previous post if you want to know more about jruby, rake and ant.

Tags: , , , , , ,

JRuby Rake Vs Ant

For the longest time I’ve been writing Java build files in XML and I’ve always felt a little dirty. Not too long ago I was re-introduced to rake by a colleague (thanks Fabio) and how nicely it integrates with ant. *Headslap*

This means that you can now turn this:

<project name="spring-conversations" default="build" basedir=".">

    <property name="src.dir" location="src/main" />
    <property name="test.dir" location="src/test" />

    <property name="build.dir" location="build" />
    <property name="dist.dir" location="${build.dir}/dist" />
    <property name="report.dir" location="${build.dir}/report" />
    <property name="compile.dir" location="${build.dir}/compile" />

    <path id="classpath">
        <fileset dir="${compile.dir}" />
        <fileset dir="lib/buildtime" />
        <fileset dir="lib/runtime" />
    </path>

    <target name="build"
            depends="clean,run-tests,make-war"
            description="Main target to create WAR file." />

    <target name="run-tests"
            depends="make-jars,run-unit-tests,check-tests"
            description="Run all tests" />

    <target name="clean">
        <delete dir="${build.dir}" />
    </target>

    <macrodef name="make-jar">
        <attribute name="srcdir" />
        <attribute name="jarfile" />
        <sequential>
            <mkdir dir="${compile.dir}/classes" />
            <javac srcdir="@{srcdir}" destdir="${compile.dir}/classes" classpathref="classpath" debug="yes"
                   includeantruntime="no" />
            <jar jarfile="${compile.dir}/@{jarfile}" basedir="${compile.dir}/classes" />
            <delete dir="${compile.dir}/classes" />
        </sequential>
    </macrodef>

    <target name="make-jars">
        <make-jar srcdir="${src.dir}/java" jarfile="${ant.project.name}.jar" />
        <make-jar srcdir="${test.dir}/java" jarfile="${ant.project.name}-tests.jar" />
    </target>

    <target name="run-unit-tests">
        <mkdir dir="${report.dir}" />
        <junit fork="yes" forkmode="once" printsummary="yes" haltonfailure="no" failureproperty="tests.failed">
            <classpath location="${src.dir}/resources" />
            <classpath refid="classpath" />
            <formatter type="xml" />
            <batchtest todir="${report.dir}">
                <fileset dir="${test.dir}/java">
                    <include name="**/*Test.java" />
                </fileset>
            </batchtest>
        </junit>
    </target>

    <target name="check-tests" if="tests.failed">
        <junitreport todir="${report.dir}">
            <fileset dir="${report.dir}" includes="TEST-*.xml" />
            <report todir="${report.dir}/html" />
        </junitreport>
        <fail message="One or more tests failed. Please check the logs for more info." />
    </target>

    <target name="make-war" depends="make-jars">
        <mkdir dir="${dist.dir}" />
        <war warfile="${dist.dir}/${ant.project.name}.war" webxml="src/main/webapp/WEB-INF/web.xml">
            <fileset dir="src/main/webapp" excludes="**/web.xml" />
            <lib dir="${compile.dir}" excludes="*-tests.jar" />
            <classes dir="${src.dir}/resources" />
            <lib dir="lib/runtime" />
        </war>
    </target>

    <target name="run-jetty" depends="clean,make-jars" description="Run application in Jetty.">
        <java classname="example.jetty.WebServer" fork="true" failonerror="true">
            <classpath location="${src.dir}/resources" />
            <classpath refid="classpath" />
        </java>
    </target>

</project>

Into this:

require 'ant'

PROJECT_NAME = 'spring-conversations'

MAIN_SRC_DIR = 'src/main/java'
TEST_SRC_DIR = 'src/test/java'

RUNTIME_LIB_DIR = 'lib/runtime'
BUILDTIME_LIB_DIR = 'lib/buildtime'

BUILD_DIR = 'build'
DIST_DIR = "#{BUILD_DIR}/dist"
COMPILE_DIR = "#{BUILD_DIR}/compile"
CLASSES_DIR = "#{COMPILE_DIR}/classes"
TEST_REPORT_DIR = "#{BUILD_DIR}/report"

task :default => [:clean, :run_tests, :make_war]

task :clean do
  ant.delete :dir => BUILD_DIR
  puts
end

task :setup do
  ant.path :id => 'classpath' do
    fileset :dir => COMPILE_DIR
    fileset :dir => RUNTIME_LIB_DIR
    fileset :dir => BUILDTIME_LIB_DIR
  end
end

task :make_jars => :setup do
  make_jar MAIN_SRC_DIR, "#{PROJECT_NAME}.jar"
  make_jar TEST_SRC_DIR, "#{PROJECT_NAME}-tests.jar"
end

def make_jar(source_folder, jar_file_name)
  ant.mkdir :dir => CLASSES_DIR
  ant.javac :srcdir => source_folder, :destdir => CLASSES_DIR, :classpathref => 'classpath',
            :source => "1.6", :target => "1.6", :debug => "yes", :includeantruntime => "no"
  ant.jar :jarfile => "#{COMPILE_DIR}/#{jar_file_name}", :basedir => CLASSES_DIR
  ant.delete :dir => CLASSES_DIR
  puts
end

task :run_tests => :make_jars do
  ant.mkdir :dir => TEST_REPORT_DIR
  ant.junit :fork => "yes", :forkmode => "once", :printsummary => "yes",
            :haltonfailure => "no", :failureproperty => "tests.failed" do
    classpath :refid => 'classpath'
    formatter :type => "xml"
    batchtest :todir => TEST_REPORT_DIR do
      fileset :dir => TEST_SRC_DIR, :includes => '**/*Test.java'
    end
  end
  if ant.project.getProperty("tests.failed")
    ant.junitreport :todir => TEST_REPORT_DIR do
      fileset :dir => TEST_REPORT_DIR, :includes => "TEST-*.xml"
      report :todir => "#{TEST_REPORT_DIR}/html"
    end
    ant.fail :message => "One or more tests failed. Please check the test report for more info."
  end
  puts
end

task :make_war => :make_jars do
  ant.mkdir :dir => DIST_DIR
  ant.war :warfile => "#{DIST_DIR}/#{PROJECT_NAME}.war", :webxml => "src/main/webapp/WEB-INF/web.xml" do
    fileset :dir => "src/main/webapp", :excludes => "**/web.xml"
    lib :dir => COMPILE_DIR, :excludes => "*-tests.jar"
    classes :dir => "src/main/resources"
    lib :dir => RUNTIME_LIB_DIR
  end
  puts
end

task :run_jetty => [:clean, :make_jars] do
  ant.java :classname => "example.jetty.WebServer", :fork => 'yes', :failonerror => 'yes' do
    classpath :location => "src/main/resources"
    classpath :refid => "classpath"
  end
end

Damn, this feels like code.

Tags: , , , , ,

Post-Redirect-Get in Rails

For a while now I’ve been flying the flag for using a post-redirect-get design pattern when writing web applications. In my opinion the current crop of web frameworks still make it very easy to do the “bad” thing since to do PRG properly you need to think what kind of an interaction you want with users and not cop out saying its technically very difficult in <insert framework here>. If you resort to ActiveX controls, popups without navigation bars and/or weird javascript hacks to stop users from clicking refresh or back buttons then perhaps you should have written a better web application.

Whenever I play with Rails, or for that matter any other web framework, I get stuck on trying to find a problem to solve (or a set of requirements). Fortunately the Agile Development with Rails book from the Pragmatic Programmers has a nice little bookstore application that I can develop iteratively. I’ve put my latest adaptation of their depot application to use post-redirect-get (even works with ActiveResource scaffolds), UUIDs as ActiveRecord primary keys, HAML, SASS and RSpec on GitHub. Feedback is always welcome.

Tags: , , , , , , , ,