• Shortcuts : 'n' next unread feed - 'p' previous unread feed • Styles : 1 2

» Publishers, Monetize your RSS feeds with FeedShow:  More infos  (Show/Hide Ads)


Date: Tuesday, 28 Jan 2014 06:27

Next week will be 6 months since I moved to the west coast—the San Francisco Bay area. It’s been eye-opening to see the differences, big and small. It’s got me thinking about the path of Web development since I started almost 20 years ago.

The apps at my current job are single-page interface apps where Node and Rails provide the API to the service layer and Backbone, ExtJS, and Marionette provide the user interface. This technology stack is very typical these days, right? Typical to the point that if you don’t speak at least half of those, or equivalents like Ember and Django, most of the companies around here look at you funny.

As a thought experiment: imagine trying to explain that tech stack to a Web developer circa 1995. Or, I’ll make it a bit easier, how about 2000?

Changing Architecture

If you had come to me in 1995 and tried to convince me that there would be a need to differentiate between a Web server (Apache, nginx) and an Application server (Passenger, Espresso), I probably would have laughed at you and accused you of overcomplicating things. Even as late as 2005, I probably would have said “that’s what load balancing and reverse proxy servers are for”. Those crazy Java propellerheads with their servlet containers were just overengineering things as usual, right?

Developing a front-end app? What in the world is that? JavaScript was cute and all, but there was no way you’d build entire apps in it. Data modeling in JS instead of plain old JS objects? Why would you do that when that one page would just be navigated away from when the user clicked anything?

You might have been able to convince me that MVC was the way to go for Web apps. In 1995 I was writing apps in C/C++ as Apache modules and ISAPI DLLs. It sucked eggs, and I would have been ecstatic to take up anything to make it less of a pain.

Changing Attitudes

Had you shown me a Rails app I would have laughed at you. Convention over configuration? Just put this class file here, name it in this specific way, and include this specifically-named instance method, and poof you’ve got an app? No real Web developer would ever give up that much control. That’s just a toy, right? For kids?

DevOps? Developer Operations? You mean network admins, right? They’re the guys who think I should just put a bunch of files on a network share, or Lotus Notes.

If you asked each of the Web developers on you team if they could telnet to port 80 and type out a legitimate HTTP GET request and get a working response, how many would be able to? What about a POST request? No cheating with curl or wget—you have to do it by hand. But is that even a valuable entry-level skill anymore, what with Firebug, Web Inspector, Charles, Privoxy, etc?

And how long do you think it will be before there are more teams with devs who only know front-end or only back-end than there are who know both?

Changing Priorities

In 1995 (and 2000) the most important attribute you could have as a Web developer, as I asserted while interviewing countless applicants, was an extremely wide breadth of knowledge. You needed to know everything about every aspect of the Web. If you didn’t know it you needed to be able to figure it out, or know where to go for help to figure it out. I didn’t have a need for someone who only knew CSS, or only knew JavaScript, or only knew a server-side language, or only knew this one particular framework. I said over and over again that Web development was different than any other kind of development because we hadn’t stratified to the extent that all those jobs were different people.

In case the writing on the wall isn’t obvious: that stratification has already happened.

You might be working for a company where you’re the one Web person, but if so you’re a dinosaur. An artifact. An endangered species. You just haven’t accepted it yet.

Fifteen or twenty years ago, we needed generalists because the Web was still being invented and you had to be able to cover all the bases. The specialists were those idiots writing IE-only VBScript apps that would only work in that one version of that one browser on that one operating system. But if we hired a generalist we could train them up on the particular combination of technologies we could afford, cultivating them into specialists over years of experience.

It’s probably news to many of the East-coasters, but this has completely flipped on the West coast. The companies out here don’t care about generalists. Generalists can’t help them with this one specific tech stack that has already been chosen. Why train a generalist, when you can get a specialist who is good right now? When you replace your tech stack you can replace your obsolete specialist with a shiny new one—it’s not like there’s a shortage of Web people anymore.

Changing Rick

This year will be 20 years since I got my first email address and wrote my first HTML page. Next year will be 20 years since I got my first paid Web work.

I admit I am struggling with how I fit into today’s Web development world. I struggle against specialization. I’ve got too much perspective to think that tying myself to one stack could ever be sane. And who wants to settle on one thing, anyway?

At the same time I struggle for differentiation. I’m not a kid who’s fresh out of college but has been developing Rails apps for 4 years. I’ve been doing Rails for 6 months—I’m not going to know the perfect collection of Gems for this particular project, but I’m also not going to freak out when someone asks if we can switch out technologies in our stack. (And I wrote my first computer program before that kid was born, dammit!)

I see that I am on the cusp of becoming a dinosaur. It would be very easy for me to wave my hand at the changes in the industry, find some small shop where I can hole up, and do what I can to keep my job how it’s always been. It would be a complete lie, struggling against reality itself, but I could do it.

The scary part is that such a decision isn’t made all at once, but in degrees. I can say that I won’t do it and still find myself inching toward it year after year. Historical inertia is an addiction, just like any other: we want things to go back to how they were when they were good and easy. The best I can do is to keep running—learning and keeping up.

To misquote Kay:

Twenty years ago I knew exactly what I was doing on the Web.
Today I know how much the Web has changed.
Imagine what I’ll know tomorrow.

Author: "Rick Osborne" Tags: "Web"
Comments Send by mail Print  Save  Delicious 
Date: Wednesday, 16 Oct 2013 21:51

Dear Google & Gmail developers-

I don’t speak Chinese, Russian, and, quite frankly, pretty much anything other than English. Okay, my Spanish is haltingly passable in an emergency, and I can read Russian in a transliterate-and-pray kind of way, but that’s not the point.

Please, please, please, I would love a setting that provides the following logic: if an email comes in for me and it’s not [English] then it’s spam. And not just “I might want to double check so throw it in the Spam folder” spam, but “no really, just delete it immediately spam. Even better, use it for honeypotting: if an email starts to be relayed and it’s not in my chosen language(s), then slow the ACKs to longer and longer intervals. Throw in a random NACK. Whatever.

For reals though, I’m pretty confident that out of the entire list of written languages, most people speak just a few. Whitelisting languages makes far more sense than accepting everything.

Thanks and love,
-R

P.S. Yes, I know that most spammers don’t send proper language MIME headers. But your language detection is solid enough that it’s not a problem for you. I’d even take a brute force approach of “if there are more non-Latin characters than not, then it’s spam”.

Author: "Rick Osborne" Tags: "Random"
Comments Send by mail Print  Save  Delicious 
Date: Saturday, 06 Jul 2013 18:05

So I went through this morning and figured out how to get libgdx compiled for x86 and running in Android Studio. It’s a bit of a PITA to figure out, but pretty easy to do once you know how.

Run libgdx for x86 in Android Studio

This will enable you to use libgdx in the Android Emulator with an x86 image. Add in HAX and you should be able to get pretty decent performance.

I. Install the Android NDK

  1. Download the NDK for 64-bit OSX: http://developer.android.com/tools/sdk/ndk/
  2. Unzip the NDK to your Desktop, which should produce a folder named android-ndk-r8e. (You may move this someplace else, but if you do then you’ll need to change a few paths later.)
  3. Don’t touch anything else. It should work out of the box.

II. Compile libgdx shared objects

  1. Open Terminal.
  2. Clone the libgdx repo (do not just use the zip):

    cd ~/Desktop
    git clone git@github.com:libgdx/libgdx.git
    
  3. In the libgdx folder, create or edit gdx/jni/Application.mk to include:

    APP_ABI := armeabi armeabi-v7a x86
    
  4. Run ndk-build in that jni folder:

    cd ~/Desktop/libgdx/gdx/jni
    ~/Desktop/android-ndk-r8e/ndk-build
    

    You should see a whole bunch of Compile... lines for arm and x86.

  5. When the build finishes, check the libgdx/gdx/libs folder. You should now have folders for armabi, armabi-v7a, and x86. Each folder should have a libgdx.so file in it.

III. Compile libandroidgl20 shared objects

This is the same process as before.

  1. In Terminal, clone the gl2-android repository:

    cd ~/Desktop
    svn checkout http://gl2-android.googlecode.com/svn/trunk/gl2-android
    
  2. In the gl2-android folder, create or edit jni/Application.mk to include:

    APP_ABI := armeabi armeabi-v7a x86
    
  3. Run ndk-build in that jni folder:

    cd ~/Desktop/gl2-android/jni
    ~/Desktop/android-ndk-r8e/ndk-build
    
  4. When the build finishes, check the gl2-android/libs folder. You should now have folders for armabi, armabi-v7a, and x86. Each folder should have a libandroidgl20.so file in it.

IV. Package the shared objects

  1. On your Desktop, create a new folder named lib.
  2. From your libgdx/gdx/libs folder, move the three armabi, armabi-v7a, and x86 folders into into the new lib folder.
  3. From your gl2-android/libs folder, move each .so file into its matching folder in the lib folder you created above. You should have each of the libgdx.so files sitting next to its matching libandroidgl20.so file.
  4. Right-click the lib folder and choose “Compress”. This should produce a lib.zip file.
  5. Rename lib.zip to gdx-so.jar. Finder may ask to confirm that you want to change the extension from .zip to .jar, which is okay.

V. Add the JAR to your Android Studio project

  1. Drag the gdx-so.jar file into the libs folder inside your Android Studio project. (It should already have the android-support-v4.jar in it.) You will probably be asked to confirm the move, which is okay.
  2. If you haven’t already done so, do the same for the gdx.jar and gdx-backend-android.jar files you get from the official libgdx download.
  3. Go into your “Project Structure” window by using the File > Project Structure menu.
  4. In the Project Settings menu, select the Libraries option.
  5. Use the + button to add a new Java library. When asked to select files, navigate to your gdx-so.jar file, select it, and use OK to add it.
  6. Repeat the last step for your gdx.jar and gdx-backend-android.jar if you have not already done so.
  7. In the Project Settings menu, select the Modules option.
  8. For each project in the middle list: a. Select the project. b. Select the Dependencies tab on the right. c. Use the + button to add the Library items you just created for your JARs. d. Check the Export box for each.
  9. Use Apply or OK to apply your changes.
  10. Rebuild the project using the Build > Rebuild Project menu.

VI. Use libgdx in one of your activities

  1. Open an activity .java source file.
  2. Do something similar to:

    package com.example.mygame;
    import android.os.Bundle;
    import android.view.Menu;
    import com.badlogic.gdx.backends.android.AndroidApplication;
    import com.badlogic.gdx.backends.android.AndroidApplicationConfiguration;
    public class GameActivity extends AndroidApplication {
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            AndroidApplicationConfiguration cfg = new AndroidApplicationConfiguration();
            cfg.useGL20 = true;
            initialize(new MyGame(), cfg);
        }
    }
    
  3. Your game’s .java source should be something like:

    package com.example.mygame;
    import com.badlogic.gdx.ApplicationListener;
    import com.badlogic.gdx.Gdx;
    import com.badlogic.gdx.graphics.GL10;
    import com.badlogic.gdx.graphics.OrthographicCamera;
    import com.badlogic.gdx.graphics.Texture;
    import com.badlogic.gdx.graphics.Texture.TextureFilter;
    import com.badlogic.gdx.graphics.g2d.Sprite;
    import com.badlogic.gdx.graphics.g2d.SpriteBatch;
    import com.badlogic.gdx.graphics.g2d.TextureRegion;
    public class MyGame implements ApplicationListener {
        private OrthographicCamera camera;
        private SpriteBatch batch;
        private Texture texture;
        private Sprite sprite;
        @Override
        public void create() {
            float w = Gdx.graphics.getWidth();
            float h = Gdx.graphics.getHeight();
            camera = new OrthographicCamera(1, h/w);
            batch = new SpriteBatch();
            texture = new Texture(Gdx.files.internal("data/libgdx.png"));
            texture.setFilter(TextureFilter.Linear, TextureFilter.Linear);
            TextureRegion region = new TextureRegion(texture, 0, 0, 512, 275);
            sprite = new Sprite(region);
            sprite.setSize(0.9f, 0.9f * sprite.getHeight() / sprite.getWidth());
            sprite.setOrigin(sprite.getWidth()/2, sprite.getHeight()/2);
            sprite.setPosition(-sprite.getWidth()/2, -sprite.getHeight()/2);
        }
        @Override
        public void dispose() {
            batch.dispose();
            texture.dispose();
        }
        @Override
        public void render() {
            Gdx.gl.glClearColor(1, 1, 1, 1);
            Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
            batch.setProjectionMatrix(camera.combined);
            batch.begin();
            sprite.draw(batch);
            batch.end();
        }
        @Override
        public void resize(int width, int height) {
        }
        @Override
        public void pause() {
        }
        @Override
        public void resume() {
        }
    }
    
  4. If you need to, copy the data folder from the libgdx zip or repo, and the libgdx.png within it, to your src/main/assets/ folder in your project.

  5. Rebuild and run your app using the Run menu.
  6. Choose a virtual device using an intel (x86) image.
  7. When the activity opens you should see the libgdx test graphic on a white background.
Author: "Rick Osborne" Tags: "Programming, android, android studio, li..."
Comments Send by mail Print  Save  Delicious 
Date: Saturday, 16 Feb 2013 22:41
Because I know I’ll need this again in the future, here’s how to use an AJAX request with a JSON body (such as how an AngularJS $resource posts) from within PHP:
if (
	array_key_exists('CONTENT_TYPE', $_SERVER)
	&& array_key_exists('CONTENT_LENGTH', $_SERVER)
	&& (strncmp($_SERVER['CONTENT_TYPE'], 'application/json', 16) === 0)
	&& (intval($_SERVER['CONTENT_LENGTH']) < 10000)
   ) {
	try {
		$bodyData = json_decode(file_get_contents('php://input'), TRUE);
		if (is_array($bodyData)) {
			foreach ($bodyData as $bodyKey => $bodyValue) {
				$this->context->{$bodyKey} = $bodyValue;
			}
		}
	} catch (\Exception $e) {
		// no fallback here
	}
}
As you can see, there’s some paranoia in there to avoid reading posts with more than 10K of data. If your client isn’t using application/json as its MIME type then you may need to fiddle with it. But keep in mind that the client may be more verbose with the MIME type, such as application/json;charset: your-mom, so you can’t just do a simple string comparison. And, of course, you’ll want to swap out the loop for something meaningful for your situation.
Author: "Rick Osborne" Tags: "AJAX, Web"
Comments Send by mail Print  Save  Delicious 
Date: Sunday, 13 Jan 2013 21:03

In which I pause half-way through a run to record a video before I run out of daylight.

Link: Education: Run-n-Ramble 2013-01-12

Author: "Rick Osborne" Tags: "Random, School, Video, youtube"
Comments Send by mail Print  Save  Delicious 
Date: Friday, 04 Jan 2013 23:54

My wife’s 2-year-old EVO 4G phone took a dive off a high counter last week. The phone is usually protected by a dual-layered Otterbox, but wasn’t on this day. The glass shattered but held, leaving the phone usable but in dire need of attention.

Being someone who teaches courses in Mobile Development, I am well aware that screen replacement services do not come cheap. I haven’t seen anyone advertise it for less than $100. A quick Amazon search found that I could ship the parts for $30.

Which do you think my ego chose?

The parts arrived yesterday, so last night found us hunched over a desk with a busted phone and a pile of tools.

Videos on YouTube show how to perform the replacement, ranging from 5 minutes to 15 minutes. They are all damnable lies.

The process looks atraightforward, and is for the first few minutes: unscrew, wedge, pop, unclamp, slide, divide, conquer. Then comes the glass.

What you think of as the screen on a modern smartphone is actually two parts. The glass is the “digitizer” and contains the circuitry that relays your touches to the processor. It sits on top of the display, which is probably OLED, LED, or something similar, and is the showy, pretty part. The glass is what breaks from simple drops, usually leaving the display intact. The display breaks when there are point impacts, such as dropping something very heavy onto the screen, or the screen landing face-down onto a hard edge.

Problem being, those two pieces are snuggled right against each other and the display is very sensitive to damage. There is some adhesive keeping them together, and you need to pry the glass off without scratching, gouging, or distressing the display. My best advice: put a hair drier on medium and use it to soften the glue/tape.

This does not take under 15 minutes if you have never done it before. It took us about 90 minutes from start to finish, and we were holding our breaths for much if it, like we were performing triage.

If you’ve never done it before and are serious about being in the Mobile industry, you probably ought to do it once. Just like everyone in Web should build a server at least once, and everyone who owns a car should change the oil and a tire at least once.

After that, make sure you make enough money that you can pay someone else to do it.

Author: "Rick Osborne" Tags: "Web"
Comments Send by mail Print  Save  Delicious 
Date: Wednesday, 02 Jan 2013 00:11

How did I do last year?

  1. Swear less: Success. Though I still swear more than I am comfortable with, several people commented that I have gotten noticeably better.

  2. Run more: Draw. In 2012 I ran 378mi. Compared to 206mi in 2011 I did pretty good, but I didn’t meet the 455mi I ran in 2010 or the 425mi I ran in 2009. However, I feel good about the running I did in 2012. I didn’t overtrain. I didn’t injure myself (except for running in the FiveFingers at the end of the year). Overall, the year didn’t suck for running.

  3. Be present more: Success. This was a life-altering resolution. I feel, and peers notice, that I am now very good about giving people my complete and undivided attention. I am more cognizant about getting sucked into the lure of a news ticker on a restaurant TV, and I never just play with my phone when I am with someone else.

  4. Whine less: Unknown? Yeah, this isn’t the easiest-measured resolution. I feel like I don’t whine much, and compared to the end of the year last year I know my headspace is much better than it was. It feels like a win, but I do still sometimes feel like I’m whining too much.

  5. Plan for more school: Draw. I really like the look of the USF PhD in Curriculum and Instruction with an Emphasis in Measurement and Evaluation. But I haven’t done as much to make that happen as I should or could have done.

What’s up for 2013?

I’ve got some personal resolutions that aren’t here, but the public ones are:

  1. Run a full marathon. It’s not my distance, I know, but I ought to do it at least once before the body starts to arc too far into decline.

  2. No dessert. I indulged a little too often in 2012, and I need to break myself of the habit and turn dessert into a treat again.

  3. Save $10k. To be clear, that is on top of my existing (paltry) savings, not starting with them. This is going to be brutally rough, but I know it’s a habit I need to get into so I am setting the bar intentionally high.

    a. Get paid for sidework. This is a corollary to the previous item. It seems like I only take on sidework for which I never get paid. This year I need to figure out how to do this right.

    b. Get 1+ paid app in the iOS and Android stores. I’ve got the knowledge and the skills. I just need the focus.

  4. Two home-cooked meals per week. I dine out far too often. ‘Nuff said.

  5. Learn 2+ functional programming languages. This seems to be a persistent topic lately, and I need to keep up.

Nope, school is not on that list. It’s being demoted to “great, if it happens”. I’m not ecstatic about that, but it is what it is. And, yeah, I know it’s orthogonal to #3 up there.

Author: "Rick Osborne" Tags: "Life"
Comments Send by mail Print  Save  Delicious 
Date: Monday, 15 Oct 2012 01:42

Remember how I was trying to automate workout mixes back in 2010?

Yeah, I finally got around to it: workout-mixer on GitHub.

Given a sequence of BPM and duration specifications (“5min at 165bpm”) it will:

  1. Scan through your iTunes Library

  2. Find random songs of appropriate original BPM

  3. (Optionally matching accept/reject filters)

  4. Slice and tempo-adjust them to the correct output BPM and duration

  5. Combine and cross-fade them into one big M4A

  6. And add chapter markers for the songs

It doesn’t have the subtlety of, say, PodRunner but it does a decent job for being automated and taking ~10min per hour of generated music. And best of all: I don’t have to pay attention to the tracks before I am on my run. One of the downsides of making workout mixes manually is that by the time you’re done in GarageBand (or whatever) you’ve heard the songs so many times you’re already sick of the mix.

It takes about the same time to make a new mix and transfer it to the phone as it does for me to eat a quick breakfast and get dressed to run. No more shall I agonize over what to to put in my workout mix!

Author: "Rick Osborne" Tags: "Fitness, Programming"
Comments Send by mail Print  Save  Delicious 
Date: Sunday, 10 Jun 2012 20:18

Earlier this weekend I attended the Front-End Design Conference in St. Petersburg, FL. I would never consider myself a designer, especially not in an aesthetic sense, but it was still worthwhile trip. Here are some of the points I took away:

Jason VanLue (@jasonvanlue) talked about Three-Pipe Problems, the concept of taking your design away from trivial concerns and into much larger problem sets. He asserted that design is about solving problems—whether it is making something easier to use, or to understand, or to relate to, etc. He told a good story about Louis Pasteur. While other scientists at the time were concerned with electromagnetism and blowing up stuff in labs, Pasteur picked a bigger problem: how to make milk safe.

Jason also made an assertion that I am still thinking about. Developers, by which he means coders, pretty much have Open Source figured out. They build something, they put it up on GitHub, another coder comes along and makes it better, etc. But designers can be overly protective about their work and are far less commonly found in the Open Source community. And yet, how many projects would benefit from the cohesive, professional skills of a good designer?

Giovanni DiFeterici had arguably the highest-class presentation, with a steady stream of fine art throughout. His talk was on Conceptual Design, and I’m oversimplifying it but it boiled down to this: pick a single, solid concept and stick to it throughout your entire project. Even if the concept seems silly or weak, the attention to detail that comes with the total commitment to the concept adds a great deal to the overall experience.

He also pointed us at Mark Hassenzahl’s paper The interplay of beauty, goodness, and usability in interactive products. I haven’t read it through yet, but a quick skim tells me it’s probably well worth an hour with a tablet and a few glasses of tea.

Sarah Parmenter (@sazzy) talked about considerations when designing for iOS. It wasn’t anything we don’t already teach in our Mobile Development program, but it’s always nice to hear someone in the industry saying the same things we teach. But she did make one assertion that I really liked: stop designing with placeholder content. Stop using lorem ipsum. Make your client get you real content before you start and your entire production process will improve.

Sarah, and a few others, also talked about the shift from designing web and mobile solutions as a single, solid whole. Instead, designers need to start thinking about the individual pieces and parts: “here’s just what the nav will look like; here’s just the footer”. Of course, those parts must still come together cohesively to build the greater experience, but the focus should be the parts first. This shift is coming at us from multiple fronts, and responsive web and cross-platform mobile are just two of them.

There were many more speakers, and I have a ton of notes, but these were just the few I thought worth passing along. It was a very well-designed conference and I would absolutely go again next year.

Author: "Rick Osborne" Tags: "Web"
Comments Send by mail Print  Save  Delicious 
Date: Saturday, 26 May 2012 02:34

I’ve decided that 2012 will be the year in which I switch over to paying for all of the online services that I use instead of just mooching off of their freemium plans. I’m a big boy now and can afford it. I haven’t downloaded an illegal MP3 in I can’t remember how long, so I may as well take my game to the next level.

To date I now pay for:

Okay, so it’s not a huge list. But those are services that I use on a daily or weekly basis and they deserve my business.

Author: "Rick Osborne" Tags: "Books, Fitness, Life, Web"
Comments Send by mail Print  Save  Delicious 
Date: Sunday, 21 Aug 2011 23:27

I’m fighting with audiobooks again.

I’ve mostly got my process down, but there continue to be new levels of dumb to deal with. As it stands, the process goes something like:

  1. Buy an audiobook. Generally, this is on CD because I trust Audible about as much as I trust Ubisoft, and for the exact same reason.
  2. Rip the discs to high-quality MP3. On OSX I use Max, and on Windows I built a custom version of CDex to support disc numbers.
  3. Queue up all of the individual tracks and play the first few seconds of each, looking for chapter breaks. For the breaks, title the track something smart like 4: The Leaky Cauldron.
  4. Use the audiobookify Perl script I wrote to bundle tracks into chapters and convert from MP3 to M4B format so that I get nice chapter markers, cover art, etc. (And because pretty much every device plays M4B at this point.)

It’s a real PITA most of the time, but manageable. Sometimes, if I’m lucky, the CDDB titles for the tracks will be something like Chapter 4c so I don’t have to manually scan and tag.

But sometimes it’s just profoundly dumb.

Some audiobook producers (Blackstone, for one) think it’s amusing to disregard the actual spoken breaks in the book and instead use some fixed amount, such as tracks every 3 minutes. Some producers find it necessary to use all 99 available tracks on a CD. Some include intro and outro music on each disc. Some include a minute or two of the end of the previous disc as the start of the next disc (and then don’t put the overlap into its own track). Some audiobooks are only available as giant disc-length MP3 files with no breaks at all.

Why am I so hung up on chapter breaks? I spend most of my time listening to audiobooks while running. Trying to manage iPod volume and sport accoutrements while running involves a whole lot of fumbling. Occasionally, I’ll accidentally hit the track forward or back buttons. This is compounded by the length of audiobooks, which are often packaged into 5½ hour chunks, owing to a longstanding iPod firmware issue that does hinky things with longer audiobooks. Without proper breaks, scanning through a 5½ hour file is a nontrivial task.

When I am confronted with such lunacy, the easiest solution I’ve found is:

  1. Join together all of the track files into one giant file.
  2. Use mp3splt to find likely chapter breaks based on silence. But since I can’t under-split, as it would offend my delicate sensibilities to have 18 of 20 chapters marked, I have to over-split. And mp3splt is a bit of a hammer, so you have to really over-split. I find that a 20:1 ratio is what generally works: for every 1 chapter in the audiobook, split the book into 20 parts.
  3. Pick up on step 3 above.

I occasionally run into audiobooks that are just a bit too long to fit neatly into 5½ hour chunks. In such a case, SoX has functionality to let you change tempo without changing pitch, which works well on audiobooks. I’ve found that speeding up most audiobooks up to 20% is almost negligible for impacting comprehension. This is also useful for the narrators that are just a bit too slow for my liking.

I’ve been looking into speech recognition software to help me out. You would think that it would be trivial: obtain an electronic text of the book, give the audio and the text to some magical program that parses the speech and associates timecodes with the text, and then do a phenome search for words like chapter, book, and part.

Unfortunately, it looks like that magical software doesn’t exist yet. The closest thing might be Google Audio Indexing, the technology that powers Google Voice. But as near as I can tell, it’s not for public use yet. Something might be built atop CMU Sphinx, but it would take a significant effort.

Oh, and on a side note: M4B files will upload to Google Music (with some limitations on file size), but are automagically converted to MP3 format on their servers, thus removing any chapter breaks. Amazon’s Cloud Player does not yet support M4B files.

Side note the second: a 5-hour M4B file of decent quality weighs in at around 100MB. Even really long audiobooks, Neal Stephenson’s Anathem for example, don’t generally run more than 30-35 hours. You could fit even that entire book as a series of 7 M4B files on a single CD. Up that to a 1GB flash drive and get ridiculous audio quality instead of just decent. And again, pretty much every device supports M4B files these days. Or, you know, you could go on selling a 2lb stack of 28 CDs.

Author: "Rick Osborne" Tags: "Books"
Comments Send by mail Print  Save  Delicious 
Date: Wednesday, 10 Aug 2011 05:53

This is for my own future reference.

I had a problem earlier tonight where the slave DNS server my domains wouldn’t return anything but SERVFAIL for any domain. The server was up and running and the config files hadn’t changed, but it just wouldn’t give any useful answers.

The log (in /var/log/named/bind.log) only gave messages like:

general: warning: zone rickosborne.org/IN: expired

I tried using rndc on the slave to retransfer the zones, upped the debug trace logging, and even eventually stopped and restarted Bind. No change.

On a whim, I restarted the master Bind. This produced a more useful error:

security: error: client a.b.c.d#nnn: request has invalid signature: TSIG rickosborne: tsig
 verify failure (BADTIME)

That BADTIME message comes up when the clocks on the master and slave get more than 5 minutes out of sync. The master server’s clock was off by ~15 minutes.

The virtual server, running on Xen, ignored any attempts to set the clock. I tried plain old date and ntpdate, and both appeared to work, but were actually silently ignored. There’s a magical incantation to fix that:

echo 1 > /proc/sys/xen/independent_wallclock

So, yeah. That was a fun 2 hours of my life to track down and fix. I hope it helps someone else.

Author: "Rick Osborne" Tags: "Web"
Comments Send by mail Print  Save  Delicious 
Date: Friday, 25 Mar 2011 00:26

One of my long-standing assertions is that perfect OOP is amusing and all, but it tends to stumble a bit when it comes to web development. Interfaces are my go-to example for this. How often, in everyday real-world web development, are you suddenly going to need to swap out one Model class for another? “Well, it’s Tuesday, so we need to load the Oracle layer instead of the Access layer.”

To prove myself wrong, I built interfaces into this term’s Advanced Database Structures final exam practical. The course has changed a bit since I started teaching it almost 2 years ago (holy crap!). The core objectives are the same, but the underlying technology has been migrated to CouchDB and MongoDB.

From the exam instructions:

You will be refactoring an existing application to work against a MongoDB database and a CouchDB database. The codebase has been set up so that the application works as-is against a MySQL database and you can easily switch back and forth between each of the databases without having to modify anything other than the two Model classes you are responsible for.

The application I give them is a really simple proof-of-concept link shortening service called “fsa.il” and works the same way any other does: you input a long URL and your name, and you get a short URL back. You can also come back later and track click-through and referring URL statistics.

Along the top of the page are tabs for three persistence layer choices: MySQL, MongoDB, and CouchDB. The code they are given to start has a service that keeps track of your preferred data layer in your Session and handles all of the object creation for you. The students, who built the MySQL version of the app for last month’s final exam, are given a working MySQL class, an interface for the persistence layer, and two stubbed-out skeleton classes for the other two layers. Oh, and data dumps for quick import into all three layers.

That’s it. That’s the exam. The students just need to make the app work against the other two databases. The interface definition is only five functions, and ends up yielding ~150-200 lines of code they need to bang out over 3½ hours.

I think it’s a fun exam, but more importantly it’s a great piece for each student’s portfolio. How much of a rock star would you look like on an interview if you could show a piece of code that worked against both relational and non-relational persistence layers without a truckload of third-party code?

It’s interesting writing code that will work against all three layers at the same time. Naming conventions play a big part. It’s easy in SQL to write a query that aliases column names to something friendlier to code:

SELECT
    id AS linkID,
    long_url AS longURL,
    creator_name AS fullName,
    created_date AS createdDate
FROM link_links
WHERE (id = :linkId)

And while you could use Map functions in CouchDB and MongoDB to alias your document field names, that seems like a heckuva waste. Instead, you have to think about it a little bit harder beforehand. If you don’t, you end up doing translation of array key names at the Model layer, which again seems sub-optimal.

The seemingly-minor differences between the implementations of Map/Reduce in CouchDB and MongoDB also some into play. For example, when using the group() method in MongoDB you get an array back instead of a cursor, which means you can’t chain the sort() or limit() methods and have to do those in your Model.

Sorting in CouchDB can also be tricky. CouchDB assumes that you want to sort by your key and doesn’t give you any way to sort by value, making Top 10 lists a real pain to do. You’re back to sorting in your Model.

String manipulation functions are another friction point. SQL has a bevy of them, such as LEFT() and even the dreaded LIKE. No such luck in either MongoDB or CouchDB. You have to fake them in JavaScript. Compare:

WHERE (LEFT(:link, link_len) = link_start)

Versus:

function(obj, prev) {
    if("http://".substr(0, obj.linkLen) === obj.linkStart)
        prev.count++;
}

While that’s doable, and arguably potentially more powerful, it certainly isn’t what I would call elegant. Similarly, CouchDB’s startkey and endkey parameters are nice and speedy, but there are times when I’d kill for a keycontains parameter.

More to the point, I think the course is teaching great skills. Not only do students get to play with a variety of databases1, but they also get to see how easy it is to decouple layers of an application. It’s nice that they get to muck around in Map/Reduce and REST APIs and what not, but the architectural and problem-solving lessons will take them so much further.

Oh, and just to head anyone off at the pass, the exam is done in PHP because working with MongoDB in PHP is stupid easy, while in ColdFusion it’s not for the faint of heart2. The underlying Java library is usable, but requires a whole lot of syntactic icing. That is, a metric ton of javaCast calls and type paranoia. While that might be great for the students to encounter on a take-home assignment, it’s not something they need to be tripping over on a timed exam. And yes, there are some great MongoDB libraries available for ColdFusion, but the point of the exam is to have the students show that they can do it themselves.


  1. If I had another 3 hours on the exam I would totally make the students add in layers for Sqlite and XML.

  2. JSON-related quirks aside, using CouchDB requires an equivalent effort level from both PHP and ColdFusion. Yay REST API.

Author: "Rick Osborne" Tags: "Programming, SQL, Web, adb, couchdb, mon..."
Comments Send by mail Print  Save  Delicious 
PHP-FW/1   New window
Date: Tuesday, 08 Feb 2011 21:36

Short version: I ported FW/1 to PHP.

Long version:

I’ve been using Sean Corfield’s Framework/1 as a teaching tool for ~18 months. It’s been going well, as I find FW/1 is one of the few frameworks that doesn’t suck. It’s easy to set up, easy to configure, and it mostly stays out of your way.

This month, we switched things around a bit at the University. The server-side classes have been, well, rotated. Instead of one term of in-depth PHP followed by one term of in-depth ColdFusion, we now teach one term of entry-level PHP and ColdFusion, followed by one term of advanced PHP, ColdFusion, and other server-side languages and frameworks. Long story short, I needed a good MVC framework to use to show off the PHP side of things.

Unfortunately, it seems like most PHP MVC frameworks are big, lumbering beasts. (To be fair, so are most CF MVC frameworks.) CodeIgniter, for example, is a couple hundred files.1 It may be an awesome framework, but I can’t very well spend more class time installing and configuring the thing than actually building something with it.

I also needed to sharpen my PHP skills, as I haven’t done any serious PHP work in years. So, as an exercise, I started porting FW/1 to PHP. I thought I would eventually hit a wall, but I didn’t. It turns out that FW/1 is simple enough that even my rusty PHP skills could make it work.

The code has now undergone enough revisions, and I’m using it in a couple of internal projects, such that I feel it can be released. I’ve set up a GitHub repo here:

https://github.com/rickosborne/php-fw1

There are a few minor changes, mostly owing to nitpicking language differences, and a few big ones, such as the lack of subsystem code.2 But it works, and I think it works surprisingly well. YMMV, of course.

-R


  1. An install of Cake is almost 1000 files. WTF? Seriously?

  2. There’s nothing bad about the subsystem code in the original version, I just didn’t need it.

Author: "Rick Osborne" Tags: "Web, fw/1, php"
Comments Send by mail Print  Save  Delicious 
Date: Sunday, 06 Feb 2011 23:16

This podcast is part of an assignment for my MS schoolwork, but I decided to share it on my main blog.

It appears you have a browser that doesn’t grok the HTML5 <audio> tag. You can download the MP3 directly: 2011-02-06 Teaching Anecdotes.

Transcript:

Good day, all. Rick O here. In this podcast I want to share with you a story about my experiences as a teacher. For the second course in my Master’s program I have been given an assignment to build a portfolio website and do a podcast about my previous teaching experiences. I’ve already got the website, so here’s the podcast. But enough intro, Rick, get to the good stuff.

It turns out that I don’t remember too many of my really great teaching experiences. I know I’ve had successes, but they don’t stand out as much as the total failures. That probably says something crucial about my personality, but there you go. So instead of a story of success, I’m going to give stories of failure, but with an underlying theme that has led to my success as a teacher.

Anecdote the first: I was in middle school, in a pre-Algebra class. This was the first time in my life I was given homework that I actually had to take home and do, instead of just finishing it while the teacher was talking in class. I don’t remember what the exact topic was, but at one point I was frustrated by something and called my mom over to help me. We looked at it, and tried a few dead-ends. We both got increasingly frustrated, and voices were raised, until she finally exclaimed “I can’t help you with this!” and went off to do something else.

I didn’t understand it at the time, but I had hit the limits of what my mom remembered about math. My mom’s a smart gal, but math isn’t her strong point, and it had been a decade or more since she’d had a serious math class. This was my first exposure to the idea that maybe adults didn’t know everything about everything nor do they have perfect recall.

Fast forward a few years to high school. My then-girlfriend-now-wife was taking a statistics class. She, too, was not a mathlete like I was. She asked me for help one night, and try as I might I couldn’t get her to see the connections in the math that I could. I completely failed as a teacher, and neither of us has forgotten how horrific that failure was. Again, I didn’t see it at the time, but this was my first indicator that maybe everyone didn’t learn the same way.

It took a while until she was willing to ask for my help again, but a few years later she was in an Intro to Programming class that used Pascal as the language of choice. This should have been a red flag for me—I had been about 8 years old when I devoured my first book on Pascal, even though I wouldn’t have access to a Pascal compiler for another 5 years. Yeah, I was one of those kids. Anyway, the lesson went about as well as could be expected—epic fail—and we made a tacit agreement that I would never again try to help her with her homework. The lesson here? It’s harder to teach things you are passionate about, because your students may not have that passion and it’ll break your heart to see how much they don’t care.

And now back to the present. I’ve been teaching for about a year and a half now. I’ve taken these failures to heart, and used them to make myself a better teacher. I don’t expect my students to have perfect recall, nor to remember the minutiae that is accumulated over years of constant exposure to a subject. I try to present my course material in as many formats as I can generate — pages of long form text, bulleted slides, screencasts, interactive examples, real-world examples, et cetera — because everyone will absorb it differently. And I know that my students probably won’t love what I am teaching. They may even hate it. But that’s okay, because I am not what I teach. I’m just the messenger.

Attached Media: audio/mpeg (4 537 ko)
Author: "Rick Osborne" Tags: "School, mp3, ogg, podcast"
Comments Send by mail Print  Save  Delicious 
Date: Wednesday, 19 Jan 2011 03:55

For my own future reference, here are the steps I used to lock down an Amazon EC2 instance running the Railo image:

  1. SSH in as the ubuntu user. It should use your existing Amazon key pair. If it doesn’t, go read up on how EC2 works.

  2. Create a new user account and add the user to the admin group:

    sudo adduser fred
    sudo adduser fred admin
  3. Copy your SSH key over to the new user:

    sudo mkdir -p /home/fred/.ssh
    sudo cp ~/.ssh/authorized_keys /home/fred/.ssh/
    sudo chown -R fred:fred /home/fred/.ssh
    sudo chmod -R 0700 /home/fred/.ssh
  4. Add your new user to /etc/sudoers.

    export EDITOR=nano && sudo -E visudo
    fred ALL=(ALL) ALL
  5. Logout. SSH back in as your new user.

  6. Remove the Railo admin password and restart Railo:

    sudo perl -p -i.bak -e 's/password=".+?"/password=""/g' /opt/railo/lib/railo-server/context/railo-server.xml
    /etc/init.d/railo_ctl restart
  7. Reset your Railo Server password at http://./railo-context/admin/server.cfm. Set up a default password and reset all of the web context passwords.

  8. Change the Mango admin password by logging into http://./admin with admin/railo4all. Create a new user account with your own name while you are in there.

  9. Reset your mySQL password (current is railo4all) and make a new user:

    mysql -u root -p mysql
    update user set password=password('newpassword') where user = 'root';
    create user 'railo'@'127.0.0.1' identified by 'newpassword';
    grant select, insert, update, delete, alter, index, drop, create on mango.* to 'railo'@'127.0.0.1';
    flush privileges;
    \q

    If you aren’t using ORM, your grant statement can be reduced to the first 4 CRUD operations.

  10. Go into your Railo Web Admin at http://./railo-context/admin/web.cfm and change the Mango datasource to use user railo and the new password. Lock down permissions appropriately.

  11. Set up Git, and make a repo to cover your web root:

    sudo apt-get install -y git-core
    mkdir -p ~/git/www.git
    cd ~/git/www.git
    git init --bare
    git config core.bare false
    git config core.worktree /var/www
    git config receive.denycurrentbranch ignore
    sudo chown -R fred:fred /var/www
    cat > /var/www/.gitignore
        WEB-INF
        ~*
        *~
        settings.xml
    git add /var/www
    git commit -m 'Created repo'
    cat > hooks/post-update
        #!/bin/sh
        GIT_WORK_TREE=/var/www GIT_DIR=/home/fred/git/www.git git checkout -q -f
    chmod +x hooks/post-update
    

    You should now be able to clone this repo with something like fred@host:git/www.git. Committing and pushing to Git checks out the files to your web root, so you never need to transfer anything manually unless it’s too big to put in Git.

  12. Fix SES URLs in Apache+Tomcat. You’ll need to edit /etc/apache2/apache2.conf to include a /*.cfm/* rule.

Author: "Rick Osborne" Tags: "ColdFusion, Web"
Comments Send by mail Print  Save  Delicious 
Date: Friday, 26 Nov 2010 14:36

It’s put up or shut up time for me. I have a running coach scheduled for 2011. I’ve completed a half marathon ahead of my goal time. The next step really can’t be anything other than a full marathon.

But which one?

I come from a military family. We have people in the Army, Navy, and Air Force. I may not be one of them, but I grew up in that world and respect it. The choice is clear: the Marine Corps Marathon, 30 Oct 2011, will be my first full marathon.

I have 11 months to get my distance from 14mi to 26mi. I have a time goal of 4:30 for now, but I hope to be able to revise that down as low as 4:00 if my training next year goes well.

My other running goal for 2011 is the Florida 15K Challenge, which is the completion of three 15km runs: the Gasparilla Distance Classic in Tampa Bay on 26 Feb, my fourth Gate River Run in Jacksonville on 12 Mar, and the Miracle Miles here in Orlando on 24 Sep. For the Gasparilla run I can substitute in their half marathon and still meet the challenge.

I have an actual running calendar. That feels so strange.

Author: "Rick Osborne" Tags: "Fitness, Life"
Comments Send by mail Print  Save  Delicious 
Date: Tuesday, 09 Nov 2010 20:13

You know what would be really cool? ColdFusion for Android.

Wait. I’m not talking about ColdFusion the server-side framework. I’m talking about ColdFusion the language and development platform. I’m talking about the data-oriented language with primitives for building task-centric applications.

I’m talking about the ColdFusion language that already has a cross-compiler to Java source and JVM bytecode.

If you look at Android development, it’s not all that different compared to CF development with an MVC framework like Framework/1. Each Android task equates to a FW1 View, and has similar enter and leave Controller validation. Android apps are backed by SQLite storage, and generally use the same Model-style access to their database. Apps can start Service components that can function as asynchronous threads, or as persistent Application-level objects.

I’m not advocating porting the CF runtime to Android, nor client-server style development where the app UI is just a wrapper around WebKit1. I’m saying you take the CFML tags and CFScript language and cross-compile them to Java source and/or JVM bytecode. You write apps like this:

<cfwindow id="hello">
    <cfform action="#nextthing">
        <cfinput type="text" required="true" id="loginEmail" message="#localize.emailRequired#" label="#localize.emailLabel#">
        <cfinput type="submit" id="loginSubmit" label="#localize.next#">
    </cfform>
</cfwindow>

And poof, presto-magic, Eclipse compiles this into a native APK using native widgets and puts it on your Android. There are already a ton of CF tags for UI that are fairly render-agnostic. I’m not saying it would be a perfect fit, nor that every tag would be useful, but it’s a heckuva start.

Adobe has already made it pretty clear that they think Flash is the future for Android development. Some smart person who wants to make a ton of money should build a few proofs-of-concept and go to the smart guys at Railo with a business plan. If they turn you down, go through the presentation and replace Java/Android with .NET/WP7 and go talk to NewAtlanta. Once your pile of money is big enough, you shovel it into building an Objective C cross-compiler and start targeting iPhone.

ColdFusion is so money mobile and it doesn’t even know it!


Footnotes:

  1. You’ve already got Appcelerator Titanium, PhoneGap, and other competitors in that space.

Author: "Rick Osborne" Tags: "ColdFusion"
Comments Send by mail Print  Save  Delicious 
Date: Wednesday, 03 Nov 2010 20:34

Goal for the Daytona Beach Half Marathon: 2:15

Actual time: 2:10

I made my goal, and beat it by 5 minutes! This is my first (competition) run at this length, so to have been able to make my goal is a really big deal.

However, I’m told that maintaining this kind of heart rate for 2 hours isn’t all that good for you:

Go figure.

Author: "Rick Osborne" Tags: "Fitness, half marathon, running"
Comments Send by mail Print  Save  Delicious 
Date: Wednesday, 27 Oct 2010 04:40

This weekend I will be in the Daytona Beach Half Marathon. At 13.1 miles (21km) it will be the longest competitive running I’ve done1.

And I will be below average. Not even average—below average.

This is a difficult concept for me to get my head around. This thing, this running that I’ve been doing for 3½ years and 1000+ miles now, I haven’t gotten much better at since I started. I can run for longer distances2, but my pace has remained almost unchanged.

I can drag myself to the middle of the pack for shorter distances, such as the Gate River Run 15K, but in this race I will most likely finish about ¾ back. I’ll be in the slowest handful for my age+gender class—well to the right of the curve.

I don’t stomach mediocrity in myself lightly. It takes a good long while to come to terms with it. I have not made a conscious decision to be a below average runner, and yet here I am.

I do know that I have trained and trained and trained. I have put in a serious, undeniable effort. This Sunday, I will continue this effort and push to meet my time goal, even as all of my training says I won’t match it. I feel comfortable with what I have put into it.

But that doesn’t mean I have to be happy with where I am.

So I will continue. I will train, and I will train correctly.

-R


  1. For extremely liberal definitions of “compete”
  2. For a definition of “longer” that would be laughable in most other contexts
Author: "Rick Osborne" Tags: "Fitness, Life"
Comments Send by mail Print  Save  Delicious 
Next page
» You can also retrieve older items : Read
» © All content and copyrights belong to their respective authors.«
» © FeedShow - Online RSS Feeds Reader