Ruby
Growing the Ranks of Female Devs
rebecca posted on Thursday, September 6th, 2012 | Rails, Ruby, Ruby on Rails | 1 Comment
At PRX I am the only woman on a team of six. With a team that size, it could be a coincidence that I’m the only one. But it is not. In most companies with six developers, zero would be female. I’m here at PRX because of my skills but also because PRX cares about having and encouraging a diverse workplace. We took that beyond our office a few weeks ago when we participated in a full-day free workshop focused on teaching Ruby on Rails development to interested women. In one evening and one long day we taught 44 students enough so that by the end of the workshop they had each deployed their own web application live on the Internet. You can read an interesting and thorough recap of the event, replete with pictures, feedback, etc., here. Also, if you’re interested in participating, we plan to do another workshop with the same format later this fall. You can find out more on our site.

PRXers had roles in every aspect of the workshop. Dan Choi, a former PRX employee and contractor and permanent PRX friend, was the driving force behind the workshop and the main organizer. We developed the curriculum with a team of volunteers from BostonRB including Andrew K., Chris R. and me from the PRX team. I was the main lecturer/teacher for the workshop, Andrew was a teaching assistant and lecturer, and Kathryn Bennett, a tech intern at PRX, also TA’ed. Genevieve, who works on the content side of PRX and does a lot of work in our content management system, participated as a student. And PRX backed the event financially as one of the sponsors.
The main goal of the event was to encourage people with no previous experience to get involved with Ruby/Rails development, and I believe we succeeded. But for me there was another positive outcome. In putting on the event we pulled together a team of volunteers, teachers, and sponsors who all see the importance of an inclusive, diverse community of developers. At this one event I met and worked with over ten other female Ruby/Rails developers many of whom work in places where they are the only girl. I had only met two of them before, and I was frankly but happily surprised that there were so many of us. It helped show me that the community we were envisioning is already nascent among the women who are working as developers and the colleagues, companies, and organizations that are supporting us.
I’m proud of what we accomplished and of PRX for having a strong hand in it.
Our Experience with Chef
chris posted on Tuesday, July 5th, 2011 | Ruby | No Comments
Part of the rollout for some automation features we have been working on is a requirement that we have a very scalable set of FTP servers. We’ve been using Amazon Web Services for many things at PRX, and the ability to spin up very cheap instances on both sides of the country is a huge win for performance and reliability. Unfortunately, there’s a fair amount of work that needs to be done when a new server is spun up. Because the specifics of this work changes with some frequency and because we have a number of different kinds of servers we need to be able to spin up (and more are coming), the standard use of AMIs will be a nightmare to maintain.
Enter Chef.
Chef allows us to develop (in source control) a system which describes both a particular configuration we would like our servers to have, and how to achieve this configuration. The configuration files are implemented in ruby, so the learning curve is primarily around nomenclature (and there’s a whole lot of it to learn). That having been said, most of the new concepts introduced by chef make sense once the system at large, “clicks.” I don’t expect a blog post to explain all of the nuance of an infrastructure configuration framework, but I think I can give enough introduction that one can walk away confident in their ability to do some simple deployments.
Clients and Servers
The first thing one needs to learn about Chef is the notion of a server and a client. A chef server holds information about the chef clients ((This is not strictly true – chef makes a distinction between clients and nodes; the former being something which interacts with the chef server and the latter being something which is assigned a set of code to execute. In nearly every case, however, the two are interchangeable. Just remember that when you assign something a role or recipe, you are assigning a node, and when you download updated configuration details from the chef server, you are using a client.)) in your deployment environment and what configuration they should have in order to be considered set up. It also stores the code you have written so that clients can achieve the expected configuration, but it never executes it. A chef server can be thought of, in simple terms, as a database.
One can install the server themselves, or can opt to use the Opscode platform, which is free for smaller deployments.
Clients pull down information about how they are expected to configure themselves from the chef server, and then execute the code you have written to get to that point. They do this by manually or periodically running chef-client, which reads from the chef server what code it will execute, then downloads and executes it.
Cookbooks and Recipes
The ruby code stored on a chef server is grouped into files called recipes, and those recipes are grouped into directories called cookbooks. Usually, cookbooks are collections of recipes that relate to a specific software package. For instance, one might write a cookbook for the Apache web server that included a recipe for the web server itself, a recipe for mod_ssl, and a recipe for mod_dav.
Many cookbooks (and, by extension, many many recipes), are available on the opscode community website. If you see one you would like to install, you can use the knife cookbook site install <cookbook name> command. Recently, Opscode did a major overhaul and made sure that many of these cookbooks are in working order. Please be aware, however, that many of them were written months or years ago, and may require some tweaking, especially if the software package they work with changes often.
Run Lists and Roles
Another construct to be aware of is the, run list. These are usually recipes, listed in order, that should be executed by the client every time it pulls information down from the chef server. Chef also supports roles, which are run lists in their own right, but can be referenced in other roles and run lists. In our current deployment system, we have a few roles which each contain several recipes, and each Chef client is configured to have only one or two roles in its run list.
Starting a Chef-Managed Instance
The typical workflow for starting a new instance is to spin one up, install chef, register it as a client with your chef server, add things to the client’s run list on the chef server, and then run chef-client to pull down the run list and execute it. Quite a mouthful!
Thankfully, in practice, this can easily be rolled up into one step. Because we are making use of Amazon EC2 at PRX, we installed the knife-ec2 gem and we can run knife ec2 server create -r "run list". This spins up an ec2 instance, bootstraps chef, registers it as a client with the chef server, and sets the run list appropriately. It also runs chef-client automatically when everything is set up, so one should have a new EC2 instance with all of the software one wants installed and configured.
Wrapping Up
We’ve covered the actual steps involved in using a configured chef server to deploy new EC2 instances, but we haven’t touched on how to actually configure the server. For that, I will refer you to the fantastic Chef Repo article on the Opscode Wiki, which will explain the directory structure and basic workings of the repository you will use to interact with the chef server.
I found much of the language involved in learning the chef framework confusing when I was learning it, and I hope that this article serves to make the more fundamental concepts that are needed a little easier to understand.
Git Hooks and Ruby
chris posted on Wednesday, March 16th, 2011 | Git, iPhone, Ruby | 1 Comment
Happy Wednesday, everyone! I don’t have very much time this week, so I’ll keep this short and sweet: While we’ve historically used Subversion at PRX, we have recently been migrating to Git to take advantage of some awesome tools and to better interact with the community.
We’re also most comfortable with Ruby, so when I was asked to look into setting up a build server for our iOS apps, there wasn’t much question as to how I would do it.
I set up a bare git repository running on a spare MacBook Pro with XCode and found the commands that were necessary to run when a new build was ready to be deployed. The next step was to set up the appropriate hook for that Git repository so that the builds could be triggered by a push.
In the SVN world, this would be a post-commit hook, but because Git works differently (one push can contain many commits), the hook we are interested in is the post-receive hook. You can take a look in your .git/hooks directory for some samples, most of which are written in sh. We wanted something in ruby, and here’s what we came up with:
#!/usr/bin/env ruby
require 'rubygems'
require 'grit'
repo = Grit::Repo.new(File.join(File.dirname(__FILE__), '..','..'))
while msg = gets
old_sha, new_sha, ref = msg.split(' ', 3)
commit = repo.commit(new_sha)
case ref
when %r{^refs/heads/(.*)$}
branch = $~[1]
if old_sha.to_i(16) == 0
# A new branch was pushed
else
# A branch was updated
end
when %r{^refs/tags/}
tag_object = Grit::GitRuby::Repository.new(repo.path).get_object_by_sha1(new_sha)
tag = tag_object.tag
tag_message = tag_object.message
if old_sha.to_i(16) == 0
# A tag was created
else
# A tag was moved
end
end
end
Simply save this in your .git/hooks/post-receive file and make it executable. Then, every time you push to this remote, the script will execute. You can make whatever modifications are necessary for your specific application.
I hope this helps everyone working with Git hooks and Ruby!
Polymorphic Associations and Abilities
chris posted on Monday, November 1st, 2010 | Rails, Ruby | 8 Comments
I recently had a conversation with an old friend who is in the process of learning Ruby on Rails. He had run into a problem with polymorphic associations and I recommended he try something I had done for another project using polymorphic associations. It turns out that it was a good solution to the problem, and since I haven’t seen the pattern talked about elsewhere, I thought I should get it down on paper.
For the sake of example, let’s say we’re building a Tumblr clone, where the polymorphic association lives inside of a Post model, and the different kinds of posts are all models unto themselves. We could use single table inheritance, but maybe the models vary enough or the storage method differs enough that that’s a bad idea. For that matter, Single Table Inheritance often just feels wrong.
Simplicity will work best in this case, so let’s say that every post has a title, timestamps, and belongs to a user.
# db/migrate/001_create_posts.rb
class CreatePosts < ActiveRecord::Migration
def self.up
create_table :posts do |t|
t.string :title
t.references :user
t.integer :postable_id
t.string :postable_type
t.timestamps
end
end
def self.down
drop_table :posts
end
end
# app/models/post.rb
class Post < ActiveRecord::Base
belongs_to :user
belongs_to :postable, :polymorphic => true
end
Before we even get into the different kinds of posts we might want to make, let’s abstract this model so that we can tack it onto nearly any model in our application. How? A little bit of metaprogramming.
# lib/postable.rb
module Postable
def self.included(klass)
klass.instance_eval do
has_one :post, :as => :postable
alias_method_chain :post, :autocreate
[:title, :user, :created_at, :updated_at].each do |method|
delegate method, "#{method}=", :to => :post
end
end
end
def post_with_autocreate
post_without_autocreate || build_post
end
end
What does this get us? Well, in just a few lines of code, we have saved ourselves from really having to think about this problem very hard again. Consider creating two kinds of posts, something like a quote post and a freeform text post (called a blob, in this example):
# db/migrate/002_create_quotes_and_blobs.rb
class CreateQuotesAndBlobs < ActiveRecord::Migration
def self.up
create_table :quotes do |t|
t.string :quotee
t.text :quote
end
create_table :blobs do |t|
t.text :content
end
end
def self.down
drop_table :quotes
drop_table :blobs
end
end
I have deliberately chosen separate interfaces for these two kinds of model so I can show off some other stuff later. But to get them posting, all we need to do is include our “Postable” module and we are up and running:
# app/models/quote.rb class Quote < ActiveRecord::Base include Postable end # app/models/blob.rb class Blob < ActiveRecord::Base include Postable end
That’s it. We can get started immediately by creating our forms. Because of the Postable interface we have created, both models will automatically have the fields on Post (and, in fact, will work transparently thanks to delegation). The only other thing we need to think about is creating a common interface back to the individual post types from the Post model. We can do this with a helper, the model, or we can take advantage of Rails’ partial rendering and just create a views/quote/_quote.html.erb and views/blob/_blob.html.erb. Those views might look something like:
# app/views/blob/_blob.html.erb <h2> <%= blob.title %> </h2> <div class='byline'> by <%= link_to blob.user.name, blob.user %> on <%= blob.created_at %> </div> <div class='blob-content'> <%= blob.content %> </div>
I’ll leave the quote partial as an exercise for the reader.
At this point, we can put all of our posts on a single page like this:
# app/controllers/posts_controller.rb
class PostsController < ApplicationController
def index
@posts = Post.order_by('created_at DESC')
end
end
# app/views/posts/index.html.erb
<h1>All Posts</h1>
<%- @posts.each do |post| %>
<%= render post %>
<% end -%>
The same technique can be used in a number of situations, such as many kinds of authenticable users with one login screen or some other sort of shared attributes. What’s nice is that it is trivially extensible. You create a model with any extra fields that you need, include your module, and you’re up and running.
Categories & projects
- Android (4)
- Audio (1)
- Big Data (1)
- Git (2)
- HTML5 (1)
- Introductions (1)
- iPad (4)
- iPhone (8)
- Javascript (2)
- Jobs (1)
- KCRW (2)
- Mobile (14)
- Node.js (1)
- OmniGraffle (1)
- Rails (2)
- Ruby (4)
- Ruby on Rails (1)
- Social Media (1)
- The Count (1)
- Uncategorized (3)
- Wireframes (2)
