"Homeland"

I was casually reading this Consumerist story and the use of the word "homeland" was kind of disturbing.  Even after more than 10 years, "homeland" still has a disturbing, big-brothery, propaganda-y, dystopian-ness to it.  Maybe "even after" is the wrong phrase, maybe it is exactly due to how it has been used over the last decade that gives it the same, if not more of an, overtone of unpleasantness, of wrongness, even sinisterness.

The usage was a quote from some unnamed "senior U.S. official" in this form:

While we are not aware of a specific threat to the homeland at this time, this routine communication is an important part of our commitment to making sure we meet that priority...

I just can't read that sentence without "homeland" pricking me deep in my psyche and bothering me deeply.

The whole thing bothers me.  Someone "official" telling everyone that they should be afraid not for any particular reason ("not aware of a specific threat"), but just because they want you to remember you're supposed to be afraid or something.

I dislike what we did to our country after September 11, 2001.  Terrorists killed a few thousand people; tragic--but an absolute pittance compared to car accidents (33,000 per year), heart disease (600,000 per year), and suicide (38,000 per year).  Terrorists killed a few thousand people in 2001, but we've been terrorizing ourselves for over 12 years.  We pushed an agenda of fear and allowed that agenda to either erode or destroy founding ideals of liberty and freedom.

I'm hoping we can begin to change that; but quotes like this one seem to have no purpose other than to continue pushing an agenda of fear-mongering.

I'm hoping we can begin to change that because of articles like this one from Ars Technica discussing that one of the authors of the PATRIOT Act is trying to undo some of the damage he's done and reign in some of these activities (not that he admits this is his fault in any way, of course).  It is just really sad that these people were not listening 10 years ago when others were warning how dangerous these laws would be.

Criteria Aggregator: Dynamic Criteria Queries in Grails

A few years ago I wrote about dynamically building criteria queries in Grails.  Well the use case we had disappeared and I didn't do much of anything with dynamic criteria queries until recently.  As I've mentioned in an update on that post, you probably just want to use DetachedCriteria now instead of taking this route, but DetachedCriteria don't support the same range of functionality that normal criteria queries do.

I ran into this limitation when I needed to use subqueries which DetachedCriteria do not support but standard criteria queries do.  After spending hours trying to work around the limitation and still use DetachedCriteria I decided to give up and create a nice way of using the dynamic criteria queries based on my past experience, the comments on that post, and my greatly enlarged knowledge of Grails.

So I created a nice clean class called "CriteriaAggregator":

package org.example.package
import grails.orm.HibernateCriteriaBuilder

// Aggregate query criteria for a Domain Class
// Example: def qa = new CriteriaAggregator(MyDomainClass)
//    qa.addCriteria { idEq(12345L) }
//     def results = qa.get()
public class CriteriaAggregator {
    private Class forClass
    private List<Closure> criteriaClosures

    // forClass should be a Grails DomainClass; but since Grails injects rather than inherits I can't specify the type better than "Class"
    public CriteriaAggregator(Class forClass) {this.forClass = forClass; criteriaClosures = new ArrayList<Closure>(10)}

    // criteriaClosure is the exact same type of closure you'd pass to DomainClass.withCriteria(criteriaClosure)
    public void addCriteria(Closure criteriaClosure) {criteriaClosures << criteriaClosure}

    public long count() {return runQuery('get') {projections {rowCount()}}}
    public def get(Closure additionalCriteria=null) {return runQuery('get', additionalCriteria)} // Query must return only a single row
    public def list(Closure additionalCriteria=null) {return runQuery('list', additionalCriteria)}

    private def runQuery(String method, Closure additionalCriteria=null) {
        HibernateCriteriaBuilder criteriaBuilder = forClass.createCriteria()
        def critClosures = criteriaClosures // Bizarre that criteriaClosures won't evaluate properly inside the "$method" closure, but it won't so this works around that issue
        criteriaBuilder."$method" {
            critClosures.each{closure -> closure.delegate = criteriaBuilder; closure()}
            if (additionalCriteria) {additionalCriteria.delegate = criteriaBuilder; additionalCriteria()}
        }
    }
}

This wraps and defers the creation of the normal criteria builder allowing you to build up the criteria dynamically and execute it when desired.

Suppose you had a Customer domain class with many Orders.  Orders have a date, paymentMethod, and totalPrice.

def customerQueryAggregator = new CriteriaAggregator(Customer)
customerQueryAggregator.addCriteria {
  orders {
    def now = new Date()
    between('date', now-7, now)
  }
}
customerQueryAggregator.addCriteria {
  orders {
    eq('paymentMethod', 'cash')
  }
}

def numCustomersInPastWeekPayingCash = customerQueryAggregator.count()
println numCustomersInPastWeekPayingCash

def highRollersInPastWeek = customerQueryAggregator.list{
  orders {
    gt('totalPrice', 50000)
  }
}
println highRollersInPastWeek

This is obviously a trivial example where you don't need to use the aggregator.  But it's just to illustrate the usage.  I'm using it to build up a query based on a set of options provided by a caller as part of a reusable and flexible service.

Using this I can handle subqueries like so:

import grails.gorm.DetachedCriteria as GrailsDetachedCriteria
import org.hibernate.criterion.Subqueries
import grails.orm.HibernateCriteriaBuilder

def customerQueryAggregator = new CriteriaAggregator(Customer)

GrailsDetachedCriteria largeOrderSubquery = new GrailsDetachedCriteria(Order).build {
  eqProperty 'order.id', 'this.id'
  gt 'totalPrice', 50000
  projections {property 'id'}
}

customerQueryAggregator.addCriteria {
  add(Subqueries.exists(HibernateCriteriaBuilder.getHibernateDetachedCriteria(largeOrderSubquery)))
}

Update April 4, 2014:

I ended up having some trouble with the proper definition of table aliases (it always wanted to call "order" "this" even though we already had a "this") while handling the GrailsDetachedCriteria subqueries.  To work around it I ended up dropping back to straight Hibernate, but it still works with the CriteriaAggregator.

import org.hibernate.criterion.DetachedCriteria as HibernateDetachedCriteria
import org.hibernate.criterion.Restrictions
import org.hibernate.criterion.Disjunction
import org.hibernate.criterion.Subqueries
import org.hibernate.criterion.Projections

def customerQueryAggregator = new CriteriaAggregator(Customer)

HibnerateDetachedCriteria largeOrderSubquery = new HibernateDetachedCriteria.forClass(Order.class, 'order')
largeOrderSubquery.add(Restrictions.eqProperty('order.id', 'this.id'))
largeOrderSubquery.add(Restrictions.gt('order.totalPrice', 50000))
largeOrderSubquery.setProjection(Projections.property('order.id'))

customerQueryAggregator.addCriteria {
  add(Subqueries.exists(largeOrderSubquery))
}

My Custom Charging Box

I decided to be crafty.  I had an idea for a decent looking box from which to charge gadgets.  I finally found a box I liked (a cigar box, as it turns out).  And then we went off to Jo-Ann Fabrics for other materials (Josh, if you're reading this, I was reminiscing of our Jo-Ann Fabrics trips back in the day).

I was looking in the upholstery fabric section, but not finding anything smaller than about 10 feet long.  I did see some options in the remnants of some suede that would probably have worked.  But then Jess stumbled upon some swatches of leather on the other side of the store which were perfectly sized.  They also had a strap of leather that gave me an additional idea to give it an old strapped trunk sort of look.

You'll quickly see that I'm not exactly a fine craftsman, but it came out well enough and I only threated to give up 3 or 4 times (me and reality don't get along so well; my job has "undo").  It was kind of fun and now it cleans up the mess of charging cords that normally are strewn about the house and kitchen counters.

Nifty Fifty - Pentax smc DA 50mm f/1.8

I finally got a "nifty fifty" lens, the Pentax smc DA 50mm f/1.8.  I've been busy getting this new home for the blog up and running and haven't had much of a chance to play with it yet.  But I did, of course, take a couple test shots when it arrived earlier this week.

Prime 50mm lenses are often referred to as "nifty fifties."  The name apparently stems from the Canon EF  50mm f/1.8 II which earned the title for being affordable and still having good optical qualities.  The nickname has leaked out to pretty much any manufacturer's 50mm lens that maintains good optics while hitting an affordable price point.  My understanding is that since the 50mm lens has long been a staple of the industry pretty much every manufacturer has a "nifty fifty."

The Pentax smc DA 50mm f/1.8 definitely earns this title.  I picked it up new for just under $200 (I'd been waiting months for a good price) and the image quality is fantastic with great depth of field control.

Here's the first test shot I took:

Pentax 50mm f/2.0 test shot
50mm prime, f/2.0, ISO 2500, 1/60s

The sharpness is quite good and the depth of field is very slim (at f/2.0 and a short object-distance).  I look forward to playing with this lens more.

The Blog's New Home

This is our blog's new home. If you're reading this you're in the right place.

The blog is now hosted on my own server running WordPress. I have some cleanup to do with the archives that came over from Blogger, and I'll get through that over the next little while. Meanwhile, everything should just work for you.

I'm planning on creating a mini-blog that you'll be able to get to via the "PhotoBlog" menu entry at the top-right of the page. If you go there now you'll just get a "Nothing found" message of some kind.