iPhone Web Development

I haven’t found much info on developing for the iPhone, but this link provides some good info. Here are some extracts:

  • Safari User Agent for iphone:: Mozilla/5.0 (iPhone; U; CPU like Mac OS X; en) AppleWebKit/420+ (KHTML, like Gecko) Version/3.0 Mobile/1A538a Safari/419.3
  • No Java, No Flash
  • HTML5
  • 10MB max html size for web page
  • Javascript limited to 5 seconds run time
  • Javascript allocations limited to 10MB
  • 8 documents maximum loaded on the iPhone due to page view limitations
  • Quicktime used for audio and video
  • stylesheet device width:480px
  • there are no scroll bars or resize knobs. the iphone will automatically expand the content
  • new telephone links allows you to integrate phone calls directly from your webpage. remember this is only on safari.
  • built in google maps client for integrated mapping from your website
  • iPhone screen size 480×320
  • encode movie size 480×360
  • Links to movies on a web page will take users directly to video full screen playback

I didn’t have to say in line to buy an iPhone, the Apple Store in Aspen Groove served everyone that was waiting in line in about 30 minutes, I just happened to go eat next to the Apple Store with my wife and kids ;-). So I just picked one up on the way to the Restaurant, zero wait. The store is open to midnight but there is not too much activity. I guess only geeks like me (or Sol) will try to pick one the first day, even while being fully aware of the limitations or the price. And this despite that I told everyone that in no way I will get one. Note, we have to test our different web applications on the new platform. ATT seems a little overwhelmed and iTunes provides me with a nice message that it will take a little longer and I will be notified by email when I can activate the phone. Note that I just received a follow-up email that telling me to that the phone will be activated by July 1st noon.

From Flex, to Spry, to Rails

For one of the product we are currently developing for ourselves, we want to create a very slick interface. When we started, I was doing some Flex work at that time, so we (I) decided to start building the UI in Flex against a RESTFul Rails application. I used Cairngorm and I spent 10 days over several month developing the main functionality. I created a Flex ActiveResourceClient class (Actionscript) to access our RESTFul Rails server. I found a nice mechanism to display Rails errors in Flex similar to what scaffolding provides. I managed to add some Flex charting to show time series. We also nicely integrated testing with rake to allow running Flex unit tests from rake (rake test:flex). All was good. All was good, if it wasn’t for a little feeling that coding was slowing down. And it was getting a little slow to add new features. And I wasn’t so sure any more that we really needed flex for that project. So as the server was RESTFul, I thought why not use Adobe’s SPRY framework that consumes XML to drive HTML generation in Javascript. And our Rails application returns XML. So I devised a nice way to integrate Spry with Rails. I thought let’s give it a try. Four hours later I had a working demo, and 12 hours later a working application. The result was a cool application that behaves like Gmail. Erb was used to generate Spry templates and once the client was loaded (html+javascript) only calls to get new data from the server where issued. Then came RailsConf 2007 and my vacations (without any computer). Well, coming back to the project I wondered if I really needed Spry and if I could just go plain old Rails for the views. So on I went back and rewrite once more the UI just using standard Rails views. Eight hours later I had a clean, simple UI.

Lessons learned: keep it simple, don’t use technology for technology, don’t find a problem to your solution. Well, I got to enthusiastic about trying out new stuff, but now we are back on track.

So, 10 days for Flex, 12 hours for Rails+Spry, and 8 hours for plain Rails. Now this comparison is not fair as we spent quite some time on integrating Flex and Rails. But definitively when a Flex application becomes quite large, the compile time causes a drag to the development time. This is compared to plain Rails where you can just keep doing changes at which ever level you want (model, controller, view) and instantly see the change, resulting in a way faster development cycle. Don’t take me wrong I love Flex, but for this project Rails is a better fit.

#51 will_paginate

In edge rails (soon to be Rails 2.0), the built-in pagination has been moved into a plugin: classic_pagination. I recommend jumping over to the will_paginate plugin as shown in this episode.

#51 will_paginate

In edge rails (soon to be Rails 2.0), the built-in pagination has been moved into a plugin: classic_pagination. I recommend jumping over to the will_paginate plugin as shown in this episode.

Free-for-all: Tab Helper (Summary)

The first RailsWay free-for-all came off quite well. Many of you posted your favorite solutions to the problem of tab-based navigation, as posed by Nate Morse.

Jamis’ Take

Of all the solutions posted, my personal favorite was the pragmatic and simple CSS-based solution given by Mr. eel (Nate Morse came to the same solution independently):

I take a completely different approach. I ID the body of the page with the name of the current controller. Then I use a descendent CSS selector to highlight the current tab based on the body id and an id given to each link. I don�t bother with replacing the current tab link with a span. If the user wants to click that link again� then it�s the same as refreshing. Totally up to them.

With html like:

1
2
3
4
5
6
<body id="users">
  <ul>
    <li><a href="/users" id="usersNav">Users</a></li>
    <li><a href="/comments" id="commentsNav">Comments</a></li>
    <li><a href="/posts" id="postsNav">Posts</a></li>
  </ul>

I would use CSS like this

1
2
3
4
5
6
#users #usersNav,
#comments #commentsNav,
#posts #postsNav {
  background:red;
  font-weight:bold;
}

What a great approach. Although I would make the choice of the body ID explicit (rather than depending on the controller name), it is otherwise really nice. It shrugs off the whole issue of “should the current tab be a link” by saying it just doesn’t matter—every tab is always a link. Such pragmatism gets right to the heart of the Rails Way: implement just what matters, and nothing more.

Koz’s Take

A number of solutions relied on tightly coupling the controller and tabs. While this may seem like a time-saver at first, I believe that it’s unlikely to remain useful as your application grows. You’ll find yourself moving functionality into strange locations in order to make your tabs highlight correctly.

The problem is amplified with a restful application where your choice of controllers are dictated by the resources that you’re managing. You may have a list of comments in several different sections of your application, but not want to highlight the ‘comment’ tab whenever you display them.

Personally, I prefer the really simple approach of a before filter and a navigation partial.

1
2
3
def set_current_tab
  @current_tab = :people
end

Thanks, everyone for your submissions!

#50 Contributing to Rails

The best way to contribute to the Rails project is to submit a patch. This episode shows how to do exactly that. There’s also a surprise at the end that you don’t want to miss!

#50 Contributing to Rails

The best way to contribute to the Rails project is to submit a patch. This episode shows how to do exactly that. There’s also a surprise at the end that you don’t want to miss!

#49 Reading the API

The Rails API docs are very useful but can be difficult to read. This episode will give some tips on reading the docs and mention a few alternative sites for accessing the API. Update: sorry about the broken movie, it should work now.

#49 Reading the API

The Rails API docs are very useful but can be difficult to read. This episode will give some tips on reading the docs and mention a few alternative sites for accessing the API. Update: sorry about the broken movie, it should work now.

Code Digest #2

When you program for a living, you write lots of code. There is often some code that you are fond of. We started the Code Digest series to present such code written by the RHG developers. We encourage other teams and individual developers to share similar snippets in their blogs so we all can learn from each other and become better rails developers.

Mai Nguyen

Simple AJAX error messaging

When simple javascript validation is not enough and your product managers insist on ajax for error messaging, guess what. You have to implement ajax error messaging. One such case is ‘username availability’. This simple example displays an error message on blur of a the username field. It doesn’t hit the server unless the value of the field is well-formed (at least that will save you some network traffic …)

Controller would look something like:

class FooController < ::ApplicationController

def is_username_taken

unless
Continue reading "Code Digest #2"

Code Digest #2

When you program for a living, you write lots of code. There is often some code that you are fond of. We started the Code Digest series to present such code written by the RHG developers. We encourage other teams and individual developers to share similar snippets in their blogs so we all can learn from each other and become better rails developers.

Mai Nguyen

Simple AJAX error messaging

When simple javascript validation is not enough and your product managers insist on ajax for error messaging, guess what. You have to implement ajax error messaging. One such case is ‘username availability’. This simple example displays an error message on blur of a the username field. It doesn’t hit the server unless the value of the field is well-formed (at least that will save you *some* network traffic …)

Controller would look something like:

class FooController < ::ApplicationController

def is_username_taken

unless Person.find_by_username(params[:username])
render :nothing => true
return
end

message_html_id = params[:message_html_id]
render :update do |page|
page.replace_html message_html_id ,"Username is not available, please choose another."
# if you want error styling associated with the failed state
page << "document.getElementById('" + message_html_id + "').className = 'failed'"
end

end

end

View would look something like:

<dl>
<dt><label>Username: </label></dt>
<dd id="username_input">
<%= text_field :person, :username, :class => "input", :type => 'text', :id => 'person_username_input' %>
</dd>
<dd id="username_messaging"><%= @person.errors.on(:username)%></dd>
</dl>

Your javascript would look something like:

var UserNameUnique = Class.create();
UserNameUnique.prototype = {
initialize: function( field_id, message_id) {
this.message_id = message_id;
this.field = document.getElementById( field_id );
if (typeof this.field == 'undefined') {return;}
// Observe blur on field
Event.observe(this.field, 'blur', this.checkName.bindAsEventListener(this));
},
checkName: function() {
if( typeof this.field != 'undefined' )
{
var name = this.field.value;
// don't hit server unless username is well-formed
var re = /^[A-Za-z])[a-zA-Z0-9]{2,25}$/;
if( name.match( re ))
new Ajax.Request('/foo/is_username_taken', {asynchronous:true, parameters:'username='+name+'&message_html_id='+this.message_id});
}
}
};

new UserNameUnique('person_username_input', 'username_messaging');

You could also use Rails helper observe_field instead of the writing your own javascript, but there is more flexibility in writing your own javascript (such as the need to check well-formed values before hitting the server).

Mark Brooks

Creating the options list for select tags can be annoying, especially if there is a requirement that the current “option” be displayed as the default option. With javascript, it isn’t such a big deal, but one must take care of the degraded case as well.

In any event, the base object is an array of two-item hashes representing both the name of a video channel and the number of videos in that channel. By way of example:

@channelinfo = [
{"name"=>"Fitness","count"=>4},
{"name"=>"Diabetes", "count"=>1},
{"name"=>"Pregnancy", "count"=>11}
]

The option values are the name fields of each hash. The key is, we want the current option value to be the first one in the option list, and the rest to be in alpha order by name.

So let’s create an option builder from the bottom up.

First, we only need the channel names for the select tag. This gives us what we need:

options = @channelinfo.collect { |channel| channel['name'] }

That gives us a list of channel names. Using the list above, it would be [“Fitness”, “Diabetes”, “Pregnancy”].

However, we need to make sure that the current channel is first in the list. Let’s say that current channel is Diabetes. Since we already have that value in @current_channel, we can exclude it from our options list:

options = @channelinfo.collect do |channel|
channel['name']
end.reject do |channelname|
channelname == @current_channel
end

Now the resulting list will look like [“Fitness”, “Pregnancy”]. However, we still need the current channel to be at the front of the lift, so we add it back as the first element:

options = @channelinfo.collect do |channel|
channel['name']
end.reject do |channelname|
channelname == @current_channel
end.unshift(@current_channel)

Now our options list looks like [“Diabetes”, “Fitness”, “Pregnancy”].

Two points. First, we want to make sure that, while the current channel is at the head of the list, the rest of the list items are in alpha order, so we add a sort directive in the appropriate place. Also, it is probably a good idea to exclude any nils that might pop up in the collection on the original data object, since it comes from a service and it is possible, however unlikely, that a hash might get spit out without a ‘name’ property. The more rigorous code looks like this now:

options = @channelinfo.collect do |channel|
channel['name']
end.compact.reject do | channelname |
channelname == @current_channel
end.sort.unshift(@current_channel)

Now we can generate our options list using:

options.collect do |channelname|
"<option>" + channelname + "</option>"
end.join(",")

although if you want to, you can simply combine the whole thing as follows:

@channelinfo.collect do |channel|
channel['name']
end.compact.reject do |channelname|
channelname == @current_channel
end.sort.unshift(@current_channel).collect do |channelname|
"<option>" + channelname + "</option>"
end.join(",")

to get the same result, and eliminate the unnecessary options binding.

Todd Fisher

Here is an extension trying to execute a block multiple times before giving up. Handy for network operations and such.

module Kernel
def could_fail(retries = 3, &block)
tries = 0
begin
yield
rescue Exception
tries += 1
if tries < retries
retry
else
raise
end
end
end
end

Call it as result = could_fail { some_operation_that_might_fail_first_time }

Todd Fisher

When you are writing a script that needs to auto install gems, you are likely to run into a problem that it stops because there are multiple platform versions available (jruby, win32, etc) and the gem command expects you to pick one that matches your platform. This patch forces to use a specific platform so no user interaction is needed. Original idea from Warren updated to support rubygems 0.9.4.

module GemTasks
def setup

return if $gems_initialized
$gems_initialized = true
Gem.manage_gems

# see => http://svn.bountysource.com/fishplate/scripts/debian_install.pl
Gem::RemoteInstaller.class_eval do

alias_method :find_gem_to_install_without_ruby_only_platform, :find_gem_to_install

def find_gem_to_install( gem_name, version_requirement, caches = nil )
if caches # old version of rubygems used to pass a caches object
caches.each {|k,v| caches[k].each { |name,spect| caches[k].remove_spec(name) unless spec.platform == 'ruby' } }
find_gem_to_install_without_ruby_only_platform( gem_name, version_requirement, caches )
else
Gem::StreamUI.class_eval do

alias_method :choose_from_list_without_choosing_ruby_only, :choose_from_list
def choose_from_list( question, list )
result = nil
result_index = -1
list.each_with_index do |item,index|
if item.match(/\(ruby\)/)
result_index = index
result = item
break
end
end
return [result, result_index]
end

end

find_gem_to_install_without_ruby_only_platform( gem_name, version_requirement )

end
end

end

end
end

Val Aleksenko

Since class-level instance variable are not inherited by subclasses, you need to go some extra steps when writing a plugin using them. Depending on the amount of such variables, I have been either defining a method instead of class-level instance variables or forwarding them to subclasses.

Example #1. acts_as_readonlyable needs to provide a single class level instance variable. Defining a method instead.

def acts_as_readonlyable(*readonly_dbs)
define_readonly_model_method(readonly_models)
end

def define_readonly_model_method(readonly_models)
(class << self; self; end).class_eval do
define_method(:readonly_model) { readonly_models[rand(readonly_models.size)] }
end
end

Example #2. acts_as_secure uses a bunch of variables. Forwarding them to subclasses.

def acts_as_secure(options = {})
@secure_except = unsecure_columns(options.delete(:except))
@secure_storage_type = options.delete(:storage_type) || :binary
@secure_class_crypto_provider = options.delete(:crypto_provider)
@secure_crypto_providers = {}
extend(ActsAsSecureClassMethods)
end

module ActsAsSecureClassMethods
def inherited(sub)
[:secure_except, :secure_storage_type, :secure_class_crypto_provider, :secure_crypto_providers].each do |p|
sub.instance_variable_set("@#{ p }", instance_variable_get("@#{ p }"))
end
super
end
end

[PLUGIN RELEASE] Metrics

From Jeffrey Damick

Introduction

This gem provides a metrics collecting for controllers, database queries, and specific blocks of code or methods. It is designed to be light-weight and have minimal impact on production builds while providing performance indicators of the running application.

Disclaimer

This software is released to be used at your own risk. For feedback please drop us a line at rails-trunk [ at ] revolution DOT com.
Using this plugin should not be your first step in application optimization/scaling or even the second one.

Example

class SomeClassToTest
collect_metrics_on :my_method

def my_method(blah = nil)
true
end
end

Output:

[ERROR] [2007-06-21 23:21:19] [trunk] [Metrics]|[76716]|[MysqlAdapter.log]|0.012727|args=["root localhost trunk_test", "CREATE DATABASE `trunk_test`"]
[ERROR] [2007-06-21
Continue reading "[PLUGIN RELEASE] Metrics"

[PLUGIN RELEASE] Metrics

From Jeffrey Damick

Introduction

This gem provides a metrics collecting for controllers, database queries, and specific blocks of code or methods. It is designed to be light-weight and have minimal impact on production builds while providing performance indicators of the running application.

Disclaimer

This software is released to be used at your own risk. For feedback please drop us a line at rails-trunk [ at ] revolution DOT com.
Using this plugin should not be your first step in application optimization/scaling or even the second one.

Example

class SomeClassToTest
collect_metrics_on :my_method

def my_method(blah = nil)
true
end
end

Output:

[ERROR] [2007-06-21 23:21:19] [trunk] [Metrics]|[76716]|[MysqlAdapter.log]|0.012727|args=["root localhost trunk_test", "CREATE DATABASE `trunk_test`"]
[ERROR] [2007-06-21 23:19:56] [trunk] [Metrics]|[35158]|[Request to [Test::SomeControllerWithMetricsId]]|0.001373|action = index|path =some?
[ERROR] [2007-06-21 23:19:56] [trunk] [Metrics]|[33676]|[SomeClassToUseModuleMixin.another_method]|0.000020|args=["also"]

for more samples and test cases see test/metrics_test.rb

Usage

The metrics are written to: logs/_metrics.log

Configuration can be updated in metrics/config/metrics.yml, you may copy this file to your RAILS_ROOT/config/metrics.yml and customize for your application, the RAILS_ROOT will be checked first.

Sample metrics.yml

production:
min_real_time_threshold: 1.0
single_line_output: true
some_module/test_controller: 0.0

Installation

As plugin:
script/plugin install svn://rubyforge.org/var/svn/metrics/trunk/vendor/plugins/metrics

License

metrics is released under the MIT license.

Support

The plugin RubyForge page is http://rubyforge.org/projects/metrics

#48 Console Tricks

The Rails console is one of my favorite tools. This episode is packed with tips and tricks on how to get the most out of the console.

#48 Console Tricks

The Rails console is one of my favorite tools. This episode is packed with tips and tricks on how to get the most out of the console.

Happy Birthday to Me!

42! No way, that’s twice the legal drinking ages in most of the US states, 2.625 times the Swiss legal drinking age. I have been a professional programmer half my life and I am still having fun. I am now the oldest coder on most of the projects I work on 🙂 Ruby and Ruby on Rails is inspiring me in trying to write elegant and better code. But it’s really working on awesome projects and working with awesome people that inspires me to continue developing software. This said I will be away from my computer today. I will go to the Zoo with the kids and then to the pool. Couldn’t be much better. Have fun!

Open For Business: eBay Desktop Beta – an Adobe AIR Application.

The invitations are going out! So, if you haven’t yet, go signup for the eBay Desktop beta (code name Project “San Dimas”“) at ”http://www.projectsandimas.com/">http://www.projectsandimas.com/.

The eBay Desktop is an Apollo Application, oops, Adobe AIR application, that is developed by our friends Tony Hillerson and Sean Christmann from EffetiveUI for eBay. As they had their hand full applying the finishing touch to the beta, they asked Lee and myself to help with the beta Signup program and administration site which is a Ruby on Rails application. How cool is that! Thanks guys for the opportunity to work on such a visible project…

Now if you know Sean you will understand why using the eBay Desktop is fun. When Sean is not coding he is playing games, and he want’s any application to be fun and playful. Even when he gives talks he manages to demonstrates how to program a WII controller. It’s cool that Alan Lewis gave them the creative freedom to create some new user experience concepts on top of the existing eBay apis.

Ken, my desk neighbor at one of my clients asked why would you need something like Adobe AIR? Well in the case of the eBay Desktop, you are provided with an immersive experience, having a dedicated application will encourage the user to stay within that application and not get (too) distracted. The desktop also allows to save custom searches and additional information locally, thus providing functionality that may not be available by the existing eBay apis. It could also allow for functionality like drag&drop of images and text from the file system to create online auctions, use a connected camera to snap pictures, scan bar codes to retrieve product information, and so on…

20070620_eBayDesktop.jpg

PS: Hey Sean what’s your blog…or are you too busy coding?

#47 Two Many-to-Many

There are two different ways to set up a many-to-many association in Rails. In this episode you will see how to implement both ways along with some tips on choosing the right one for your project.

#47 Two Many-to-Many

There are two different ways to set up a many-to-many association in Rails. In this episode you will see how to implement both ways along with some tips on choosing the right one for your project.

Roundtable: Women in Development II – Ruby on Rails Podcast

Part II of a discussion with and about women in development.
See also: