Rebasing is Editing Commits
June 30, 2008 at 05:19 PM
Rebase is one of Git's most alluring and yet most difficult-to-comprehend features. Rebasing is editing commits. When you rebase, you're rewriting history.
This is possible with Git because of the separation between a commit and a push. A commit is a changeset with some attached metadata like the commit message. A push publishes your commits to a remote repository. In Subversion, these steps are inseparable, both part of the commit.
Because of this separation, you can rewrite your commits before you push them. Just as you can edit your sourcefiles as many times as you want before you commit the results, so to can you edit your commits as many times as you want before you push the results.
Rebasing comes in two main forms. One is the interactive rebase. git rebase -i HEAD~5 pops you into an editor where you can change the order of the commits, delete entire commits, or squash commits together. You can also edit a commit, which will take you out of the editor and let you work on the commit in your working tree, and then commit with git commit —amend.
The other type of rebase is rebasing your local commits on top of some changes you're pulling from a remote source, or against a local branch. (Hence the term "rebase": you're creating a new base for your patches.) The action takes some new commits from a branch and slips them in underneath yours, at the point the two branches diverge. Commits prior to the divergence point are unaffected.
I use this kind of rebasing instead of git pull. You'll notice that pull almost always creates a merge commit, which is one of these things:
commit c4110e1fb1aa50c4f876716bde07f6a982a1f31c Merge: 296a0db... cb6050b... Author: Joe <joe@example.com> Date: Tue Jun 24 14:46:41 2008 -0700 Merge branch 'master' of example.com:repo.git
You might wonder why a merge commit is needed. Subversion doesn't have that, after all. But that's because the merge commit in svn is always implicit. Did you ever find yourself working on an active project, and then when you went to commit, you needed to svn up and got a whole boatload of changes, including some conflicts? Sure you did. And then you had to sit there and perform the merge on your source, finally making a big commit which included both your changes and the merge.
Git encourages discrete changesets, so it makes sense to break apart regular changes (new feature, bugfix, etc) from merges. But on an active project with lots of contributors, there's always merging going on. So you end up with lots of ugly merge commits cluttering up your logs.
Rebasing lets us have and eat our cake. Now you can make atomic commits as you're working, regardless of whether you are ready to share those commits with your team. But when you want to pull down the latest work from your team and merge it with your work, you can instead use rebase to reapply your patches on top of theirs. If you're not working on the same areas of the code, then this takes almost no work. Just type git fetch && git rebase origin master and you're done. (Notice that the output of git log then shows your recent changes at the top, regardless of the timestamps.)
Occasionally there are merge conflicts during the rebase, and this will drop you out into a shell with some rather intimidating messages. Don't worry though, all you need to do is take a look at the conflicted files, choose the part that you want (just like resolving a regular merge conflict), and then git add them. When you're done, run git rebase —continue. It does this merge step separately for each commit, so if there are a lot of commits in the difference between you and the remote source, this could get time-consuming. In that case you may want to git rebase —abort and then run a regular git merge or git pull. Next time, resolve to rebase more frequently, to make the merge job less of a headache.
Since rebasing is editing of commits, it doesn't make much sense to rebase things that have already been pushed. You can do it, but as soon as you go to merge with another repo that had the unedited commit history, you'll bump into weirdness (and probably invalidate your whole reason for rebasing, which was to clean up the history). So as a general rule, I recommend never rebasing things that have already been pushed.
If you push something and then realize five seconds later that you shouldn't have, it is possible to rebase your local branch and then git push --force, which will obliterate the remote repo's history. This won't help if someone else has already pulled the commits, since the next time they push the commits will come back, so only use it when you're certain that no one else has pulled.
EVDO Rules
June 28, 2008 at 05:21 PM
EVDO cards rule. The speed and latency are good enough for working over ssh and other common development tasks like pulling up documentation on the web or installing a small gem or two. (Though I don't recommend doing a git clone of Rubinius over EVDO). At Railsconf I used my EVDO card instead of the spotty wireless and was tapping away happily as everyone else struggled to load google.com.
Mine is from Verizon, $50/mo, worked on my Mac without as soon as I plugged it in, no software installation or configuration of any kind. So far I've been able to use it everywhere I've traveled (all within the US so far, though I'll be racking up some of those $0.002/kb roaming charges in Canada on my way to Rubyfringe next month).
During a road trip to Santa Barbara last month, the pager went off right in the middle of farm country. I was able to crack open my laptop and take care of the problem without even pulling over. sshing at 75 mph is a whole new experience. (No, I wasn't driving.)
EVDO has changed the way I work and travel. Cutting the tether means I'm no longer afeared of getting stuck in a waiting room for an hour - that's just enough time to crank out a cool new feature.
Office Aesthetics
June 27, 2008 at 03:26 PM
Slicehost's new offices are mouth-wateringly gorgeous.
Service-Oriented Architectures
June 26, 2008 at 01:56 AM
I've always liked to build systems with a bunch of small apps that talk to each other through various protocols. Orion and I built TrustCommerce in this manner, and that gave it some pretty impressive fault-tolerance and scalability.
I had heard the term SOA (service-oriented architecture), but had always dismissed it as enterprisey talk. (Bland-yet-pompous three-letter acronyms make my brain turn off.)
At some point, it dawned on me that what I like to do - build small apps communicating with each other over the network - is exactly what SOA means. In its modern incarnation, service architectures use REST calls, which follows the unix tradition of small sharp tools, loosely coupled by a simple but extremely flexible protocol.
Heroku is no one app - even aside from all the server software and configs, our code is currently split among around two dozen apps, each with their own repository, and most with their own database. (Some have no database.) Most of these are Rails apps, though some are bare Ruby on a Mongrel or bare Rack on a Thin, and some are Sinatra apps.
This is why my Railsconf talk was about HTTP routing. When you've got dozens of apps, some of which respond to complex domains (for example, edit.*.heroku.com), a powerful http router outside your application VM becomes damned near indispensable.
Service architectures are the solution to the longtime problem of apps growing to monstrous proportions. Once you exceed a couple dozen models and/or controllers, it starts to be very hard for new developers to grock everything that's going on, and the barrier of entry becomes very high.
With a service architecture, each app has a simplicity that's reminiscent of the "make your own blog" sample apps you see in tutorials. Rails seems to better retain its beauty in this state. Probably everything does.
But it introduces new challenges. You've got dependencies between repositories - any time you change the interface, you have to be sure to roll out the server and client apps together. The relationship between internal apps is thus very similar to external ones - you need versioning and dependency management. Heroku has an app we call our architecture atlas which tracks all the components, dependencies between them, and documents their APIs.
Managing authentication becomes a big job. We do a lot with custom HTTP headers on this (again, one of the main topics in my Railsconf talk), but I've got my eye out for even more sophisticated solutions. OAuth is one that has piqued my interest.
Perhaps the hardest question is where to draw the dividing line between one app and the next. Does a given model go in app A or app B? And that requires a lot of hard thinking about your design. An app should do just one thing, and without having to touch other apps too much. This often means splitting an app apart, and occasionally fusing two apps back together. This is the same process as managing object classes within an app: as each item's responsibility within the architecture changes, code moves around.
I find it useful to think of each internal component as its own service that could potentially be spun off as its own company. If it has its own code repo, database, tests, API, and docs, then turning it into a standalone service would just be a matter of giving it a slick marketing name and putting up a website. It's not that you'd want to do this, mind you: but if your service architecture is designed well, it would be easy to.
Git Submodule
June 25, 2008 at 12:09 AM
Git submodules are pretty cool, except for kind of sucking. Things I don't recommend doing if you value your sanity:
- Switching a submodule from one repository to another (i.e., editing .gitmodules and changing the repo url)
- Switching a directory from a submodule to regular content
- Switching a directory with regular content to a submodule (though this might help you)
It's a shame, because submodules are pretty handy. But you'll probably end up wanting to do one of these things during the lifetime of your project, and then you're screwed.
Recruiting
June 23, 2008 at 02:39 PM
Once or twice a week, I get an email from a recruiter looking to hire a Ruby developer. I can spot these within the first half a sentence, and delete them without reading the rest. Obviously they got my name someplace and didn't stop to notice that I'm the founder of VC-backed startup and am by no means looking for employment. I suspect that most other Rubyists get the same sort of emails, and the better know you are, the more you get.
These emails are the waste product of an inefficient recruiting system. There are tons of Rubyists out there that very much want a day job using the language they love. There are also tons of great companies, big and small, who very much need to hire said Rubyists. But there's no good mechanism for making those matches efficiently.
The first generation of web-based recruiting technology (monster.com, dice.com) tried to solve this in a straightforward way. If it's just a search problem, then throwing a bunch of job postings and resumes online with keywords and parameters like years of experience should do the trick, right? Turns out - no, not in the slightest. I used these sites a few times in hiring for the first company I founded, and they were borderline useless. Turns out that making a hacker <-> company match is way harder than just "you need code, I need a paycheck, let's connect."
This is a nearly identical problem to the one faced by dating sites. Again, you've got millions of people out there who want to make some sort of romantic connection with another person. But the parameters aren't as straightforward as it seems. Posting an ad on a dating site which states "I am a heterosexual male, seeking a heterosexual female as a mate" probably won't get you a lot of response. Even though there are in fact millions of heterosexual females out there looking for a heterosexual male mate.
You might think that this just a matter of needing more parameters. For dating, that's stuff like smoker or non, drinker or non, age, photos, hobbies, and favorite movies. For hiring, it's skills (languages and tools), years of experience, and keywords like "self-managing" or "enterprise" or "agile." This stuff certainly helps, but it's not enough - not enough by far. A dating site may make what seems like a perfect match, but more often than not, no sparks fly when the people meet face-to-face. A recruiting site can also fit based on quantitative parameters, but then the moment you sit down to start the interview, you discover something like that the energy level of the candidate's personality is a total mismatch with that of the company's engineering team.
In dating, we call this bit of magic "chemistry." If you don't have chemistry with someone, it doesn't matter how many hobbies you share. In hiring, we call this magic bit "culture." If the hacker's culture doesn't match with his employer, it doesn't matter whether the hacker's years of experience with a certain technology and desired salary are a perfect match with employer's open position.
So what is the next-generation solution? Recruiters, in my opinion, are nearly worthless. I've experienced using them on both sides and while they do occasionally make a good match, as near as I can tell that's usually blind luck. Yet they get paid a huge amount - $10k - $20k/yr for the length of the employment is common. I don't think that the value they provide is really on par with this price; and I think both employer and employee would be much happier if that money could go into the employee's salary instead of to the recruiter. This particular avenue seems like a dead-end to me.
So how about a next-generation technology solution? There's Catch the Best, which is a tool to help manage the screening process. And there are two startups in my Y Combinator session, Snaptalent and Joberator. Snaptalent seems to understand the importance of culture, and have a lot of features like embedded video to try to help convey culture - like in this job listing for Anybots. They are also not offering a generalized search solution, but instead only expose the ads on targeted sites - blogs related to the particular industry or area of interest of the available position.
This only addresses one side of the problem, though. The other problem is that top people are never actually out looking for new employment. (This insight comes by way of Joel Spolsky, in a rare recent moment of relative lucidity.) Using myself as a data point, this seems correct: I've never once been on the job market. (Although since most of my career has been as a founder, I may not be representative.) Back in my employee days, I never really went looking for a job. I'd just start to get disillusioned with, or bored of, my current job. Then a friend or former co-worker would get hired someplace else and talk me into coming with them. So it was a matter of being solicited at exactly the right time.
This is why recruiters have the traction they do: they go out and bug people who, 99% of the time, are annoyed at being bugged. But 1% of the time it spurs them into action, even though they may not have been quite at the point of wanting to go start scanning job ads yet. Surely we can come up with a recruiting process which reduces that wasteful 99%.
RubyGems 1.2
June 23, 2008 at 01:38 PM
The new RubyGems doesn't update the index every time, so gems install very quickly. Add the —no-rdoc —no-ri options and they install instantly. Excellent.
RestClient 0.5
June 21, 2008 at 02:56 PM
gem install rest-client for new features:
- SSL support
- User/password embedded in the url (e.g. https://joe:mypass@example.com)
- Subresource nesting with [] syntax (e.g.
site['posts/1/comments'].get) (more examples) - Better exception classes with access to the response object and more readable output in irb
Thanks to Pedro Belo and Ardekantur, who contributed most of the new code in this release.
Rack, and Why It Matters
June 19, 2008 at 05:37 PM
Rack is one of the most important developments in the Ruby web space in the past year. I suspect it's been slow to get attention because the benefits are a bit subtle. Witness the Rails core team being confused about Rack just a few months ago. So if you don't get what the deal is with Rack, don't feel bad - you're in good company.
James covered Rack in his Railsconf talk, partially at my insistence. (His talk was about Mongrel handlers, but Rack middleware is a newer and better way to achieve the same end.) It's worth noting that he asked the crowd - a couple hundred Rubyists - whether they had heard of Rack, and almost every single hand went up. But when he asked if they knew what it was for, not a single hand was raised.
So what's the deal with Rack? In short, Rack provides a standard interface between the web app server and the app framework. This is useful in light of the multiplying number of web app servers (Webrick, Mongrel, Thin, Ebb...) and frameworks (Rails, Merb, Sinatra, Ramaze...). A standard not only reduces the amount of code the framework authors have to write, but it makes the layers in the stack more pluggable. Pluggability encourages experimentation (which means more innovation over time), and generally makes the whole stack more robust.
One implication of Rack is that you can skip the app framework altogether. I've always liked using use standalone Mongrels running tiny Ruby apps without a framework for internal daemons. These days, I generally use Sinatra for that purpose - but there's still something cool about skipping the use of any framework and just coding down to the metal.
Want to try it out yourself? Stick this code into hello.ru:
class HelloHandler def call(env) [ 200, { 'Content-type' => 'text/plain' }, 'hello, world' ] end end run HelloHandler.new
Then at the shell:
rackup hello.ru & curl http://localhost:9292/
Congratulations, you've just made a frameworkless Ruby web application in five lines of code.
A Rack handler is anything that can respond to the call method and returns an array with the status code, output headers, and output body. Handlers can be the end of the request chain, or do input and output filtering anywhere in the middle (hence "middleware"). Here's an example from Marc-André Cournoyer. Though his example is presented for Thin, you can run his code on any Rack-compatible server.
In the real world, what is Rack middleware useful for? We recently ported the Heroku toolbar to Rack middleware. The previous implementation was several hundred lines of very hard-to-follow monkeypatching of ActionController, combined with a rarely-used and poorly-maintained plugin framework for Mongrel call GemPlugins. (Which I nominate for Most Confusing Name Ever.) That code was hard to read and nearly impossible to spec, but it's the only way we could make it work with the traditional Mongrel/Rails setup. It was also very tightly coupled to a particular version of Rails and Mongrel.
Ricardo (one of the new Heroku devs) banged out the Rack middle port in just a couple of days. It's a fraction the number of lines of code, and can be speced normally. Plus, our toolbar is now compatible with any Ruby app server or framework.
Because Rack separates the layers of the stack more cleanly, it was way easier to hook the Heroku toolbar code into the right place. Take that lesson and generalize it, and you'll start to glimpse the significance of Rack.
Battling Wedged Mongrels with a Request Timeout
June 17, 2008 at 12:06 AM
The dreaded "wedged Mongrel" - your app server stuck on one request, with others piling up, waiting infinitely for it to come free - is a problem all production Rails apps face sooner or later. The solution most commonly used is to restart the app servers frequently, via something like Monit, or just on a cron job.
But such solutions are just a band-aids which hide the real problem, which is that your code is getting stuck in an infinite loop, or waiting on an IO request which never returns. A better solution is to wrap all your actions in a timeout:
class ApplicationController < ActionController::Base around_filter :timeout def timeout require 'timeout' Timeout.timeout(30) do yield end end end
This prevents the wedged app server. And combined with an exception notifier, you'll be able to see which requests are getting wedged, so that you can fix your code. (Periodic app server restarts are still needed to combat memory leakage - another problem entirely.)
I'm surprised that request timeouts aren't a standard part of web frameworks like Rails, application servers like Mongrel, or both. (If you've seen the "timeout" parameter for Thin or Mongrel, don't be fooled - it's not that kind of timeout.) After all, web requests aren't supposed to be long-lasting. Nginx or Apache will time out the request after 90 seconds or so anyway, but this doesn't stop your app server from grinding away infinitely on the request.
But there's a catch with Timeout. It uses Ruby threads, which only works as long as it's Ruby code that's getting stuck or taking too long. The second case - a system call that's getting stuck - is often the problem. So this will time out:
Timeout.timeout(3) do sleep 4 puts 'done' end
...but this will not:
Timeout.timeout(3) do system 'sleep 4' puts 'done' end
Good unix jockeys know that SIGALRM is the correct solution here. Back in my MUD days I encountered this technique in the CircleMUD server: it would detect infinite loops and abort with a log message, allowing the game to continue running. "Wow," I said the first time I saw it in action. "How does it know?" That's the magic of SIGALRM.
Philippe Hanrigou and David Vollbracht have implemented a SIGALRM solution for Ruby in the form of SystemTimer. (They also give a great description of green threads and why they don't play well with the underlying OS.) This is a nearly drop-in replacement for Timeout. Try it:
SystemTimer.timeout(3) do system 'sleep 4' puts 'done' end
Woot! So now, your final solution for preventing wedged app servers in production:
class ApplicationController around_filter :timeout def timeout require 'system_timer' SystemTimer.timeout(30) do yield end end end
Cloud Computing Taxonomy
June 16, 2008 at 04:28 PM
Most agree that cloud computing is the Next Big Thing, but beyond that things get murky. Being such a new space means that there's not yet a consensus on what all the pieces are, and how they fit together.
Michael Crandell gives a good descrption of what he considers to be the three tiers of cloud computing: apps, platforms, and infrastructure. (He correctly puts Heroku on the middle tier.)
It gets a bit harder as you try to subdivide each tier. This diagram is one I draw a lot these days, but it's a little different each time, as we continue to discover new challenges about our slice of the cloud computing pie.
Quickstart to Hacking Rubinius
June 12, 2008 at 01:38 AM
I recently tried my hand at hacking on Rubinius. Here's a rough description of what I did - following this same pattern should let any skilled Ruby developer be contributing patches in no time.
First:
git clone git://git.rubini.us/code rubinius cd rubinius rake spec:update rake build
This will take a while to finish. Once it's built, you can run Ruby scripts by using shotgun/rubinius [filename], instead of ruby [filename]. You can also run it with no arguments to get an interactive shell (i.e., irb). Put this alias into your bashrc:
alias rbx=/home/adam/rubinius/shotgun/rubinius
...and then you can run rbx instead of ruby from anywhere.
Install gems:
rbx gem install [gemname]
I kicked things off by running the specs on my own open source Ruby libraries, once I had installed the rspec and other dependency gems. RestClient passed fine, so I moved on to rush, which is much more complex. Running individual specs, e.g.:
rbx rush/spec/dir_spec.rb
...I soon found some that broke. Since I know these specs pass normally, this served as my launchpoint for how I could improve Rubinius.
Rubinius was throwing an exception in popen, which was only partially implemented. But before trying to fix it, the first step was to write a spec. Rubinius uses mspec, which an RSpec-style specing library. I ran the popen spec like this:
bin/mspec spec/ruby/1.8/core/io/popen_spec.rb
This runs it on Rubinius. To run it on MRI (aka Ruby 1.8), use:
bin/mspec -t ruby spec/ruby/1.8/core/io/popen_spec.rb
Write the spec to pass on MRI first. Then check to see if it passes on Rubinius. If not, you'll need to tag it as failing. Here's my first committed spec, and notice that the second file is popen_tags.txt, which marks the read/write pipe as failing.
All specs should pass on MRI at all times. But it's entirely reasonable to write a spec that passes MRI and fails on Rubinius - this makes evident a missing feature in Rubinius.
Once I had the spec which passed on MRI but failed on Rubinius, I could now turn my attention to making the spec pass. The code I tinkered with was in kernel/core/io.rb. It relies on a primitive create_pipe (via IO.pipe), which is actually a very simple C function that can be found in shotgun/lib/primitives.rb.
In the process of working on the main issue you will most likely discover small ones. Take the opportunity to write a spec, and fix Rubinius. But if you can only do one or the other (write the spec, or fix a failing spec), that's great too. This two-phase process (expose the problem, then fix it) works extremely well for mapping out the complex problem space of writing a language VM, particularly in trying to track an implementation that has no formal specification.
One thing that triped me up was that you must run rake to rebuild after any code change - even if all the code you changed was Ruby. I'm not used to having a compile step on an interpreted language, but once I got into the habit it was easy to remember.
For submitting the patch, put it into a pastie and post it on Lighthouse. Most developer communication happens in the IRC channel, so you may want to start by posting your pastie there and soliciting feedback.
I was really impressed by the receiptiveness of the Rubinius developers to new hackers. Even while critiquing my patches on IRC, they took every opportunity to let me know how much they appreciated my contribution. From what I've seen, these guys are setting a new bar on making a low barier of entry for people to jump in and contribute. I hope to see other open source projects follow their good example.
Further reading:
yaml_db and heroku-client in Github
June 11, 2008 at 02:40 AM
Not big news, but a few folks have bugged me recently to make it easier to contribute patches to some of our gems and plugins. So here you are: the Heroku client gem and yaml_db.
Sinatra, My New Favorite Microframework
June 10, 2008 at 02:19 PM
A few months ago, I went in search of a way to build an extremely lightweight Ruby web app. Merb can be stripped down pretty far, but I wanted a true microframework. Ramaze and Camping were getting close, but didn't quite fit my taste. Then I discovered Sinatra.
Sinatra apps are typically written in a single file. It starts up and shuts down nearly instantaneously. It doesn't use much memory and it serves requests very quickly. But, it also offers nearly every major feature you expect from a full web framework: RESTful resources, templating (ERB, Haml/Sass, and Builder), mime types, file streaming, etags, development/production mode, exception rendering. It's fully testable with your choice of test or spec framework. It's multithreaded by default, though you can pass an option to wrap actions in a mutex. You can add in a database by requiring ActiveRecord or DataMapper. And it uses Rack, running on Mongrel by default.
One of the most important backend services for Heroku is written using Sinatra. We're now running several hundred instances of it in our cluster. It's performed like a champ - I haven't seen it die or leak memory, other than bugs in our app code.
Some interesting (though not necessarily meaningful) stats.
Lines of framework code (not counting tests or examples)
| Rails | 87,990 |
| Merb-core | 12,417 |
| Ramaze | 11,796 |
| Camping | 1,704 |
| Sinatra | 1,576 |
require 'sinatra' pulls in just one file: sinatra.rb. Now that's a commitment to small.
How about memory footprint? Camping takes the crown here, but Sinatra doesn't do too shabby:
Memory footprint of an empty application
| Rails | 52MB |
| Merb-core | 25MB |
| Ramaze | 18MB |
| Sinatra | 16MB |
| Camping | 7MB |
Aside: I got these numbers using the Linux free command before and after starting the server. Gauging real memory usage is very difficult because of shared pages, but free is much better than the VSZ/RSZ silliness you see in ps, which don't tell you very much.
But my real joy in Sinatra is its minimalist simplicity. The direct mapping of URLs to code (routes? who needs 'em?), the incredible ease of writing tests, and even just the simple fact that the return value from an action is its output. For example, an action might look like:
get '/posts/:id.xml' do Post.find(params[:id]).to_xml end
And a matching test:
should 'get a post in xml format' do Post.expects(:find).with('123') get_it '/posts/123.xml' end
When I return to writing Rails apps after working on Sinatra for a while, I sometimes find myself thinking: "wait, what did I need all this other crap for again?"
Railsconf Wrapup
June 08, 2008 at 02:54 PM
Whew, I think my brain has finally returned from its liquified state after Railsconf. Last year the conference felt like a vacation, since all I did was attend. This year, with all the speaking and booth-manning and meetings, it was pretty grueling.
Despite that, it was still plenty of fun. For example, due to some capacity issues at the convention hall, my talk got moved into the keynote room. So I got to feel like a big shot for an hour up on the big stage with all the banners and lights. :)
Tim Goh posted an excellent blow-by-blow of my talk. Apparently he manually keyed in all the code from my slides during the presentation, which is damn impressive because I was going through them pretty quickly!
We answered about a zillion questions about Heroku at our booth (not to mention just people stopping us in the hallway). This involved lots of waving our arms around and drawing on our plexiglass wipeboard.
Talking to so many people about what we're doing brought a few things into focus for me. The main one is that not many people really get what we're doing. Questions I answered over and over: No, we're not a web-based IDE. No, we're not a reseller of EC2. No, we're not a competitor of Engine Yard.
My best answer to the "what is Heroku?" question is as follows: Heroku is a automated deployment platform for Rails. "Automated" means you don't think about server stuff at all: just load up your code and go. "Deployment platform" means a place to run your app - which could be while you're developing on it, could be a staging/prototype/demo-for-the-client deployment, or could be a full production deployment.
Going forward, the guys and I are going to be thinking about ways to make our message clearer. I guess that's one of the challenges of using a blue ocean business strategy: when you're pioneering a completely new space, explanations are hard.
Thanks to the conference organizers for another great event; to everyone who came to my talk; to everyone who approached us about partnership opportunities; and to all the excellent speakers. And to all the new friends I made, I hope to see you again soon (maybe at Rubyfringe?).
Railsconf Slides
June 03, 2008 at 10:19 PM
Here's my slides from Railsconf. More thoughts to come once I recover a bit more - it was pretty intense for my partners and I this year, what with doing three talks and manning a booth.
Someone in the session asked to see get_logged_in_user() (called from the code shown in slide 36). Here it is, in all its C string-manipulation glory:
void get_logged_in_user(ngx_http_request_t *r, u_char *user, int user_size) { ngx_table_elt_t **cookies; ngx_table_elt_t *elt; char cookie[256] = ""; int i; cookies = r->headers_in.cookies.elts; for (i = 0; i < (int)r->headers_in.cookies.nelts; i++) { elt = cookies[i]; if (extract_and_overwrite_cookie((char *)elt->value.data, "heroku_session=", cookie, sizeof(cookie))) break; } if (cookie[0] != 0) find_user_by_cookie(cookie, (char *)user, user_size); } void find_user_by_cookie(const char *cookie, char *email, int size) { char sql[256], scratch[128]; snprintf(sql, sizeof(sql)-1, "SELECT username FROM sessions WHERE cookie='%s'", pg_escape(cookie, scratch, sizeof(scratch))); pg_select_one_string(sql, email, size); }
Also, one correction: I incorrectly stated that redirect() was an Nginx function. It's actually a helper function I created; here's the code.
void redirect(ngx_http_request_t *r, char *url) { location = ngx_palloc(r->pool, strlen(url)); r->headers_out.location = ngx_palloc(r->pool, sizeof(ngx_table_elt_t)); ngx_copy(location, url, strlen(url)); r->headers_out.location->value.data = location; r->headers_out.location->value.len = strlen(url); r->headers_out.content_length_n = 0; r->header_only = 1; r->keepalive = 0; }
If you use this, make sure to return NGX_HTTP_MOVED_TEMPORARILY immediately after calling it, as shown in the slides.


