Archive for category other

The iPad’s lack of Flash is a win-win situation

It seems iPad lack of Flash support is the debate of the moment. On one camp: “You can’t use the web without Flash”, on the other camp: “We don’t need no stinking Flash”. Although I do realize how a technology like Flash is sometimes needed, I’m more on the second camp. The less flash, the better.

I think iPad’s lack of Flash cause two things to happen:

  • Slow down the adoption of the iPad: surely someone will say “No Flash, No iPad“.
  • Speed up the adoption of HTML5: surely someone will consider using HTML5 to support the tablet.

Giving that the iPad is a closed device, probably the closedest non-phone computer consumers ever had access to and that HTML5 is good progress for the web, I consider both results of the iPad not having Flash positive. If I have to say anything about it, it’d be: please, stop trying to wake Steve Jobs up regarding this, you’ll ruin it.

, , , , ,

No Comments

I’m changing how I deal with spam

SpamMy email address, pupeno@pupeno.com, existed since around 1998 and was never obfuscated or protected in any way. Spam wasn’t such a huge problem in those days. Today my Spam folder has 3200 mails.

My spam filter is quite good, but I still like going through my spam in case some non-spam message was thrown in there. I’ve tried cleaning it weekly, daily, whenever I have free time and even inbox zero. It’s a hassle and I’m tired of it.

My new way to deal with spam is ignoring it. Since my spam is deleted automatically whenever it is more than 30 days old, filling up my inbox won’t be my problem; and whenever I someone tells me “I’ve sent you an email, haven’t you receive it?” I’ll be able to search for it and find it if it was on the spam folder unless it’s more than 30 days old. The cases I won’t be able to spot are mails that just went there. Life is tough.

, , , ,

1 Comment

What Joel Spolsky doesn’t understand about Linux

Once again I hear Joel Spolsky saying the same thing about Linux that I consider wrong. I’m sure I’m not the only one thinking that and I’m sure I’m not the only one writing about it, but I’m going to do it anyway. Joel’s position is that administrating Linux must be harder because something like 70% of the support calls they handle for FogBugz come from Linux while it is less than 30% of its market.

I’d agree that for someone that doesn’t know anything about administrating, getting a Windows server running is easier than getting a Linux server running. I think that being an expert in both environments is equally hard. The sysadmin problems are hard and there are no shortcuts.

But Joel Spolsky point is wrong in one regard: Linux is not an operating system. I’ve said it before and I’ve got many negative comments, mails, messages about it but very few actually got my point. This is an instance where Linux being taken for an operating system is causing someone pain and thus causing that someone to say something that is not completely right.

Joel makes FogBugz for Windows and Linux. Does that mean that I can run it on my Windows CE phone? It is a Windows, isn’t it? It says Windows in the name! It has a start button! Well, of course I can’t run FogBugz on it. They ship it for Windows the PC operating system. Does it work on ReactOS then? In a sense it is a Windows PC operating system; an alternative implementation of one with all the same interfaces and libraries. Well, of course not. It work only in the Windows PC operating system produced by Microsoft. For example, Windows 7 Starter Edition, yes, that one that allows you to run only three apps at the time. Well, no!

All those things I mentioned are in a sense Windowses as well, yet they are not supported and probably FogBugz doesn’t work on them. Yet, he says he supports Linux. Saying you support Linux is like saying that you supports computers that have RAM. It’s too broad. Trying to ship something that should run on almost everything won’t work. Be it Linux or anything else.

Linux is only a kernel. It’s a kernel used by many different operating systems. Some of them radically different (like Android or Chrome OS). If you are a company and make a product supporting it on Linux is crazy. What should Joel do then?

He should support operating systems, not kernels. What operating systems should Joel support? I don’t know, whatever is popular. I’d guess Red Hat’s RHEL, Ubuntu, Debian are probably popular operating systems. Once you start supporting operating systems instead of kernels the world looks differently. You don’t have to deliver a tar.gz (a compressed archive) that should compile and run everywhere. It’s insane to ship that and it’s insane to expect users to be able to install them. I administer various Debian and Ubuntu servers and workstations. Do you know how often I compile source code that I haven’t written myself? Never!

When you support an operating system like Ubuntu or Debian you can ship binaries. You build binaries in the form of debs or rpms. And you don’t build only one of them, you probably need one deb for Ubuntu, one deb for Debian and maybe even one deb for each version of Ubuntu if they vary enough. If you think this is a lot of work, then look at how much work is building a Windows installer that can work in any Windows from XP pre-SP1 without .Net to Windows 7. If it was trivial there wouldn’t be companies like InstallShield making money.

I’ve heard FogBugz comes with its own copy of MySQL and Apache. How Windowish! That’s crazy and meant to break. You don’t copy code, programs or libraries in the world of Linux-based operating systems. You set dependencies. You make a fogbugz.deb that depends on Apache and depends on MySQL and when fogbugz.deb is installed it will automatically install Apache and MySQL. It will install an Apache built, optimized and customized for that version of Apache that follows the Ubuntu guidelines for storing caches, config files, etc.

There’s more, since there are some pretty straighfoward guidelines where everything is installed, fogbugz.deb could work out of the box. In an Ubuntu box I can run

pupeno@wopr:$ aptitude install phpmyadmin

and it works out of the box. And phpMyAdmin depends on Apache and MySQL. Of course I could break those services so that nothing works, but most people don’t. What the phpmyadmin package does is drop files in certain locations where Apache is going to pick them up. Apache as many other programs don’t have just one configuration files but directories of them, so you can drop another configuration file and it’s picked up.

I am very certain that if FogBugz was packaged in this fashion, then it would not generate as many phone calls as it does now. It is packaged and distributed like software in 1995 for Linux-based operating systems, when almost everything was a mess and chaotic and there were very few people putting it in production.

To close this post let me tell you something else to convince you that Linux is not an operating system. You can write a program for Linux, but then it runs just fine in OpenBSD. And then you find out that it also runs just fine in Solaris. Well, but you wrote it on a Debian box, which is Linux. Did you know that there’s a Debian kFreeBSD (or something like that)? which is Debian running the FreeBSD kernel. Debian kFreeBSD is more similar to Debian than to FreeBSD. Nexenta is Ubuntu with the Solaris kernel, and it’s more similar to Ubuntu than to Solaris. Android is Linux, yet almost none of the software that runs on Debian, FreeBSD, Nexenta, Solaris runs on Android.

You see, Linux is just one component, and not even the biggest component, of an operating system. It is not the component users interact with when using a computer and it is not even the component programmers interact most of the time, when writing a program. It makes almost no sense to say “I make this program for Linux”.

, , , ,

2 Comments

Blood pressure and conclusion

In less than an hour, I quickly added blood pressure:

sano-020

It was really quick and dirty and it doesn’t have an in-line form, but it’s there and it’s working. I wasn’t able to achieve as much as I could but I think I got pretty far for one weekend. And it’s very clear that by the end, my speed was much greater than at the beginning, just compare the difference in time it took to implement each of the trackers.

I think the bigger time sinks were new stuff that I didn’t know. At the moment I was starting those I knew they were going to waste some time, but I was thinking in long term. Getting Formtastic to work as I wanted took some time, but now I’m able to create forms pretty fast. Starting to use nifty_generators took some time to actually find what they were and the syntax, but now every time I generate code I’m one step closer to finish it than before; and the look of the page is not hideous.

Another waste of time was figuring out how Rails and gems interacted. Some gems I add them and work, others don’t. Previously I included those gems manually in my code, but now I know how it works. During a couple of hours I wanted to make a much more advanced graph that would properly display 4 values or 400 values until I realized that it doesn’t have to be dynamic. When you want to see the last week, you pick last week, when you want to see last month, you pick that (well, a graph like Google’s Finance would be better, but ok, I’m humble). There’s no way to pick bigger range for the graph now, but the code is very ready for it.

I believe the experiment was a success: I can and I am very productive in Rails, same as with ASP.NET MVC or more.

,

1 Comment

First graph

It’s not perfect, but it’s a start:
sano-019

,

No Comments

My Profile page: a RESTful single resource using Formtastic

I’ve just implemented the My Profile tab for Sano:

sano-018

Can I write 500 words about? Well, I can try.

I like using RESTful routes. In case you don’t know what they are let me try to explain it quick, at least the relevant part. You normally have a set of route rules that would point /movies to the movie listing, /movies/new to a form to add a new movie, /movies/123 to see the movie 123. With RESTful routes in Rails all that is done automatic in a single line:

map.resources :movies

What you are doing is defining a resource. The resource has several actions that can be performed on them:

  • index (a.k.a.: listing)
  • new
  • edit
  • create
  • update
  • destroy

In Sano I have a weights resource that is a very fine example of it:

map.resources :weights

Running the rake routes command we can see all the routes it generate:

    weights GET    /weights(.:format)          {:action=>"index", :controller=>"weights"}
            POST   /weights(.:format)          {:action=>"create", :controller=>"weights"}
 new_weight GET    /weights/new(.:format)      {:action=>"new", :controller=>"weights"}
edit_weight GET    /weights/:id/edit(.:format) {:action=>"edit", :controller=>"weights"}
     weight GET    /weights/:id(.:format)      {:action=>"show", :controller=>"weights"}
            PUT    /weights/:id(.:format)      {:action=>"update", :controller=>"weights"}
            DELETE /weights/:id(.:format)      {:action=>"destroy", :controller=>"weights"}

You see the (.:format) in there? That means that every route is also accessible in alternative formats. For example: xml. Go and try it, add some weights and access http://sano.pupeno.com/weights.xml.

If you are curious, the code for that is this:

def index
  @weights = user.weights.all
 
  respond_to do |format|
    format.html
    format.xml  { render :xml => @weights }
  end
end

I now want everything to be a resource. How can “my profile” be a resource? Well, it’s not hard. It’s not a collection resource, it’s a single resource. There’s no list of profiles, no creation of new profiles or destruction of profiles. There’s only editing and updating of a single profile (which is actually your user).

It turns out that in Rails, that’s very easy to define:

map.resource :profile, :only => [:edit, :update]

Notice how it says “resource” instead of “resources” and it only allows certain actions. Rails is really quite flexible here, logging in is also a resource. It’s called session and you can create them, by logging in, or destroy them, by logging out (no editing). There’s also an extra action needed by OpenID. This is the route definition:

map.resource :session, :only => [:new, :create, :destroy], :member => { :finish_creating => :get }

The “member” part specifies that action to be only for items, not for the whole collection. If it was a collection resource, you could have extra listings. The same way you have index, you could have sorted_index.

The form in the my-profile-page is an example of what Formtastic is good at. This is the whole form:

<% semantic_form_for @user, :url => profile_url do |f| %>
  <% f.inputs do -%>
    <%= f.input :name %>
    <%= f.input :email %>
    <%= f.input :height, :hint => "meters" %>
    <%= f.input :gender, :as => :radio, :collection => [["Male", false], ["Female", true]] %>
    <%= f.input :birthday, :start_year => 1900, :end_year => Time.now.year %>
  <% end -%>
  <% f.buttons do -%>
   <%= f.commit_button :label => "Update profile" %>
  <% end -%>
<% end %>

Special thanks to Ryan Bates who covered the gender case in Railscasts episode 184 and Eifion Bedford of ASCIIcasts for making it easy to find. It surely would have take me some time to figure it out.

Can you please update your profile on Sano?

There you go 553 words!

, , , , ,

No Comments

Migrations that change the schema

Up until today I did everything with a lot of planning and I let my OCD use migrations in a way they were not intended: I would go back and fix old migrations and destroy the database and re-run them.

With Sano, as I went through as fast as I could, two things happened: I made mistakes in the schema and those mistakes are now deployed. Time to make migrations to fix them.

The original migration for the weight model was like this:

create_table :weights do |t|
  t.integer :user_id
  t.float :weight
  t.datetime :measured_at
  t.timestamps
  t.foreign_key :users
end

and then I created two destructive migrations:

change_column :weights, :measured_at, :date
rename_column :weights, :measured_at, :measured_on

and

add_index :weights, [:user_id, :measured_on], :unique =&gt; true

The first one converted the measured_at datetime column in a measured_on date column. It destroys data, but I believe there’s no way that one could fail.

The second one adds an index for uniqueness between measured_on and user_id. That means that users can have only one weight per day. That one doesn’t destruct any data but it has the potential to fail when run on the production server.

I was about to just give it a try and pray. It’s not like thousands of people are using Sano anyway. Well, I’ve just realized I didn’t have to pray. I could test the migration first. It was trivial:

  1. Open local phpMyAdmin and go to sano_devel
  2. Delete all tables in local sano_devel
  3. Open remote phpMyAdmin and go to sano (the production database)
  4. Export everything
  5. Run SQL in sano_devel with the exported text
  6. Try migrations

Well, they worked:

pupeno@wopr:$ $ rake db:migrate
(in /Users/pupeno/Projects/sano)
== ChangeMeasuredAtTypeAndName: migrating ====================================
-- change_column(:weights, :measured_at, :date)
-> 0.3609s
-- rename_column(:weights, :measured_at, :measured_on)
-> 0.1440s
== ChangeMeasuredAtTypeAndName: migrated (0.5056s) ===========================

== AddUniquennessIndexToWeightUserIdMeasuredOn: migrating ====================
-- add_index(:weights, [:user_id, :measured_on], {:unique=>true})
-> 0.1171s
== AddUniquennessIndexToWeightUserIdMeasuredOn: migrated (0.1173s) ===========

and while I’m at it, let’s test the down-migrations, so I can be sure that if something goes wrong on production, I can rollback:

pupeno@wopr:$ $ rake db:migrate VERSION=20091121135320
(in /Users/pupeno/Projects/sano)
== AddUniquennessIndexToWeightUserIdMeasuredOn: reverting ====================
-- remove_index(:weights, {:column=>[:user_id, :measured_on]})
-> 0.2745s
== AddUniquennessIndexToWeightUserIdMeasuredOn: reverted (0.2748s) ===========

== ChangeMeasuredAtTypeAndName: reverting ====================================
-- rename_column(:weights, :measured_on, :measured_at)
-> 0.1381s
-- change_column(:weights, :measured_at, :datetime)
-> 0.1335s
== ChangeMeasuredAtTypeAndName: reverted (0.2719s) ===========================

Note: actually, there was a typo in the down-migrations; I’ve fixed it and everything was all right.

The new version with the improved forms is now deployed (the one I showed in the previous post), you can now play with it: sano.pupeno.com.

, , , ,

No Comments

I love to code

I said I was done for the day more than 6 hours ago, but I love to code, I couldn’t stop. I wanted to implement a small feature: make the creation of new weights simpler for the common case and I did it:

sano-017

Note: That change is not yet deployed. I don’t play with servers while I’m half-asleep.

This took awfully long. The problem was that in the process I’ve found a bug in Formtastic, which made me realize I was running version 0.2.4 when the latest version was 0.9.2. That is because I was using justinfrench-formtastic:

config.gem 'formtastic', :lib => 'justinfrench-formtastic'

when I should have been using formtastic from gemcutter:

config.gem 'formtastic'

When I moved to 0.9.2 I’ve found two bugs on it, one was temporary solved and then reverted (and I fixed it by reverting the revertion) and the other is still there but I’m not 100% confident my solution is the appropriate one. At any rate I forked Formtastic in GitHub, fixed the bugs and made a merge request.

Conclusion: Git is great, GitHub is great, Formtastic is great, open source is great, Rails is great and yes, I am great ;)

, , , , ,

No Comments

Sano is open for business

I really wish I was able to get farther in one day, but I think it’s good enough that I went from idea to deployed app. On retrospective I wasted too much time figuring out formtastic. I don’t regret doing it because it was in my TODO list and in the long run it should make me more productive, but in the short run maybe I should have used the good old forms.

The other two big waste of times was CSS and tables and an issue with the Ruby OpenID gem. Both problems I encountered before and both times I gave up trying to solve them and moved on. I should have moved on again this time; but instead I figured them out.

The application is at http://sano.pupeno.com. Please don’t break it ;) Remember to log in and if you add data I’ll be grateful as it’ll make my migrations more realistic:

sano-016

I’m done for today.

, , , ,

No Comments

Super Exception Notifier

I like knowing when something goes wrong with my web apps, so I’m using Super Exception Notifier to get by email a report should any exception be raised in the app. If you go to Super Exception Notifier’s site you’ll see some instructions on how to add it to your project. This is how I do it.

Add the gem requirement in environment.rb:

config.gem 'super_exception_notifier', :version => '~> 2.0.0', :lib => 'exception_notifier'

Then be sure to have gemcutter in your gem sources:

pupeno@wopr:$ gem sources
*** CURRENT SOURCES ***

http://gemcutter.org
http://gems.rubyforge.org/
http://gems.github.com

If you don’t have it, you can add it this way:

pupeno@wopr:$ gem install gemcutter
pupeno@wopr:$ gem tumble

To install the gem, in your Rails project run:

pupeno@wopr:$ sudo rake gems:install

Create a file in config/initializers, I’ve called it exception_notifier.rb and inside I’ve set up the only really needed value for the notifications, the email address:

# Notification configuration
ExceptionNotifier.configure_exception_notifier do |config|
  config[:exception_recipients] = %w(pupeno@pupeno.com)
end

The last task is to make your application controller noisy by adding one line to it (the second one of course):

class ApplicationController < ActionController::Base
  include ExceptionNotifiable
  #...
end

You also need to be sure that you have ActionMailer properly configured, otherwise no mail is going to get through.

, , , ,

No Comments