There were not a lot of extra Ruby gems needed to put this together. The most useful were bubble-wrap, with its simplification of many things in the iOS Framework, and ib, which allowed me to use Interface Builder to layout the UI.
The other gems are fairly self explanatory. cocoapods and motion-cocoapods allowed me to choose from many existing Obj-C libraries and avoid re-inventing the wheel. motion-testflight made it easy to upload test builds for beta testers to try builds of the app during development. If you haven’t had a chance to try out TestFlight, you should definitely check it out.
Including the Ruby Gems
Using Bundler made it easy to manage the dependencies. After creating the Gemfile and making sure that Bundler was installed, gem install bundler, I just needed to download and install all the Ruby gems with a bundle install. I could also update the gems to the latest version with a bundle update. It should be noted that you can specify versions of gems in the Gemfile and you will probably want to do this to avoid getting updated gems that break something or change things underneath you.
To include the gems in the project, I just had to add a couple of lines to the Rakefile:
Not all Rubygems will work for Rubymotion. There is no require functionality in Rubymotion, so you will need to find gems that have been built to work with Rubymotion.
The Cocoapods
There are a lot existing open source Obj-C libraries out there that can be included in iOS projects. Fortunately, we can also leverage many of those via Cocoapods, which aims to be the package manager for Obj-C projects (think Bundler). Normally, you would have a Podfile, but for Rubymotion projects with the motion-cocoapod gem, you add your Pod dependencies to your Rakefile:
...
app.pods do
pod 'ViewDeck'
pod 'QuickDialog'
pod 'JSONKit'
pod 'CMPopTipView'
pod 'ShareKit/Facebook'
pod 'ShareKit/Twitter'
end
...
Much like Bundler, you can specify specific versions to avoid the pods getting updated beneath you. The Pods are installed into your project vendor/Pods directory where you can look through all the source when you need.
Here is how each Pod was used:
ViewDeck: slide out a view from the left or right, similar to Facebook or Path apps. Used to make navigating and searching questions easier.
QuickDialog: made the settings form slightly easier to build. Documentation is weak and you will need to be comfortable spelunking.
JSONKit: even though bubble-wrap has helpers for parsing JSON, it relies on iOS 5 features. If your project needs to support iOS 4.3+, you will need to use something like JSONKit.
CMPopTipView: A nice little tooltip library to make the tutorial and show off features when some first installs the app.
ShareKit: A swiss army knife of sharing content to a number of sites. I only needed Facebook and Twitter, but there are a bunch more that can be used.
Gotchas
Adding new Pods, it may sometimes appear that the Pod isn’t available or working. I found that because some Pods affect the project .pch file, it is sometimes neccessary to rake clean to get things working again.
Conclusion
It is easy enough to leverage the growing number of Rubymotion gems and Obj-C iOS libraries. The challenge can be finding them so that you don’t re-invent the wheel.
I have been a fan of the CSS frameworks for a while now. I started with YUI and now use Blueprint or 960.gs on a regular basis. What I never liked about the frameworks was the need to add all of the extra classes to the HTML markup. It seemed messy, wasn't semantic (not that I am a fanatic about that), and made it harder to reuse partials in my Rails projects.
CSS Improved
For a while now there has been SASS, which allows you to write CSS-like files that get translated into CSS. The advantage is that you can now use things like nested rules, variables, mixins, and more. The markup is similar to CSS so the learning curve is minimal. On top of that, there is Compass, which adds some of the popular CSS frameworks as mixins. Now it is easy to mixin the styles of the frameworks to your semantic classes in the CSS without adding all of the extra framework specific classes to your HTML markup.
What I did not like about SASS and Compass was the dependency on HAML. I have tried to make the switch form ERB to HAML and I know that you can use SASS and Compass without using HAML in your templates. But it always seemed like an extra unneeded dependency in my apps.
Less CSS
I recently came across LESS, a Ruby gem similar to SASS. The idea is that you can write .less files that are CSS-like and they will be translated into CSS. The advantage, as I see it, is that you can use existing .css files as .less files since the syntax is so similar. In addition to the standard CSS syntax, you also get nested rules, variables, and mixins, just like SASS, but without the extra dependency. You can also import other CSS files as-is, like the CSS frameworks, and mix those styles into your semantic styles. This eliminates the need for Compass to provide the SASS-ified version of the framework.
The gem itself is not specific to Rails and can be used on any project. You just need to run the LESS compiler to translate the .less file into a .css file. There is a Rails plugin that make it easy to start using LESS in your Rails projects.
LESS In Action
So what does it all look like? I will leave you with an example of how it all fits together.
I recently had a spike in traffic (1-2 visits/day to 30-40 visits/day) over at CatechizeMe.com. Someone out on the internet came across it, found it useful, and linked to it. Yay!
With this new traffic, came some new requests for features. The first was a request for a Google Gadget. I didn't know much about what I needed to create a Gadget, but after looking it up I realized I could use the CatechizeMe API that came automatically when I built the app. With just a few lines of code, the Daily Question service was created and returning JSON data. You gotta love it when things are this easy.
BEFORE:
def daily_question
@question = @catechism.daily_question
render :template => '/questions/show'
end
AFTER:
def daily_question
@question = @catechism.daily_question
respond_to do |wants|
wants.html { render :template => '/questions/show' }
wants.js { render_json @question.to_json }
end
end
I do not hide the fact that I like to try out new ideas by building lots of little applications. One thing I find myself doing is recreating many of the same pieces for each application. So I finally gave in and built a default template for the way I like all of my applications to start. There are other starter apps, but this one is tailored to my idiosyncrasies.
Say what you will about CSS frameworks, but they make my life a lot easier. From the website, it “gives you a solid CSS foundation to build your project on top of, with an easy-to-use grid, sensible typography, and even a stylesheet for printing.” There are official plugins for the framework, like “buttons” and “link-icons”, and other user created ones, like silksprite (http://www.ajaxbestiary.com/Labs/SilkSprite).
The way I think authentication should be done. Instead of copying a lot of authentication logic (encrypting passwords, remember tokens, etc.) into your user model, it is kept in the gem and is easily updatable. It has lots of configuration options to fit with your authentication needs and some reallygoodtutorials.
In app/models/user.rb
class User < ActiveRecord::Base
acts_as_authentic
end
This is great way to store application wide configuration and settings. By adding an initializer to load the config.yml, you can access configuration anywhere in the app.
From the same folks who brought you Authlogic, there is Searchlogic. You will always need pagination. You may not think so now, but believe me, you will. So just start out with it enabled. What I really, really like about Searchlogic, is not just the pagination support, but how easy it makes building advanced search forms (including searching nested objects). And again, there is a great tutorial
It just makes more sense to me. Like RSpec, only fewer calories. micronaut is a BDD framework similar to RSpec. In fact it uses all the same RSpec matchers, so there is not a new syntax to learn. And it adds metadata to the loaded examples that is useful for deciding which tests to run, exclude, document, etc. or building additional tools for your example suite.
The application currently has examples (a.k.a. specs) for most of the existing code. Adding new examples, should be quick and easy. To see it all, start with rake examples
beholder watches for files to change and then reruns the appropriate tests/specs/examples. Now I don’t have an excuse for not running the example suite, because it is always running for me.
Inevitably, every project wants to be able to manage the “static” content on the site. Comatose is a very simple CMS plugin. Nothing fancy, but that is great for these small projects. You can even style the admin interface to look more like your application (which I did), but the default styles could work just fine. It is possible to use the content in Comatose as an entire page or a partial across many pages. The app has a migration that creates some default pages and an example partial.
Fastest way to build a super simple admin interface. Or you could use it to build more complex admin. It is really quite flexible with its search, CRUD, and the ability to customize.
A few useful helpers for SEO purposes. Create page titles (h1) that match the html title (title), support for meta tags and easily add some breadcrumbs to each page.
Conclusion
Like I said before, it is tailored to they way I like things to start out. You can fork it and change it. I may not roll you changes back in, but that’s ok because now you have an starter app just the way you like it.
I have been gradually adding new features to my StagingTracks.com website. Really, it is a place where I can try out new things outside the office. I have upgraded the UI to be a little cleaner by using the Blueprint CSS framework. It was a easy way to normalize the CSS across browsers and easily implement a column-based layout. I also added Twitter notifications when new shops, clubs, and shows are added and reminders for upcoming shows each week. Does the model railroading community really need all of this? Probably not, but it helps me keep my skillz sharp.
Finding Shops, Clubs, and Shows on your iPhone
When I built StagingTracks a few years ago, I did it because I was traveling and wanted to easily find the model railroading community wherever I was. As it has grown over the past few years, so has technology. While it was possible to navigate the StagingTracks website using a browser on the phone, it was not optimal. Since this is my little sandbox for experimenting, I wanted to see how difficult it would be to add an optimized iPhone interface.
Native app or Web app?
I spend my daylight hours developing web applications for others, so it made sense that I should reuse the infrastructure that I already had in place. I didn’t want to learn iPhone SDK and all that is involved with that right now and I had recently come across the iUi javascript and css framework. iUI can give web applications a native iPhone application feel, so I just needed to see how to incorporate it into my “legacy” Rails application.
Resources
A quick Google search for iUI and Rails turned up Ben Smith’s excellent iPhone on Rails article.
iPhoney
Reading through the article, I downloaded iPhoney for quick testing without an iPhone. Be sure to use the iPhone User Agent in the iPhoney menu.
Local Subdomain for Testing
I was going to serve the iPhone version from the subdomain iphone.stagingtracks.com, so I needed to setup something similar in my local development environment. Fortunately, this was very easy with the Ruby Ghost gem found via Robby Russell’s Get to know a gem: Ghost.
sudo ghost add iphone.localhost.com
We needed to add the .com so that the call to the request.subdomains will pick out the iphone portion.
iUI Framework
After downloading the iUI framework from the project site, I moved everything into its rightful place.
public
- stylesheets
- iui.css
- javascripts
- iui.js
- images
- iui
- copy all of the .gif and .png files into here
Because I moved the images into the /images/iui folder, I needed to update the image locations in the iUI css. A quick find/replace and I was ready to go.
Application changes
I won’t go into all the details since Ben’s article hit most of the high points. Here are the few additional bits that I came across as I was adding my iPhone interface.
Basic approach
The basic approach to adding the iphone interface is to update the controller to render the iphone partial without the layout (since everything is AJAX) and then create an iphone template.
In posts_controller.rb change from:
def show
@post = Post.find(params[:id])
end
to
UPDATE:: format.html should come before format.iphone. For some reason it was working for browsers that were not IE. Weird.
def show
@post = Post.find(params[:id])
respond_to do |format|
format.html
format.iphone { render :layout => false }
end
end
Since one of the more interesing features of StagingTracks is the ability to search for organizations near you, I wanted that to be prominent. By adding a “button” link to the toolbar, it now shows up on every page.
Since I already had paging in place for the blog posts, I wanted to be able to reuse that, if possible. Turns out that was pretty easy to add as well. I needed to separate the post_items into a separate partial so that I could return the next page of
's
to replace the “More news…” link (notice the target for the “More news…” link is “_replace”).
<% end %>
<%= content_tag :li, link_to("More news...", posts_path(:page => posts.next_page), :target => "_replace") if posts.next_page %>
A quick change in the posts_controller.rb from:
def index
@posts = Post.latest.published.paginate :page => page, :order => 'published_at desc'
end
to:
UPDATE:: Same change to the ordering of format.html and format.iphone.
def index
@posts = Post.latest.published.paginate :page => page, :order => 'published_at desc'
respond_to do |format|
format.html
format.iphone do
if page == 1
render :layout => false
else
render :layout => false, :partial => "post_items", :locals => {:posts => @posts}
end
end
end
end
Styling Form Select Inputs
My search form has a dropdown for choosing the country that you want to search. By default, this did not look very nice. Since it didn’t need a label, I just left it out in the form and added some additional CSS.
In search/index.iphone.erb
<% form_tag(search_path, :class => 'panel', :title => 'Search') do %>
Find Local Shops, Clubs, and Shows
<%= content_tag :p, flash[:error], :class => 'error' if flash[:error] %>
All told, I probably spent less than eight hours over a couple of nights adding a simple iPhone interface to my existing application. I still want to look in to modifying the CSS more to have it look more like the regular StagingTracks website, but that can come later. This was a fun little experiment.