Friday, June 26, 2009

Android scripting for fun and profit

Okay the last part isn't exactly likely given that you can't directly package a script application as an APK. I doubt you'll be making much "profit" from it anytime soon. Regardless, ASE support is a great boon to hackers, and allows for a whole new level of customization of your phone.

I decided to organize and prune my book collection the other day, and remembered reading a brief tutorial on scanning barcodes using Python and the ASE. I thought this would be a great chance to learn a bit about ASE and Android in general. Here's the basic steps I took, hopefully they'll be of some use to someone.

Despite the instructions which recommend starting with the ASE install, I found it easier to download the python interpreter to the phone first. http://code.google.com/p/android-scripting/wiki/InstallingInterpreters

Then install the most recent version of ASE to your phone (either using the QRCode at http://code.google.com/p/android-scripting/ or downloading the APK straight to your phone).

The ASE site has a fair bit of sample code, so I took the barcode sample and made a few adjustments that will let me track my book collection easier.

The script is admittedly pretty simple: read the barcode, check to make sure we got an actual result, then send it on to the webserver. Make sure to fill in the proper server name before you use the code. For my purposes I was going to be scanning a bunch of books all at once, so I put it in an infinite loop so I didn't have to relaunch it for every book. To exit the loop simply hit the "back" button on your phone.

Now that we can scan the barcodes locally, we need to do something with them on the server side.

Above is a simple PHP script that will write the ISBNs to a text file. The most important part is parseUPC method, which converts a UPC barcode to an ISBN. If the barcode isn't formatted as an ISBN it's left in it's original format.

And that's basically it. Drop the PHP code on your web server, make sure the path and server name match in the python script, and you're off and scanning!

Add in some queries to Amazon ECS or Google Books, and you're halfway to your own personal version of LibraryThing.

Labels: , ,

Monday, May 18, 2009

Migrating from Sinatra::Test to Rack::Test

After seeing the release of Sinatra 0.9.2 mention (again) that Sinatra::Test would vanish by 1.0, I decided to get with it and move to Rack::Test. It was really pretty painless and my tests were forced to become a bit more explicit (still not convinced I really LIKE this). Since I found a few bits of fun along the way I thought I'd share.

I use Test::Spec so some of this may not apply, or may look a bit 'off' to you, but I think the point should be clear.


Anywhere you're using @response/response, or @request/request you need to change to last_response or last_request.

Anywhere you were checking a redirect URL, you now need to check for a full hostname not a relative path. The default hostname for requests is http://example.org.

Also be aware that that Rack::Test tracks cookies. I haven't looked heavily into what that will mean in practice, but if you were manually twiddling the cookies around, you need to look into the CookieJar and how it may affect what you're doing.

That's all that I ran into when moving my tests over. What did you find?

Labels: , ,

Wednesday, May 13, 2009

Pushing an entire Sinatra app into a Rackup file

The title pretty much says it all. This is a pretty pointless thought experiment inspired by this post by the guys at devver. They came up with a simple way to put both a sinatra application AND it's tests into a single file. Nothing TOO crazy, but definitely cool.

Not satisfied with simple and effective, I went to complete overkill. I put together a way to not only embed a sinatra app and it's tests into a single file, but for that file to be a rackup compliant file. Also, as a pointless but neat bonus, I forced the tests to run before successfully before the app can start. What's the point? I don't really know, but it seemed cool so I thought I'd share. And no, I'm not using this anywhere, I don't think.

Labels:

Saturday, January 31, 2009

Extracting subdomains in Sinatra

As part of my continuing education in Sinatra, I decided to learn how Sinatra handles subdomains. It turns out that neither Rack, nor Sinatra seem to have any native subdomain handling code. So I took a quick stab at it.



The easiest method I could come up with was simply to re-open the Rack::Request class and add a subdomain method. The method implementation is adapted from Rails, except with Rack style caching.

To test this you will need modify your hosts file with something like:

127.0.0.1 sample.test.dev


Then after running the above script, use the url http://sample.test.dev:4567. You should see just the word "sample" as the output.

I'm not sure that this is the best method, but seems to work for now.

Labels: ,

Tuesday, January 20, 2009

Multiple Sinatra .90 applications in one process

I recently started work on a new application based on the current Sinatra edge code. I'm not new to Ruby, but I'm definitely new to Sinatra. So I thought I'd start a small series of posts based on the things I find either interesting, or difficult. Today's post probably falls into both categories.

One of the things that drew me to Sinatra was the absolute simplicity in it's approach to web apps. One of the keys to this simplicity is leaning heavily on Rack. Rack provides a variety of what they call Middleware, small bits of code that insert themselves into the request/response process, handling things like caching, session management, or even JSON parsing.

Now that we're through with the background, let's hit my find of the day.

As I started working I found that I felt more comfortable with my application being defined inside of a class instead of just floating around in files (the default method of building a Sinatra app). It turns out this is one of the lesser documented parts of the new Sinatra .9 release. (In all fairness this seems to be due to the fact that a lot of the infrastructure that allows this to happen is either brand new, or heavily refactored in this release)

It turns out I could do a whole lot more, such as making each portion of my application it's own standalone Sinatra application, then using some Rack Middleware to mount each portion into it's own URL space. Why would I want to do this? I think it will make for a very extensible, and potentially scalable infrastructure. All of that aside, let's see HOW you do this!

For starters you need to define a class for your application to live in.



There's a simple set of classes that function as little application stubs. Notice that we require sinatra/base instead of just sinatra. This is one of the new things in the .9 release. By requiring just sinatra/base we get all of the sinatra goodness, without any of the intrusive top-level methods. Since we're going to encapsulate our application in our own classes, this is perfect for us! For these little applications to fully work we'll have to define some views, which I'll leave up to you (or you can check the full project files at GitHub at the end of this post).

Now here is where we define our Rack magic. We'll create a config.ru file, which uses what's called RackUp syntax to specify some options for Rack compliant servers to use to configure and start our application. For this example I'm using Thin, but you can use whatever you prefer!



Up until line 15 it's pretty standard boilerplate for getting any Sinatra application going, but after that is where things get interesting. Rack automatically includes the URLMap Middleware which gives us access to the 'map' method. This method takes two parameters, a URL prefix, and a block specifying that applications configuration. We could do any configuration we can do at the top level inside of that block, giving some pretty powerful per-application configuration.

And that's it! Start it with thin -p 4567 -R config.ru start, or the equivalent. You should be able to navigate to / and see your first index, and /blog and /blog/list respectively to see their content. Notice how our Blog class never uses the /blog URL prefix anywhere. This would let us pass the exact same class into a different application, at a different URL space, and the application wouldn't be any wiser for it.

Want to see the whole thing already setup? Check out the GitHub link below for the full sample application.
http://github.com/tannerburson/multi-sinatra-sample/tree/master

Labels: ,

Monday, November 24, 2008

on wearing mental blinders

Today I read something very annoying. I'd read of this "problem" before, but for some reason it's rampant stupidity never really struck a chord with me. I read a blog post/article/something involving a group of people trying to design a system to handle the transfer of your digital data in the event that you unexpectedly get hit by that bus everyone always talks about.

My first thought was that this was indeed a problem that would only grow in significance thanks to everyone being told to live in "the cloud". Upon further thought, this is one of the most pointless things anyone has ever wasted time on, and I should know, I've wasted a lot of time on pointless things. Sidebar: What level of irony does this post reach?

One of the key reasons this is incredibly pointless is that the problem has been faced, in varying forms, for hundreds of years. Just because the data is now in a computer, doesn't change the nature of the problem. To prove this, let's break down the process of what is actually being considered.
  1. There is data that is important to you, that you don't want others to read, so it is protected by some form of password
  2. You don't want to write down your important account information as it changes because that is too cumbersome
  3. You are now dead
  4. You now wish your private data to be shared with your loved ones (or something)
Replace data with "sharp rock good at killing things", and "password" with "which bush near the cave I hid it under" and you've got roughly the same issue.

I understand the problem that many people face today of thinking that somehow using the internet in the solution to a problem makes the solution inherently better, but in this case it's going way too far. The real problem here isn't a technical one, or a social one, it's simple human laziness. People are considering working on very complicated public key signing mechanisms, or notification/verification systems, or something else equally as complicated to solve the problem of people being too lazy to write down a couple of lines of data every few months. (A note to lazy readers like myself, the laziness in the process is in bold)

After great thought, I have come to what I believe to be the ultimate solution to this problem.

  • Write down important account information (usernames, account numbers, etc).
  • Write down a list of possible passwords, because we all know no one is changing their password to something totally unique for every service they use every 60 days.
  • Write down your most common challenge question/responses.
  • Place the above information in a safe, either at your house, or at a bank.
  • Leave a key somewhere safe, inform your loved ones that should you finally get hit by that bus that everyone always talks about, the safe has usernames and passwords they may find helpful.
You may now sleep easier knowing that your loved ones can access all of your excellent daily stock tip emails, and unsolicited offers for medication long after your untimely demise.

Now that I'm done ranting, what did I learn from this little thought exercise? The people discussing this topic aren't stupid, hell I'm sure they're smarter and more accomplished than I am. So why are they wasting time trying to fix a problem that can't really be fixed? They're either participating in a mental exercise of their own, or they put on their Mental Blinders.

Mental Blinders is what I call the "thing" that happens when we constrain ourselves to a particularly narrow solution set, often in the process ignoring simpler or more effective solutions.

The next time I find myself slogging through an increasingly complicated solution to a problem, I need to remind myself to stand back and see if I've put on my blinders.

Labels:

Thursday, October 16, 2008

it's all about the data, stupid.

On an average day I'll touch three different operating systems, and a pile of applications on each one, and I've come to an important realization for me as a programmer: operating systems, and most applications, suck. Tying your business to any one of them undermines your business from the start (but is often a necessary evil). Most applications don't respect your data, and actively work to make your life more difficult.

My day job involves working closely with our art department, who all use Macs, and their work products (primarily images). Because of this, it's convenient for me to also work on a Mac, to simplify the sharing of data as much as possible. This works out really nicely, until I need to edit a document that a manager created in MS Office 2007. I've got Office on my machine, but it won't edit that file, or it's missing some chart, or annotation, or what have you. The fact that it was created in Office is unimportant, what's important is the data in the file not the file itself. The file format is actively preventing me from getting to the data.

Applications may be irrelevant, but what isn't irrelevant is user data. The important choices when developing, or evaluating a new system aren't whether to use OSX or Windows or Linux. What's important is deciding what needs to be done, what the constraints are (cost, time, etc) and what to do with the data. Does the data need to be secured from all prying eyes? Displayed prominently on a website? Printed to paper and stored in a humidity controlled vault in an abandoned salt mine at an undisclosed location? All of the above depending on the phase of the moon?

The applications that I enjoy working with the most are the ones that understand that the way I need to see my data today may not be what I need tomorrow. When you design an application, be it on the web, or the desktop, think about the ways in which you show and share data with your users. Is it easy for them to change the format? Is it easy for them to get at the data in as raw a form as is applicable? If your answer to either of the above is 'no', then you're not respecting your users.

I don't want to have to use MS Office to read my data I've exported from your site. I don't want to be stuck with only a PDF. I want the option to get raw, unformatted text, or a formatted TeX doc (yes, yes, wishful thinking). I want the option to get a simple CSV file, or RSS/Atom feed. I want control of my data. I want the comfort of knowing I can get my data out of your system, for my own uses, with as few restrictions as possible. Because I don't expect you to know every possible way that I may want to use my data. But I don't want you restricting me from certain options based on limited export options.

So, what's the point? (Other than that I'm apparently trying to become the next Steve Yegge, yeeshk this is getting long winded) The point is that it doesn't matter what OS you use, it probably sucks. The applications you use are less important than the data you're creating in them. What's really important is having the data that's needed, in the format it's needed in, when it's needed and if you're preventing your users from doing this, you're ripe for replacement.

Labels: , , ,