As a developer, you’ll work on hundreds of different projects in your career. The biggest pain in the ass when changing projects is having to get started in a new development environment. If you’re moving to a different Rails project you have to install new Rubies and bundle – plus you might need to install Redis, Solr or jam your public key on a server somewhere. Even worse, after bundling you notice this is a Rails 2.3 project not a Rails 3.2 and you haven’t worked with Rails 2.3 for years and can’t remember how to start a server.
Then you have to work out who is in charge of this damn project and bug them to find out why your PostgreSQL is complaining about some “PostGIS” thing you’ve never heard of. Once you get that sorted you run the specs to see what shape the app is in and half your tests fail because you’re missing some `config/some_random_config.yml` file. You talk to the guy in charge of the project and he tells you to scp it from the staging environment. “But dude, how do I even get to the staging environment? What server is it on?”. Turns out it’s on an Amazon server and you need a special permissions file to be able to access it.
Imagine this process happens 12 months after anybody has ever worked on the project and the last girl to hack on the codebase has long since left the company. Your company has just lost months of her gaining knowledge that you now have to re-acquire. Knowledge that should have been documented somewhere so it wasn’t lost.
What’s the solution?
Although I did reveal the solution in the title (because I’m a bad writer, sorry) – the answer to the question is to write and maintain a comprehensive README file.
How do I write a good README?
Writing a good README is easy. You just need to know what information is required for developers to use and understand the application. Here’s some Rails-centric information I include in the READMEs I write for The Frontier Group:
README: General Information
The “General Information” section should give a new developer an idea of what the project is about and who is involved with it.
Information you might want to include is:
The name of the project
The name and contact details of the client and any 3rd party vendors
The names of the developers on the project
A brief description of the project, you should include the answer to the age-old question “What problem is this project solving?”
An outline of the technologies in the project. e.g.: Framework (Rails/iOS/Android/Gameboy Colour), programming language, database, ORM.
Links to any related projects (e.g.: Is this a Rails API that has corresponding iOS and Android clients?)
Links to online tools related to the application (e.g.: Links to the Basecamp project, a link to the dropbox where all the wireframes are stored, a link to the Pivotal Tracker project)
README: Getting Started
The “Getting Started” section outlines the process of getting the app installed and usable for a developer. I define ‘usable’ in this context as able to login to the application and access all of the functionality available.
Information you might want to include is:
A detailed spin-up process. This should include:
Instructions on installing any software the application is dependent on: e.g.: wkhtmltopdf, PostgreSQL, XQuartz.
Instructions on running the app. For rails apps you’ll want to include the rake db:create db:migrate db:seed process here, as well as instructions on starting a server (e.g. are we using pow, or just the default `rails s`)
A list of credentials that can be used to log in with each user type in the system and ideally the URL that a developer can log in from.
Any information about subdomains in the app (e.g.: api.myapp.dev/)
When writing instructions pretend you’re writing them for someone who knows next to nothing about developing in the framework/language your application uses.
README: Testing
All you need to include in the “Testing” section is the commands to run any of the test suites you have (e.g.: RSpec, Jasmine, Cucumber, Spinach) and any setup you need to do before-hand (e.g.: rake db:test:prepare). This section will be small but vital.
README: Staging and Production environments
The staging and production environment sections (one section per environment) should provide any information a developer might need to know about these environments.
Information you might want to include is:
Which server is the application on? Is it on Amazon Sydney? A server in the office? A data-centre down the road?
How can a developer connect to the server? Do they need particular permissions? Who do they need to talk to to get those permissions?
Where on the server is the application located
What is the deploy process for this server
Are there any other services on the box related to the app a developer will need to know about? Any cron jobs? Some Resque workers?
Maintaining the README
Worst case scenario, you don’t maintain a README. You’ll burn in developer hell, sorry.
Slightly better than that, your changes to the README will be reactive – you’ve had to work out how to install some required software and you’ve put the details of doing that in the README for future developers.
If you’re having to make reactive changes, you’re probably spending 30 minutes that another developer already had to spend working out a solution to the same problem. This equates to 30 completely wasted minutes that could have been spent implementing a feature for your client.
In an ideal world, maintaining the README is proactive and becomes part of your development life-cycle. As an example, your development life-cycle could look like this:
Plan feature
Write tests
Implement functionality
Update README if required
Get code reviewed and ensure all your tests pass
Merge your feature
What am I getting by writing a good README?
With a comprehensive, well-written README any developer should be able to hop on to your project and begin hacking away within 10 minutes. If you consider that over the course of developing an app you’ll likely see multiple developers set up the app multiple times, you’ll cumulatively save hours of developer time with just minutes of work.
Importantly: If you can set yourself up with this fantastic habit early, other developers will love working with you. Documentation is what separates us from the animals, ladies and gentlemen.
I recommend you invest 30 minutes of your week updating the READMEs of the projects you’re working on. You may not see any benefits straight away, but in 12-18 months time whoever has to maintain that code will be thankful you did it. Spoiler alert – that person will probably be you.
We’ve just launched Take5 Feedback and its mobile app. This is the fourth mobile app that The Frontier Group has launched, that I’ve worked on.
Previously, we had been using a combination of PhoneGap and SpineJS. This helped us get into the iOS market while still using tools we were familiar with as web developers. After releasing a few apps using PhoneGap and SpineJS, we found that we were consistently spending time during app development fixing things like scrolling, tap events propagating to other views and transition animations. This coupled with the less-than-fantastic debugging tools made us feel that perhaps we needed to re-evaluate our technology choices.
This time we decided to kick it up a notch by moving to RubyMotion. (Bam!).
For those who haven’t heard about RubyMotion, it’s a library that lets you write native iOS apps in Ruby which is then compiled down into Objective-C. As most of us at The Frontier Group are Ruby developers, this felt like a technology well worth exploring. After working with it for a few months, I think it’s the way to go for us moving forward. Now, let’s talk a little about RubyMotion.
Off the bat, one of the big advantages of RubyMotion is that you’re writing native apps in Ruby and not in Objective-C. I’ve found that Objective-C is not an intuitive language and certainly nowhere near as readable as Ruby (especially when method chaining looks like array declaration shudders). One thing RubyMotion has ported over from Objective-C that I do like is the named parameter which makes method declaration/invocation much more readable. For example, let’s say I have a method that generates a receipt and takes a the date of the purchase and the amount in cents as parameters.
In regular Ruby it would be declared something like this:
def generate_receipt date, amount
# Do something...
end
Now this method could conceivably be called like this:
square = generate_receipt('3/3/2013', 100)
In RubyMotion, however, using named parameters the same declaration/invocation looks like this:
def generateReceiptForDate date, andAmount: amount
# Do something...
end
As you can see, it gives the parameters passed in more meaning during the method invocation. Now most Rubyists won’t call methods with “magic numbers/strings” and you can achieve the same thing in < Ruby 2.0 by accepting a hash but I think this is a nice little addition.
Next I’d like to discuss generating and organizing views. MVC in iOS is a little different from MVC in Ruby on Rails, in that views are a little more tightly coupled to the controller. View elements are ultimately defined and positioned in the controller. Those elements will rely on methods which should be defined in the controller as they deal with event handling and navigation. Let’s take a simple example. I want a screen to have a table and when I tap a row I want to navigate to another screen.
As you can see we’ve got a mix of view level logic (positioning the table on the screen and defining what a row will look like) and controller level logic (setting the data source and the navigational behavior). This makes views and controllers coupled a little tighter than you might be used to. This can lead to extremely fat controllers when you have many elements on the screen each with its own position, look, feel and event handlers. A pattern I found that has worked for me is to have a subclass of a UIView that is responsible for drawing all the elements on a given screen. Let look at an example.
Let’s start by creating our own UIView where we define our table and its look and feel.
class ExampleView < UIView
TABLE_FRAME = CGRectMake(0,0,300,400)
TABLE_ROW_HEIGHT = 30
attr_accessible :table
def initWithFrame frame
super
setupTable
self
end
private
def setupTable
@table = UITableView.alloc.initWithFrame(TABLE_FRAME)
@table.rowHeight = TABLE_ROW_HEIGHT
self.addSubview @table
end
end
class ExampleController < UIViewController
REUSE_IDENTIFIER = 'example'
def viewDidLoad
@data = [1,2,3,4]
self.view = ExampleView.alloc.initWithFrame(view.frame)
self.view.table.delegate = self
self.view.table.dataSource = self
end
def tableView(tableView, cellForRowAtIndexPath: indexPath)
cell = tableView.dequeueReusableCellWithIdentifier(@reuseIdentifier)
cell ||= UITableCell.alloc.initWithStyle(UITableViewCellStyleDefault,
reuseIdentifier: REUSE_IDENTIFIER)
cell.textLabel.text = @data[indexPath.row]
cell
end
def tableView(tableView, didSelectRowAtIndexPath:indexPath)
tableView.deselectRowAtIndexPath(indexPath, animated: true)
self.navigationController.pushViewController(AnotherController.new, animated: true)
end
def tableView(tableView, numberOfRowsInSection: section)
@data.count
end
end
Ok so now the responsibility for drawing the table (and any other element) is localized to the ExampleView class and the data manipulation and navigation logic is in the controller which is looking better. We can neaten this up further by making the view responsible for assigning delegates.
Let’s start by creating a new parent view that stores an object which will be the delegate for all elements within the view. In this example the delegate is the ExampleController.
class UIViewWithDelegate < UIView
attr_accessor :controllerDelegate
def initWithFrame(frame, andDelegate: delegate)
self.initWithFrame(frame)
self.controllerDelegate = delegate
self
end
end
Now that we have this class our ExampleView class can inherit from it, allowing the ExampleView to set the delegates for any elements inside it.
class ExampleView < UIViewWithDelegate
TABLE_FRAME = CGRectMake(0,0,300,400)
TABLE_ROW_HEIGHT = 30
attr_accessible :table
def initWithFrame(frame, andDelegate: delegate)
super
setupTable
self
end
private
def setupTable
table = UITableView.alloc.initWithFrame(TABLE_FRAME)
table.delegate = self.controllerDelegate
table.dataSource = self.controllerDelegate
self.addSubview table
end
end
class ExampleController < UIViewController
REUSE_IDENTIFIER = 'example'
def viewDidLoad
@data = [1,2,3,4]
self.view = ExampleView.alloc.initWithFrame(view.frame)
end
def tableView(tableView, cellForRowAtIndexPath: indexPath)
cell = tableView.dequeueReusableCellWithIdentifier(@reuseIdentifier)
cell ||= UITableCell.alloc.initWithStyle(UITableViewCellStyleDefault,
reuseIdentifier: REUSE_IDENTIFIER)
cell.textLabel.text = @data[indexPath.row]
cell
end
def tableView(tableView, didSelectRowAtIndexPath:indexPath)
tableView.deselectRowAtIndexPath(indexPath, animated: true)
self.navigationController.pushViewController(AnotherController.new, animated: true)
end
def tableView(tableView, numberOfRowsInSection: section)
@data.count
end
end
This has separated the view and controller logic out a little more and our controller is a little thinner. This becomes more useful as the number of elements on your screen increases. The one drawback I found was that my views were ending up with walls of constants defining positions, fonts, etc. Creating subclasses for individual elements that include the position/look/feel, would solve the problem, however you’ll end up with a lot of singleton classes.
Now a little on automated testing. Automated testing is generally a big part of any of the applications we develop. This time however, I found the experience to be so much more costly than testing in a Rails world. When I started developing the app I was aiming for the level of automated coverage we have in our Rails environments. As I attempted to achieve this, I kept hitting road blocks with either the testing framework not behaving as expected, or the mocking libraries placing a significant impact on the code being tested. After some discussion, we decided that due to the simplicity of this app we could achieve an acceptable level of quality and coverage by manual user testing. From this experience though we now know areas that can be improved and are currently developing our skills and tools to ensure that as our apps increase in complexity, we are able to ensure our code quality.
I could write so much more about RubyMotion and the different technical challenges I faced during the development process, but I really just wanted to give my overall impressions. I think it’s a good choice for any Rubyist wanting to venture in iOS development. In combination with the Apple developer docs, which are fantastic, it lets you learn the iOS framework while programming in a language that you’re comfortable with. It’s a well maintained library with the RubyMotion team making constant improvements. My only real criticisms are with the testing and debugging tools not feeling as flexible and as powerful as I’m used to in the Rails environment, but like I’ve said, these tools are being continually developed and improved, and have no doubt these tools will get better with time.
Frequently rails applications need to process data that will not be stored in a database. How does one handle this sort of data?
An example of this might be a user filling out a search form, doing some sort of query. This quickly gets complicated to handle once you start searching by dates (which have to be casted from strings) or have complicated rules about which parameters are required.
This sort of data doesn’t really fit into Ruby on Rails models (which are usually concerned with persisting data). Additionally this data is often subject to business logic and doesn’t really seem to belong in the controller layer either.
The Rails answer to such things is ActiveModel, which provides some tools to get you started on building models that aren’t based off ActiveRecord::Base. Unfortunately, however, there are a number of great features, such as typecasting, that you can’t easily get this way.
Enter active_attr, a fantastic gem by Chris Griego, that helps you bootstrap your new non-persisted models and get into the good stuff. Chris’s gem provides validation, mass-assignment, and security features among others.
My favourite feature is typecasting. In a regular active-record model Rails can infer the appropriate types of your attributes from the database – not a possibility in a non-persisted model. With active_attr you can define the types of your attributes with ease, and get some of that Ruby magic going.
Here’s an example of a PersonQuery:
class PersonQuery
include ActiveAttr::BasicModel
include ActiveAttr::TypecastedAttributes
include ActiveAttr::MassAssignment
include ActiveModel::Validations
attribute :full_name, type: String
attribute :born_after, type: Date
validates :full_name, presence: true, :length => { :minimum => 5 }
validates :born_after, presence: true
def execute
Person.where(full_name: full_name).where("date_of_birth > ?", born_after) if valid?
end
end
We have created quite a few location-aware applications for our clients over the last two years, using Rails with PostGIS on Ubuntu 10.04. However, today one of them started throwing HTTP 500 errors after working fine for a year. We investigated and found the cause to be a new error:
RGeo::Error::UnsupportedOperation (Method Surface#centroid not defined.)
followed by a stack trace. This was unusual as the code had not changed for months. A quick check at the rails console revealed a deeper issue:
1.9.3p0 :001 > RGeo::Geos.supported?
=> false
This error means that RGeo could not find the libgeos library. I reinstalled the gem with gem install rgeo which did not fix it, but the mkmf.log file provided a hint:
/usr/bin/ld: cannot find -lgeos
It was odd as the library had been there when the app was deployed. Searching /var/log/ for “geos” showed that the UbuntuGIS ppa we are using was updated last week, from 3.2.2 to 3.3.3. The upgrade had removed the libgeos-3.2.2 package, and installed the libgeos-3.3.3 package. However, the upgrade script had not added any symlinks, so while libgeos-3.3.3.so existed in /usr/lib/, it could not be found by the linker. The fix was simple:
At The Frontier Group, we use vsftpd with chrooted users for clients that require FTP access. It has been working well for four years, however after a recent upgrade to Ubuntu 12.04 we started receiving this error message:
500 OOPS: vsftpd: refusing to run with writable root inside chroot ()
Ben Scobie has a good overview of the problem. One solution is adding the following to your vsftpd config file:
allow_writeable_chroot=YES
Unfortunately vsftpd 2.3.5, which is packaged in Ubuntu 12.04, doesn’t support this feature. It is only available in vsftpd 3 onwards.
As an alternative solution, we have backported it from vsftpd 3 into Ubuntu’s 2.3.5 package and made it available as a vsftpd PPA on Launchpad. To use it, run the following: