2015-10-20

Some custom iTunes shortcuts: show current track and toggle volume adjustment

I couldn't find any "native" iTunes way to do this and I thought this could help other people.

My goals:

  1. See some info on what's currently playing (singer name, song name...) from (mostly) anywhere.
  2. Change the volume adjustment of the current song from (mostly) anywhere. 
So I created some AppleScripts and Services using Automator and assigned keyboard shortcuts to them. 

The solutions:
  1.  Create a pop-up notification when keyboard shortcut is pressed.
    • AppleScript:

      to openInfo(name, num, artist, album, played, volume)
      display notification "" & artist & " (" & album & ")" subtitle "" & num & ": " & name & "(vol " & volume & ")"

      end openInfo

      tell application "iTunes"
      set sel to current track

      my openInfo(name of sel, track number of sel, artist of sel, album of sel, track count of sel, volume adjustment of sel)

      end tell
    • Service receives "no input" in "any application", run AppleScript:

      on run {input, parameters}
      set sAlias to "/Users/fcarasso/Apps/ShowCurrentItunesTrack.scpt"

      run script sAlias

      end run
    • My shortcut: command-F13
  2.  Toggle volume adjustment then show current and previous adjustment as a pop-up notification when keyboard shortcut is pressed.
    • AppleScript:

      tell
       
      application "iTunes"
      set toggles to {-50, -25, 0, 100, -50}

      set adj to volume adjustment of current track

      set newAdj to "not done"

      repeat with i from 1 to count toggles

      set low to toggles's item i

      set high to toggles's item (i + 1)

      if adj is low or (adj ≥ low and adj < highthen

      set (volume adjustment of current trackto (toggles's item (i + 1))

      set newAdj to volume adjustment of current track

      exit repeat

      end if

      end repeat

      display notification "New volume adjustement: " & newAdj & " (from " & adj & ")"

      end tell
    • Service receives "no input" in "any application", run AppleScript:

      on run {input, parameters}
      set scAlias to "/Users/fcarasso/Apps/ToggleVolumeAdjustment.scpt"

      run script scAlias

      return input

      end run
       
    • My shortcut: command-F14
The only time this seems to fail is when the focus is on Finder. Not ideal, but I can live with that.

2015-09-21

Hibernate's Criteria API and its history of strange JOIN behaviors, or how LAZY may actually become CRAZY

TL;DR version: Hibernate's Criteria API is deprecated. It has some quirks. Deal with it or migrate your code! (You should probably migrate sometime soon by the way). More specifically, if you require JOINs between multiple layers of collections, be sure to either use INNER_JOIN (vs. LEFT_OUTER_JOIN) or have a useless restriction on your collection (such as Restrictions.isNotEmpty( collection )), lest you find yourself with a strangely initialized collection.

Long version:

Once upon a time, a big figure from Hibernate declared that people would never be able to EAGER-initialize a collection when they add a criteria on it. That is, when finding an object, if your algorithm requires a certain condition on a collection within that object, that collection would have to be LAZY-initialized.

Well, that would be OK if it wasn't for a certain curious behavior.

Basically, specifying query restrictions on sub-criterias of collections could result on an object with a filtered collection.

Whoosh! Ok, let me try again. Let there be Cat:

Entity Cat has:
- Name (String)
- Mate (ManyToMany(let's simplify...) OneToOne Cat)
- Parents (ManyToMany Set<Cat>)
- Kittens (ManyToMany Set<Cat>)
- LikesFood (ManyToMany Set<Food>)

Now let there be some cats:

Myrthe ----- John   Gretta ----- Charles
         |                   |
     ----------          -----------
    |          |        |           |
   Sam        Phil --- Kitty     Mr. Furbal

And some types of food:

  • Royal Canin
  • Iams
  • Bubba Gump

Now it just happens that Phil, Kitty and Mr. Furbal all like Royal Canin, while Sam, Phil and Kitty like Iams and Sam, Kitty and Mr. Furbal like Bubba Gump.

Our fictious Pet Shop seems to have customers that will choose cats based on which food their siblings like, so we come up with something like this to feed to our CatDAO (pun intended):

session.createCriteria( Cat.class ).createCriteria( "Kittens", JoinType.LEFT_OUTER_JOIN ).createCriteria( "LikesFood", JoinType.LEFT_OUTER_JOIN ).add( Restrictions.eq( "foodName", foodName ) );

That is, if a customer comes in and says "Hoy! I want the entire litter where any one kitten likes Royal Canin", we could now find that out by running that query and looking at the Kittens collection of the resulting cats.

It's probably an horrible example, but the point is: it won't work. And it won't work because both Myrthe and John will only have Phil in their "Kittens" collection. Sam got filtered out because it doesn't like Royal Canin.

This curious behavior happens because the Criteria API seems to conclude that the LEFT_OUTER_JOIN on the collection allows it to be initialized with the query results... UNLESS there's a restriction on that JOIN.

Now there are two types of people. People that expects objects returned by Hibernate to be 100% coherent to what's in the database (me included), and people wanting to have a feature to filter the contents of such objects (search and you'll find them).

The bad news is that LEFT_OUTER_JOIN will do the latter. The good news is that it's still possible to have the former behavior by working around this "feature":

  • Use INNER_JOIN wherever possible instead of LEFT_OUTER_JOIN if joining on a collection.
  • If LEFT_OUTER_JOIN must be used on collection, be sure to have a (dummy) restriction on the collection, otherwise it may get loaded with inconsistent elements. A restriction blocks the collection from being initialized with the results of the sub query. The collection will then be loaded as expected when used by the code (lazy initialization).
    • Example of "dummy" restriction: Restrictions.isNotEmpty( collection ). Rationale: if you're left-joining on it to get to a child element and filter on it, the matched collection can't be empty anyway.

The behavior is so crazy that if instead of a Set you have a List and depending on what you're joining, you can even find DUPLICATES in your collection, which is REALLY not coherent against the database.

If you want an official statement, or more details on the issue, check here.

2015-05-26

Somebody committed a conflicted file to git. Oh noes!!!

Now I got a file full of diff "hints" that no patch or git mergetool will understand.

Option 1: have the person fix it on their side.

Option 2 (for the crazy like me): use REGEX!!!

On an editor supporting REGEX find/replace:
  • Look for (match): <<<<<<< .*[\n]([\s\S]*?)=======[\n]([\s\S]*?)>>>>>>> .*[\n]
  • To keep the first, replace by: $1
  • To keep the second, replace by: $2
Other alternatives are welcome!

2015-04-28

Nested predicates in XPath and the need (or lack of) to reference an outer context from within

Key message: At least in my case, the depth cross-reference ended up not being needed at all. I could come up with a logic where the nested tests cascaded perfectly without any cross-reference from within to the outer context.

Given the following XML:

<Root>
  <Order ID="o1">
    <Purchase ID="p1"/>
    <Invoice ID="i1">
      <Item ID="it1" PurchaseID="p1"/>
      <Transaction ID="t1" ItemID="it1"/>
    </Invoice>
    <Invoice ID="i2">
      <Transaction ID="t2" ParentID="t1"/>
    </Invoice>
  </Order>
  <Order ID="o2">
    <Purchase ID="p2"/>
    <Invoice ID="i3">
      <Item ID="it2" PurchaseID="p2"/>
      <Transaction ID="t3" ItemID="it2"/>
    </Invoice>
  </Order>
</Root>

I needed to select all Transaction nodes from the Order with a Purchase whose ID was "p1".

In this example it should return two nodes, but note that the second node is farther away from the Purchase ID than the first one. In fact, it requires passing through the first node to get to it.

The XPath that solves this riddle is:

1: /Root/Order/Invoice/Transaction[
2:     @ItemID=../Item/[@PurchaseID="p1"]/@ID
3:   or
4:     @ParentID=../../Invoice/Transaction[
5:         @ItemID=../Item[@PurchaseID="p1"]/@ID
6:     ]/@ID
7: ]

In line 5, the XPath gets into a predicate depth of 3 levels, each one selecting the appropriate node from within to out.

2015-04-17

Multiple Directive Resource Contention

Key message: this error can be caused by the declaration code (app.directive("customDir", ...)) being executed more than once, even if it exists only once in your webpage, due to tab/wizard libraries.

Now the long story.

I've been dealing with integrating new features done with AngularJS into a "legacy" application that makes heavy use of jQuery and some tab/wizard library.

The latest riddle I faced was an interesting AngularJS error:

Error: $compile:multidir
Multiple Directive Resource Contention

Multiple directives [customDir, customDir] asking for new/isolated scope on: 

Basically, a custom directive I created that was supposed to be reusable was giving me trouble apparently due to its isolated scope's nature.

Note that the two directives in the error are actually the same. The HTML linked to the error looked like this:

<customdir someparam=""></customdir>

The directive declaration looked like this:

// app is the object returned by angular.module()
app.directive("customDir", function() {
    return {
        restrict: "E",
        scope: { someParam: "=" },
        template: "
...
"
    };
});


What didn't make sense in my case was the set of conditions I was in:

  • Angular code used as a plug-in module: the declarations, templates and HTML needed to use the code were self-contained, meaning that all it needed to be used was hosted in its own single file. To use it, I included the file in the exact place in the webpage where I wanted to use it.
  • AngularJS initialized manually (angular.bootstrap)
  • Isolated scope, but...
  • "Element"-only directive, not attributes within one same DOM element.
  • Only one declaration in the webpage.
  • Dynamic template created from string: the template couldn't be referenced multiple times since it was created from string.
So none of the similar situations I found on the web that ended up with this error matched my case.

During my investigation, following AngularJS's initialization I found out that my code declaration was ending up twice in Angular's execution queue.

Further investigation revealed that the original JS code was being duplicated by the tab/wizard library and ended up being executed twice when the "tab" got active. This happened because the declaration code appeared within the scope of the tab where the Angular logic would be needed.

At this stage I saw two options. Either (1) split the code in two so that the declarations could be included somewhere outside the reign of the tab/wizard region or (2) find a way to forbid the declaration to run more than once.

I didn't want to go for (1) because it would break the self-contained nature of the module. So now I needed to find the most proper way to go with (2).

I looked for an AngularJS API that would allow me to check if directives had already been declared, but couldn't find anything.

So I ended up with a little hack. I used a custom "window" attribute that indicated if my declaration code had already been executed or not. If the attribute was not present, the code would be executed. If it was present, the code would not execute.

The end result looked like this:

if (!window.customDirDeclared) {
   // app is the object returned by angular.module()
   app.directive("customDir", function() { ... });
   window.customDirDeclared = true;
}