Antelope Canyon (Taken with instagram)
Pendleton Round-Up (Taken with instagram)
I was a little surprised to discover that GitHub doesn’t allow you to fork your own projects as simply as you can fork someone else’s (if you don’t believe me, check out this post). Fortunately it’s pretty simple as long as you can come up with a different name for your new project.
Let’s assume we have an existing project called ‘foo’ and we want to create a fork called ‘bar’.
First, create a new project on GitHub called ‘bar’.
Next, clone foo using the name ‘bar’:
$ git clone git@github.com:YOURNAME/foo.git bar
Initialized empty Git repository in /projects/bar/.git/
remote: Counting objects: 3618, done.
remote: Compressing objects: 100% (1503/1503), done.
remote: Total 3618 (delta 2629), reused 2922 (delta 2075)
Receiving objects: 100% (3618/3618), 7.31 MiB | 1.23 MiB/s, done.
Resolving deltas: 100% (2629/2629), done.
Next, edit your Git config file and replace the origin URL with your new URL:
$ cd bar
$ vim .git/config
[remote "origin"]
fetch = +refs/heads/*:refs/remotes/origin/*
url = git@github.com:YOURNAME/bar.git #replace foo with bar
Optionally add your original repo as an upstream source:
$ git remote add upstream git@github.com:YOURNAME/foo.git
Finally, push your new repository up to GitHub:
$ git push -u origin master
Counting objects: 3618, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (949/949), done.
Writing objects: 100% (3618/3618), 7.31 MiB | 646 KiB/s, done.
Total 3618 (delta 2629), reused 3618 (delta 2629)
To git@github.com:YOURNAME/bar.git
* [new branch] master -> master
Branch master set up to track remote branch master from origin.
Now you can push/pull from your new repo (bar) as expected. You should also be able to merge upstream changes using the following command (although I haven’t tried this yet):
$ git fetch upstream
$ git merge upstream/master
And that’s really all there is to it. :-)
Old Linoleum (Taken with instagram)
Launching Banterly last week was a thrill. It was unbelievably fun meeting so many new people from all over the world through my own personal creation. Everyone was so friendly and supportive and I honestly loved every minute of it (even the parts where I was frantically fixing bugs). It was an experience I won’t soon forget and hope to repeat.
Not long after I launched, the guys at Embedly started a discussion suggesting that I check out their oEmbed service. The moment I saw it, I knew it was exactly what I had been looking for. The only annoying part was that I hadn’t discovered it several weeks ago when I first started working on embedded media in Banterly. So, for that reason, I decided to do a little demonstration and offer a little praise in hopes that others looking for a solution to their embedding woes might not waste time trying to roll their own (like I did).
Don’t DIY
Let’s start with what NOT to do. Don’t try to do media embedding yourself. For starters, there are simply too many different sites out there, each with slightly different methods of exposing their data (Embedly supports more than 160 providers for free or 185 for a reasonable subscription fee). Doing it myself, it took two days to get support for images, YouTube, and the most simple OpenGraph meta tags (which was extremely flimsy since no complete HTML parsers exist for Node yet).
Additionally, doing it yourself means downloading unknown content from unknown sources at the whim of your users—not exactly the kind of thing you want to expose yourself to when all you want is to embed content on your site. In fact, I almost cut the feature before launch because I was afraid malicious users might discover it and use it to abuse the system, but then I thought—‘why not?’ and left it in anyway.
embedly-node
You can use Embedly to embed media from both server side and client side (via jQuery). I chose to use the server side module (embedly-node) in order to reduce the number of requests I have to make to the Embedly API and to keep page load times fast for users. Using the embedly-node module on GitHub (use npm install embedly), I ended up with something similar to the following:
var Embedly = require("embedly");
var embedly = new Embedly.api({
//key: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXX", // Embedly Pro Key
user_agent: 'Mozilla/5.0 (compatible; myapp/1.0; me@example.com)'
});
// Do initial comment text processing
// 1. Parse URLs from comment text
// 2. Render HTML comment by replacing parsed URLs with anchor tags
// 3. Save rendered comment HTML to the database
// 4. Send rendered comment HTML to connected clients (via WebSocket)
...
// 5. Try to get additional content from Embedly
if (urls) {
// Using Embedly oEmbed API
embedly.oembed({
params: {
urls: urls // Can send single URL also.
},
complete: function(e, objs) {
for (var i in objs) {
// Returned if the URL content is not supported on free tier
if (objs[i].type == "error") continue;
// Some embed types don't contain a URL
objs[i].url = objs[i].url || urls[i];
// Render and replace the previously rendered link
html = html.replace(getLink(urls[i]), render(objs[i]));
}
// Only save and send to clients if the HTML has changed
if (comment.html != html) {
// Update the rendered HTML in the database
comment.html = html;
comment.save(function() {
// Send updated comment HTML to connected clients (via WebSocket)
...
});
}
});
}
Sending the initial HTML to connected clients immediately (rather than waiting for Embedly) helps keep the UI fast and responsive while users are chatting. Once Embedly returns, new HTML is sent out to clients to replace the initial comment markup. In my tests, I found Embedly to be quite fast at returning embed data, but its speed will always be limited by how fast the URL host can return its own content, so it’s much more reasonable to keep the updates separate.
A simplified version of my render function looks like this (would be admittedly much cooler with a template):
function render(obj) {
if (obj.type == "photo") {
// Just an image (only works with pro account)
// image dimensions are available as obj.height and obj.width
var html = "<img src='"+obj.url+"' width='500'/>";
return html;
} else if (obj.type == "video") {
var html = "";
// Add thumbnail
if (obj.thumbnail_url)
html += "<a href='"+obj.url+"' target='_blank'><img src='"+obj.thumbnail_url+"' width='100'/></a>";
// Add title link
if (obj.title)
html += "<a href='"+obj.url+"' target='_blank'><h6>"+obj.title+"</h6></a>";
// Add an explicit link
html += "<a href='"+obj.url+"' target='_blank'>"+obj.url+"</a>";
// Add description
if (obj.description)
html += "<p>"+obj.description+"</p>";
// Wrap it up
return "<div class='oembed'>"+html+"</div>";
}
// Just a normal link
return "<a href='"+obj.url+"' target='_blank'>"+obj.url+"</a>";
}
In addition to oEmbed, there are also ‘preview’ and ‘objectify’ which I have yet to explore (maybe I’ll follow up if I do) but are only available for Pro Embedly users.
Thanks guys!
I also think it’s worth mentioning how helpful the folks at Embedly were during the process (especially Bob Corsaro aka @doki_pen). Throughout the experience, they were extremely available (chatting on Banterly and exchanging emails late into the night). They even fixed a bug I found within minutes of reporting it!
I’ve been working on Banterly ever since I caught the startup bug at Startup School 2010. It started as a simple last minute idea to apply to Y Combinator, but has since evolved into something much more (despite not being accepted). If you haven’t seen Banterly yet, go ahead and take a few minutes to check it out. Here’s a link even: http://banter.ly
~ Intermission ~

Either you saw the announcement from Convore (http://convore.com) the week before last, or you don’t follow startups. If you’re in the latter group, you should probably just skip the rest of this and go start chatting on Banterly (or I suppose…Convore). If you’re in the former, you may have taken one look at Banterly and thought “meh…this is just Convore with less features.” But of course, we hope you thought “Sweet! This is what Convore should have been.”
Regardless, if you’re an entrepreneur like me, you can probably relate to my story. While it’s normal to question any idea over the course of its development, it’s also often a great source of agony and stress for would-be founders. Second guessing yourself is difficult, but even more difficult after getting rejected by Y Combinator.
However, after experiencing all of the ups and downs of startup-idea self-examination, there’s one thing that’s even more agonizing: being beaten to the punch. When I saw the announcement from Y Combinator about Convore less than two weeks ago, I was shattered.
In researching the space over the months we had discovered many competitors, so discovering someone else was nothing new. What made Convore different was that it was actually GOOD, it had buy-in from Paul Graham & Co., and seemed to get instant respect from people in the know—all things I felt needed to be in place to make something like Banterly successful.
Discovering that your ‘revolutionary’ product is suddenly a ‘me too’ product is a bitter pill to swallow. There’s nothing like having your idea validated and taken away from you at the same time.
Somehow I resisted the urge to abandon Banterly (not easy as easy as it sounds) and have been working around the clock ever since to get something presentable ready anyway. Banterly, as we’re releasing it today, is not perfect and has no shortage of bugs (which I am sure will pop up once people start using the service in greater numbers). I am the sole programmer working on it and have only limited time for it (not to mention learning Node.js + MongoDB—both new platforms for me).
So my question to the community is this: What would you do if you were in my shoes? Would you cut your losses and move on to a new idea (we discovered several while working on Banterly)? Would you ignore Convore and go for it? Would you open source the code?
I also want to add one more thing. We have nothing but respect for the folks at Convore and we think their service is fantastic. Even if we end up moving on from Banterly, we’re really happy that Y Combinator decided to support such an awesome team with such a great idea.
Way to go—Sebadoh! (Taken with instagram)
I’m afraid the honeymoon with Node.js may finally be over. That is not to say that I’m not still enamored with it—just that I’ve encountered a few challenges that weren’t there in the beginning. These are mostly due to the fact that Node is still a very very young project and much of the support community around it is still emerging. The good news is that it is emerging faster than I think anyone expected.
node-openid
So I needed OpenID support for my project. A Google search revealed a few incomplete implementations on GitHub. So I did what any respectable open source community member would do. I started writing one.
Lucky for me, I didn’t have to spend much time on my version, because another respectable community member (@havard) released his own version (node-openid) just as I was getting mine working with Google. This version was much more complete and true to the spec than I had time for—so really a win for both me and the community.
Attribute Exchange
As frustrating as it can be to discover a large missing feature from your platform, what’s even more frustrating is finding that the implementation doesn’t work for your needs. I’m not sure how common this is, but Google additionally allows consumers to request additional properties through OpenID Attribute Exchange, which in this case get piggybacked on top of the main OpenID request. Since I needed the user’s email address as well, and was only interested in Google, I needed the node-openid to work for this case.
I briefly considered putting some time into adding Attribute Exchange to node-openid before realizing that it was not a trivial amount of work. The good news is there is an easy way to modify node-openid (you can even just use this patch) so that you can send your own additional parameters with the authenticate request. With the patch applied, your request might look something like this:
var openid = require("openid");
openid.authenticate.defaultParams["openid.ns.ax"] = "http://openid.net/srv/ax/1.0";
openid.authenticate.defaultParams["openid.ax.mode"] = "fetch_request";
openid.authenticate.defaultParams["openid.ax.required"] = "email";
openid.authenticate.defaultParams["openid.ax.type.email"] = "http://axschema.org/contact/email";
openid.authenticate(
"http://www.google.com/accounts/o8/id",
"http://example.com/verify",
null, false,
function(url) {
res.writeHead(302, { Location: authUrl });
res.end();
}
);
Strange Queries
But even that’s not quite enough to get it working. That’s because there are two active releases of Node.js at the moment. They are version v0.2 and v0.3. The latest version (v0.3) does not yet support HTTPS when making client requests (but is apparently being worked on). This means it is impossible to use OpenID to authenticate with Google (and likely others) because Google only provides an HTTPS endpoint for OpenID authentication.
The older version (v0.2) does include HTTPS support but uses a strange parameter parsing strategy that actually tries to parse parameter names with dots (‘.’) to create a containment hierarchy. This strategy sounds nice on paper, but causes lots of problems when clients send requests that use dots but don’t conform to the rules, as is the case with OpenID. While you’d expect the parser to silently fail or ignore the bad parameters—instead it throws an exception and the request dies making it impossible to even work around.
For example, in Node.js v0.2 requests that look like this will always fail:
openid.ns=foo&openid.ns.ext1=bar
So I submitted this patch which simply prevents the exception from occurring and puts the offending parameters in a special variable called __str__. Unfortunately, since v0.2 is on its way out, the nodejs-dev guys won’t be including this patch, so you’ll have to patch manually. In any case, with the patch applied, the query string above will result in this:
{"openid":{"ns":{"__str__":"foo","ext1":"bar"}}}
And that’s about it. Break over. Time to code again.
I now own this. I am a dork. (Taken with instagram)
Ice is nice (Taken with instagram)