Search Results: "decklin"

5 April 2017

Steinar H. Gunderson: Nageru 1.5.0 released

I just released version 1.5.0 of Nageru, my live video mixer. The biggest feature is obviously the HDMI/SDI live output, but there are lots of small nuggets everywhere; it's been four months in the making. I'll simply paste the NEWS entry here:
Nageru 1.5.0, April 5th, 2017
  - Support for low-latency HDMI/SDI output in addition to (or instead of) the
    stream. This currently only works with DeckLink cards, not bmusb. See the
    manual for more information.
  - Support changing the resolution from the command line, instead of locking
    everything to 1280x720.
  - The A/V sync code has been rewritten to be more in line with Fons
    Adriaensen's original paper. It handles several cases much better,
    in particular when trying to match 59.94 and 60 Hz sources to each other.
    However, it might occasionally need a few extra seconds on startup to
    lock properly if startup is slow.
  - Add support for using x264 for the disk recording. This makes it possible,
    among other things, to run Nageru on a machine entirely without VA-API
    support.
  - Support for 10-bit Y'CbCr, both on input and output. (Output requires
    x264 disk recording, as Quick Sync Video does not support 10-bit H.264.)
    This requires compute shader support, and is in general a little bit
    slower on input and output, due to the extra amount of data being shuffled
    around. Intermediate precision is 16-bit floating-point or better,
    as before.
  - Enable input mode autodetection for DeckLink cards that support it.
    (bmusb mode has always been autodetected.)
  - Add functionality to add a time code to the stream; useful for debugging
    latency.
  - The live display is now both more performant and of higher image quality.
  - Fix a long-standing issue where the preview displays would be too bright
    when using an NVIDIA GPU. (This did not affect the finished stream.)
  - Many other bugfixes and small improvements.
1.5.0 is on its way into Debian experimental (it's too late for the stretch release, especially as it also depends on Movit and bmusb from experimental), or you can get it from the home page as always.

2 February 2017

Steinar H. Gunderson: Not going to FOSDEM but a year of Nageru

It's that time of the year :-) And FOSDEM is fun. But this year I won't be going; there was a scheduling conflict, and I didn't really have anything new to present (although I probably could have shifted around priorities to get something). But FOSDEM 2017 also means there's a year since FOSDEM 2016, where I presented Nageru, my live video mixer. And that's been a pretty busy year, so I thought I'd do a live cap from high up above. First of all, Nageru has actually been used in production we did Solskogen and Fyrrom, and both gave invaluable input. Then there have been some non-public events, which have also been useful. The Nageru that's in git right now is evolved considerably from the 1.0.0 that was released last year. diffstat shows 19660 insertions and 3543 deletions; that's counting about 2500 lines of vendored headers, though. Even though I like deleting code much more than adding it, the doubling (from ~10k to ~20k lines) represents a significant amount of new features: 1.1.x added support for non-Intel GPUs. 1.2.x added support for DeckLink input cards (through Blackmagic's proprietary drivers), greatly increasing hardware support, and did a bunch of small UI changes. 1.3.x added x264 support that's strong enough that Nageru has really displaced VLC as my go-to tool for just video-signal-to-H.264-conversion (even though it feels overkill), and also added hotplug support. 1.4.x added multichannel audio support including support for MIDI controllers, and also a disk space indicator (because when you run out of disk during production without understanding that's what happens, it really sucks), and brought extensive end-user documentation. And 1.5.x, in development right now, will add HDMI/SDI output, which, like all the previous changes, requires various rearchitecting and fixing. Of course, there are lots of things that haven't changed as well; the basic UI remains the same, including the way the theme (governing the look-and-feel of the finished video stream) works. The basic design has proved sound, and I don't think I would change a lot if I were to design something like 1.0.0 again. As a small free software project, you have to pick your battles, and I'm certainly glad I didn't start out doing something like network support (or a distributed architecture in general, really). So what's for the next year of Nageru? It's hard to say, and it will definitely depend on the concrete needs of events. A hot candidate (since I might happen to need it) is chroma keying, although good keying is hard to get right and this needs some research. There's also been some discussion around other concrete features, but I won't name them until a firm commitment has been made; priorities can shift around, and it's important to stay flexible. So, enjoy FOSDEM! Perhaps I'll return with a talk in 2018. In the meantime, I'll preparing the stream for the 2017 edition of Fyrrom, and I know for sure there will be more events, more features and more experiences to be had. And, inevitably, more bugs. :-)

2 March 2016

Steinar H. Gunderson: Nageru FOSDEM talk video

I got tired of waiting for the video of my FOSDEM talk about Nageru, my live video mixer, to come out, so I made an edit myself. (This wouldn't have been possible without getting access to the raw video from the team, of course.) Of course, in maximally bad timing, the video team published their own (auto-)edit of mine and a lot of other videos on the very same day (and also updated their counts to a whopping 151 fully or partial lost videos out of a bit over 500!), so now there's two competing ones. Of course, since I have only one and not 500 videos to care about, I could afford to give it a bit more love; in particular, I spliced in digital versions of the original slides where appropriate, modified the audio levels a bit when there are audience questions, added manual transcriptions and so on, so I think it ended up quite a bit better. Nageru itself is also coming pretty nicely along, with an 1.1.0 release that supports DeckLink PCI cards (not just the USB ones) and also the NVIDIA proprietary driver, for much increased oomph. It's naturally a bit more quiet now than it was, though conferences always tend to generate a flurry of interest, and then you get back to other interests afterwards. You can find the talk on YouTube; I'll also be happy to provide a full-quality master of my edit if FOSDEM or anyone else wants. Enjoy :-)

10 January 2010

Decklin Foster: man ascii

I find myself opening a shell and typing "man ascii" more than I would like. More recently, I've found myself looking (with no success) for a equivalent web page that isn't ugly -- sometimes a browser tab is just more convenient. Yesterday, I decided to stop searching and just write one myself: man ascii. (I was a bit surprised that this domain, and all the variants of it Joker came up with, were available!) I think it gives me everything I got out of ascii(7), but I would love any bug reports.

23 October 2009

Decklin Foster: Score: 5, Privileged

(No apologies to the original.) I get the impression that the Windows 7 launch is a lot like seeing an old boyfriend suddenly show up on your doorstep wanting to get back together. He's had some work done, apparently: stomach stapling to take off some of the weight, teeth whitening, and a radical nosejob to make him look as much like your current boyfriend as medical science will allow. He's handsome, of course, almost too handsome. He still uses far too much product in his hair and carries that desperate look in his eyes. The fragrant haze around him is the cologne he overuses to mask the scent of failure. But standing there in that form-fitting t-shirt, you'd almost forget for a moment what a psycho he was- how he used to shut down in the middle of a date and forget everything you were talking about and how he was only happy when you were paying attention to him. You'd almost forget about carrying around his legacy baggage or those nights when, for seemingly no reason at all, he would simply stop speaking to you and when you asked what was wrong he'd just spit a string of hex code at you and expect you to figure it out. You complained about him for years before finally deciding to get rid of him, and here he is again. Though, somehow, he seems like a completely different person now. "I'm up here," he says when he catches you staring at his package. Tempted though you may be, you know that over time he'll get bored and slow down on you just like he always does. And then you'll be right back where you started: trapped. He keeps you by convincing you that you don't have a choice. You're just not smart enough for one option or rich enough to afford the other. "But I'm different now," he says, batting his eyes innocently. "I've changed." Indeed he has. Apparently, he's really into Kabbalah now or something like that. It's helped him discover loads of untapped potential in himself. But it also means that you'll have to buy all new furniture to fit with his understanding of feng shui. That's not the only change he has in store for you. The minute you let him move in, he'll have a new alarm system put in that succeeds only in preventing your friends from coming over on poker night. He doesn't love you, but he doesn't hate you, either. The truth is that he couldn't care less one way or the other. He's here because he doesn't want to be alone. Like all human beings, especially those well past their prime, he wants to feel wanted and, after a string of lost jobs and bad investments, he needs a place to stay. But all in all, he's OK. He's a seven. He'll do, I guess. UPDATE: There was a Hacker News thread about the Slashdot link that got killed before I could reply to someone who left a negative comment. I'll reproduce it here for the sake of anyone still scratching their head.
Thank you for replying instead of simply downvoting. There are assumptions in the structure of the original text that I don't think most of the people discussing it (here, or over there) appreciate. To wit: the narrative of technology belongs to men. Women are objects. The dumbest, easiest way to make that stick out (I did it over breakfast, ffs) is to swap the genders. It sounds "wrong". The emotional-abuse stuff is more creepy. The physical descriptions are more awkward because men aren't leered at in the same way. It's not about evening things out, it's about subverting what the text doesn't say outright by making it obvious. I am, I'm sure, as aware as you that "deconstruction" is not (anymore) a word you say to sound smart, rather a word you avoid saying to avoid sounding like you're trying to be pseudo-smart, but sometimes you just gotta call a spade a spade. You're free to disagree with that (I assume you still will), but I think "not even wrong" is a little over the top.

24 March 2009

Adeodato Sim : Microblogging

I ll take chance now that microblogging seems to be fashionable in Debian to comment this. Luk did create a while ago an Identi.ca account to send updates about his work on Debian, eg. updated chroots for goetz (official alpha buildd) . I ve created an account for myself, and intend to send there the same kind of Debian-related updates let s see how it goes. We ve also talked about setting up one for the release team as a whole, but we ll see about that. Relatedly, now that I m a tircd user and Twitter lives now on my Irssi (thanks, Decklin!), I can say it ll be easier to update my account there, which I ve had for a while. It ll be personal stuff, mainly, i.e. not intersect with the Identi.ca one. P.S.: Apologies for today s post in Spanish in Planet Debian. I realized right after Planet software had fetched it, and albeit I removed it from the feed I send to planet immediately, Planet seemed not to pick up the removal.

23 March 2009

Decklin Foster: Artificial Intelligence

After a recent apt-get:
Fetched 1B in 0s (42B/s)
Well. That settles that question! :-)

12 March 2009

Decklin Foster: Inside Out

I've been on Twitter for over a year and a half now. If I had to explain why I like it, and why it's so popular now, I would use an analogy:
Twitter : IRC :: Blogs : Usenet
(This applies equally to other "micro-blogging" services, but I am about to explain why I believe that's not the right metaphor. You may also substitute mailing lists for Usenet.) With the older media, you have a place -- a newsgroup, or a channel, that people went to, with a distinct culture, and that (mostly) weren't "owned" by anyone, but rather by the community. With the new ones, we are all sole proprietors of our own streams, and we "tune in" to the subset of people we find interesting, rather than topics we invest in. So, instead of bumping into the same person in a couple different groups, or never reading their words at all, you might find that your feeds overlap a bit more than they do with most people. This is how I find people to "follow" and blogs to read, in fact -- as my network expands, more people become loosely joined to it, and as I notice ones worth reading I add them. Is one model better than the other? Probably not. I could make an analogy to music. In theory, I'd rather read what my favorite critics have to say about a wide variety of new releases -- some of which I'd never know about otherwise -- than keep up with the discussion of bands and genres I really like, even if most people writing about them are terrible (remember, 90% of everything is crap). But I also lose that sense of community of being a "fan" of something; I no longer have a deep connection to what's going on in the fandom or the scene, which I also would never know about otherwise (some of it is just too obscure for my favorite writers to cover). In practice, it seems the new approach has been more popular, but maybe that's because more people are on the net now, and both kinds of communication are/were shaped by the technology available at the time (destinations make much more sense with limited/centralized computing resources, and aggregation makes much more sense with powerful clients and a wider, less specialized user base). Anyway. This post is not actually about social media theory or whatever you want to call it; it's about some software I have packaged. Because of all the above, I have always wished that I could use Twitter from something more like my IRC client. Like, say, my IRC client. One could abuse the concepts of IRC to make an "on the fly" channel of whoever happens to be in my feed. (I once read a blog comment somewhere complaining that Twitter could easily be implemented on any existing IRC server using one +m channel for everyone and some client-side direction of messages from all such channels to a single window. +1 for cleverness, but they did sort of miss the point of why normal people sign up for web sites rather than installing and configuring clients for obscure chat protocols.) So, I had grand plans to write a Twitter client which was an IRC server, with some clever mapping of IRC concepts and commands to their equivalents over there. I never got around to it, as I barely have enough time for anything I do now. But last month I noticed that someone else had implemented such a thing: tircd. I had seen Twitter/IRC services before, but like the official Twiter XMPP service, they were all implemented as bots, which I detest for this sort of application. Bitlbee, for example, translates various IM protocols to IRC, but only halfway -- for anything else you have to use a bot as a sort of poor man's command line. If I want a command line for Twitter, I already have several; IRC can do better. And tircd really does! It's great. You're not required to edit the config file, and there's no extra layer on top of IRC for things like logging in or adding people. I've finally packaged the latest release, which is waiting in NEW currently. I got a request for sneak preview packages, so if you want to install some unapproved .debs check this repository. I may still pick my own project back up, as Twitter itself, being a centralized service, feels like a stopgap solution on the way to to a more generalized 140-character equivalent of the, er, blogosphere as envisioned by open-source projects like Identi.ca. In the future, perhaps, when we use a certain micro-blogging "service" we might be randomly connected to one of any number of servers run by different individuals but all mirroring messages back and forth to each other. Which, now that I think of it, sounds vaguely like some obscure, obsolete chat and news-posting protocols I know.

9 March 2009

Decklin Foster: Another Monopoly Rant

A cautionary tale: Verizon's DSL salespeople are completely incompetent. Several weeks ago, I ordered residential service. I was unable to get the prequalification app on their web site to work for my address, so I called in. I was told (1) I could get a modem for free, and (2) that I didn't need to do anything for the install except plug it in -- they'd just be turning something on at the CO, perhaps even before the date my service was scheduled to begin. These were both, of course, pure fiction. On the install date, while at work, I got a call from a tech who had shown up at my house to rewire something. Luckily, I was able to rendezvous with him later in the day. The service worked briefly and then went down. Over the next few days, I called in, was told to wait at home for 8 hours, no one showed up, I called in again, was told "oh, sorry, it's going to be tomorrow", waited all day the next day, no one showed up again... a week and two actual visits later, I finally had connectivity. I can't really blame them for whatever the line problem was, but to repeatedly treat my time as worthless by making me wait at home for an entire day just in case a tech can get there, even though it's highly unlikely they will, is just offensive. Then, I received my first bill and... was charged for the modem. Calling in again, I was told that the deal I was offered was for online orders only, and even if I specifically confirmed with the sales person that he could get me the same deal since the web site wouldn't let me complete an order (and I already had an old, power-sucking modem anyway), there was no way I could get a refund. So I got an RMA and planned to go shopping for a replacement (people are selling the same model on Craigslist for $10). The RMA came in an email which also told me that if I did not return the equipment within X days I would be charged $100. This is all utter bullshit. Do not use Verizon. If your only other option is Comcast, as it is here, just steal your neighbor's wireless or tether your 3G. They could have left me reasonably happy with the whole botched situation if they had provided a honest estimate of how long it would really take to get someone out (even if it was "days"), or given me access to the same information without having to go through a call center of people only trained to read a script about how to power-cycle your router or whatever. And if they actually apologized for a saleperson convincing me to sign up by lying about what I would be charged. But why would they? What are you going to do, switch to an ISP that treats you like a human being and doesn't fuck with your traffic? Ha. Ha ha ha. Good luck with that.

5 March 2009

Decklin Foster: You're talking about things I haven't done yet

I've been converting all my Mercurial repositories to Git. One of the motivations for this was hg's poor handling of branches and tags: branches are just another metadata field on a commit, and tags are entries in a text file called .hgtags that is tracked in the repository(!). Git has its flaws as well, but I prefer its view that branches and tags are just refs, which are pointers that exist at the repository level and are pushed or pulled around in much the same way as the objects they refer to. The excellent hg-to-git.py script included in contrib created Git tags corresponding to my old Mercurial tags, but of course didn't modify the actual commits. So I still had a .hgtags file in my Git repository, and a boilerplate commit of the form "Add tag foo for changeset bar" each time I added to it (recall that .hgtags is just another file. Thankfully, I never had to deal with two branches where I added different tags...). I wanted to remove these. Git has a 'filter-branch' command that can totally rewrite or expunge commits; this is of course a horrible idea for already published code, but there's no harm in using while initially preparing a repository. While I appreciate git's object model and speed, I must agree with its detractors that the user interface is terrible. It took some tinkering to get git-filter-branch to do what I wanted, so I'm writing this to save my notes for next time (and in case someone else is searching for how to do this). Here's the command I arrived at:
git filter-branch \
    --tag-name-filter cat \
    --index-filter 'git update-index --remove .hgtags'
    --commit-filter \
        'if [ $# = 3 ] && git diff-tree --quiet $1 $3; then
             skip_commit "$@"
         else
             git commit-tree "$@"
         fi' \
    HEAD
The tag name filter is always necessary if you want tags to be updated to point to the corresponding commits on the new, rewritten branch. I consider this a UI failure -- when a branch is rewritten, the ref is modified, and the old one moved to refs/original. Tags, on the other hand, stay where they are, without any indication on the new branch that this is where you might want to move that old tag and sign it again or whatever. IMHO they ought to be handled the same as branches. The index filter is simply an efficient way of removing the unwanted file from all commits. This and the tag filter are both covered in the manual page. Writing a commit filter is a little more obscure. After .hgtags is removed from the index, we may end up at one of those useless "Added tag foo" commits and have no changes to record in the commit. By default, of course, filter-branch still records these -- the commit message might be useful, or something. But I want to suppress them. The commit filter is called with a tree -- you're at the point between write-tree and commit-tree (I recommend Git from the bottom up if you're confused here.) It gets that tree ($1), and then "-p PARENT" for each parent, just like commit-tree. So, if this is a normal commit with one parent, there will be 3 arguments. (If there's only one argument, there is no parent, i.e., the first commit, and if there are more, then it's a merge.) This is the only case we want to mess with. If there are no changes between our tree and the parent's tree, then it's one of those no-op commits, and we can skip it (skip_commit, a shell function defined by filter-tree, uses some deep magic to hand us the original parent again next time). I think diffing the index and the parent would work as well, but this seemed clearer. It still feels like a hack, so I'd love to hear from anyone who can suggest improvements. Since this is a special case, maybe it's better off being implemented in hg-to-git.py itself. There's always more than one way to do it. Update: Teemu Likonen points out that the next version of Git (1.6.2, not yet in unstable) will have a --prune-empty option which makes this particular problem totally trivial. I am starting to get the feeling that the Git developers are all reading our minds... :-)

7 January 2009

Erich Schubert: Blog rewrite upcoming

I'm going to re-do my blog sometime soon.The host my current blog is running on is going to retire "anytime soonish" (I happen to be responsible for that, so I will know when). So I will need to move my blog someplace else (I want to merge it into my regular web page, too). Right now, it's based on an ancient version of Pyblosxom with a couple of custom hacks, and is pretty much unmaintainable.So I'm also looking for an appropriate blog software, but I'm not yet sure about my requirements, so I don't have a clear software favorite yet. I don't need a browser based editor, and I'm not interested in interactive features such as comments or trackbacks.Mnemosyne is one of my favourites, because it's a rather low-level, bare-bones approach. Its templating is XML-based (KID) and it's Python.Ikiwiki actually calls itself a "wiki compiler", but I know that some people use it for their blogs. It also does valid XHTML, although its templating isn't designed for XML safety.Features that I would like to have: I guess I'll just end up writing a blog software for myself. The current text files are an odd mixture of HTML and plain text anyway, so I'll need to write an importer for the old data myself anyway (plus then I can also do some URI mapping to redirect old links). My current web pages are generated via an XSLT transformation, so I maybe just re-use that for templating.The thing which probably gives the largest issues is pagination of list views. (Especially when you want the pagination to be stable even when new entries are added while a user is browsing the pages, which is nice to have because of search engines and links). I don't think a fully static rendering will work here.

Erich Schubert: Blog rewrite upcoming

I'm going to re-do my blog sometime soon.The host my current blog is running on is going to retire "anytime soonish" (I happen to be responsible for that, so I will know when). So I will need to move my blog someplace else (I want to merge it into my regular web page, too). Right now, it's based on an ancient version of Pyblosxom with a couple of custom hacks, and is pretty much unmaintainable.So I'm also looking for an appropriate blog software, but I'm not yet sure about my requirements, so I don't have a clear software favorite yet. I don't need a browser based editor, and I'm not interested in interactive features such as comments or trackbacks.Mnemosyne is one of my favourites, because it's a rather low-level, bare-bones approach. Its templating is XML-based (KID) and it's Python.Ikiwiki actually calls itself a "wiki compiler", but I know that some people use it for their blogs. It also does valid XHTML, although its templating isn't designed for XML safety.Features that I would like to have: I guess I'll just end up writing a blog software for myself. The current text files are an odd mixture of HTML and plain text anyway, so I'll need to write an importer for the old data myself anyway (plus then I can also do some URI mapping to redirect old links). My current web pages are generated via an XSLT transformation, so I maybe just re-use that for templating.The thing which probably gives the largest issues is pagination of list views. (Especially when you want the pagination to be stable even when new entries are added while a user is browsing the pages, which is nice to have because of search engines and links). I don't think a fully static rendering will work here.[Update: Sean pointed out that by using title-only in archive pages I can avoid having to paginate. Thanks! I also received TONS of software recommendations. Ranging from PHP over bash (URGH, I want valid XHTML) to mod_lisp based (no special Apache modules, please. Should run everywhere.)]

13 November 2008

Decklin Foster: Film at 11

Managers are dicks. (ObBookMeme: "What's hard is sorting the result list so that the good results show up near the top.") (Something I am working on now that Njiiri has search, but who gives a fuck, right?)

Decklin Foster: A bit late, but still

Happy Armistice Day.

8 November 2008

Decklin Foster: Generally, "deal with huge amounts of" means "identify the 98% you can ignore"

Ted lays it out.

5 November 2008

Decklin Foster: Hello America, I missed you

As we walked back toward Mass Ave to catch the bus last night, fresh from the euphoria of my friend Josef's election-night party, I thought about how fortunate we are to be here, now. We cheered, we screamed, we sang the national anthem at the top of our lungs, popped champagne, and danced. I'm on some stranger's facebook somewhere, at the edge of a crowd of people smiling for what felt like the first time in years. I knew I'd remember, but I wanted to write something down. I'd been following along on Twitter all night, but I didn't know what to say when the good news finally came. I thought about our friends in California, where Prop 8 looked like it was going to win. Despite how far we've come, it's likely three states are about to write discrimination into their laws or constitutions rather than out of them. So I picked up my phone and just typed in, "charged." Everyone was excited, yes. I haven't felt electrified by the possibility of change like that in a very long time. But we also haven't automatically won change by changing our leaders. We have won a responsibility to make that change happen. "Yes we did", we joked to each other, and I emailed to my parents in the morning. But no one actually meant, "yes we can win an election". That's just the starting line. Now, we are charged with making America better. We are charged with protecting the liberties of everyone. I wanted to remember that too. That's why it matters that we won. Eight years from now, this could be a nation where the whole idea of "banning gay marriage" sounds as antiquated and offensive as segregated schools or not letting women vote. We have the ability to change our culture, nothing more. Mandates don't do that; people do. And yes, we can.

2 November 2008

Decklin Foster: Tap that class

And now, something entirely impractical. I picked up Beginning Ruby by Peter Cooper the other day to look for some teaching material. Flipping through, I came across a basic feature that had heretofore escaped my notice: the Struct class. If you want to use a class for nothing more than bundling together a few values, you can create a Struct instead of writing out initialize and attr_accessor yourself. The idiom is like this:
class Server < Struct.new(:host, :port, :password)
  def to_s
    port == 6600 ? host : "# host :# port "
  end
end
That particular one's from Njiiri (which may clue you in that it took me several months to get around to writing this post). For educational purposes, it's nice: you can point out how we give a name to a class (I've also tried to explain assignment as "naming" rather than "storing"), that there's an unnamed class (classes are also objects!), and overriding a method (which uses the dynamically defined stuff), without any boilerplate to get in the way. So it's tempting to use this in a lesson. Unfortunately, Ruby is a little bit weird here, and you run into those distracting "practical" issues about the particular language you're working in. Your classes can't subclass Class, and thus you can't say Foo.new like you can Struct.new. Struct is actually implemented in C (or Java, or whatever's native). So you have to do some handwaving. This is because the "metaclasses" we have in Ruby are implemented, so to speak, as singleton classes of objects (including class objects). I do not mean this as a criticism of Matz at all, but they seem like more of a serendipitous thing than an intentional design -- "hey, if we implement classes in this particular way, we get this sort of metaclassing automatically [1]." (The clearest explanation of why this is that I've found is in the first chapter of Advanced Rails by Brad Ediger.) I wanted to be able to just subclass Class, rather than have all that fun power that we normally get to abuse in Ruby, only because I think this is the clearest way to explain the abstraction. Even I still have trouble describing singletons in plain English. Struct is intuitively a class of classes, and factors out similar/boring stuff -- a good practice. It could be an example of how to refactor some simple classes, if only we could follow it. I decided to give up on the idea for my tutorial, but it kept me up at night. Ruby metaclasses clearly can do anything that the sort of Struct-like metaclasses I have in my mind -- "parametric" classes, if you will -- can do, and I can dynamically define just about anything; why not make it happen? We can instantiate Class itself, but the tool we have to shape that class is singleton methods. This can certainly be abstracted away. So, I whipped something up. Before getting to it, though, let's visit a new construct in Ruby 1.9 and 1.8.7: Object#tap. Apart from the obvious debugging use described in its documentation, it makes it quite easy to factor out this pattern:
def gimme_a_thing
  thing = Thing.new
  thing.do_stuff_to_it
  thing
end
Into something closer to the style of functional or declarative programming:
def gimme_a_thing
  Thing.new.tap  thing 
    thing.do_stuff_to_it
  end
end
(Well, "do stuff" is obviously still procedural, but A for effort.) Which one is "better" could probably be the subject of much debate, but: I really prefer the second one; even though it has exactly the same effect, it looks like "what" rather than "how" (something else I try to beat into impressionable young heads. :-)), which is I think easier to write tests for. I'm going to use it here, because it's shorter. Now, Object#tap passes the object as an argument to the block; in our case, we are going to define a metaclass, so we want to work with self, instead. So we define a new version, class_tap -- by analogy with class_def, I suppose --- which class_evals the block rather than simply evaluating it:
class Class
  def class_tap(&blk)
    tap  _self  _self.class_eval &blk  
  end
end
And to do the following trickery, we make use of MetAid, written by why the lucky stiff -- it's very small, so we could always just incorporate the bits we want into the code here, but this short file provides a common vocabulary for talking about metaclass stuff which is quite valuable. Now, we can write a method to create our new classes on the fly. Here's what I came up with:
require 'metaid'
class << Class
  def meta(_super=Object, &blk)
    new.class_tap do                     # 1
      meta_def :new do  *args            # 2
        Class.new(_super).class_tap do   # 3
          class_exec *args, &blk         # 4
        end
      end
    end
  end
end
(Note the "class << Class", opening the singleton, rather than "class Class", opening Class itself. Also, the distinction between new and Class.new -- they are the same method, but from inside the meta_def we're no longer in the Class class.) The lines of meta itself mean:
  1. The value of this thing is a generated class, which we will describe thusly:
  2. Its singleton class has a new method, which gives you a value that is:
  3. Another generated class, which is defined by:
  4. the original block, which now gets run with new's arguments.
Nary an assignment in sight! The (embarrassing) caveat is that methods in the block that defines the new class instance cannot use def to create methods as normal. A def is evaluated in a totally fresh scope (I don't think I get the opinion behind this decision, to be honest), so we need to use class_def instead. This is, I must admit, rather hideous. Perhaps someday the language will change. But, first things first: now we can implement Struct in pure Ruby.
class MyStruct < Class.meta \
  do  *args 
    attr_accessor *args
    class_def :initialize do  *instance_args 
      args.zip(instance_args).each do  attr, val 
        instance_variable_set :"@# attr ", val
      end
    end
  end
end
The real Struct class does a few other nice things for you, but this is the heart of it; I can go back to that Njiiri example and just swap in MyStruct for Struct (Providing an instant performance gain of -200%... :-)). Here's a "shapes" example from my abandoned OO lesson:
class Polygon < Struct.new(:sides)
  def perimeter
    @sides.inject(&:+)
  end
end
class RegularPolygonClass < Class.meta(Polygon) \
  do  n_sides 
    class_def :initialize do  side_length 
      @sides = [side_length] * n_sides
    end
    class_def :area do
      @sides.size * @sides.first**2 / Math.tan(Math::PI / @sides.size) / 4
    end
  end
end
class Square < RegularPolygonClass.new(4); end
class Pentagon < RegularPolygonClass.new(5); end
(Note that area has no free variables and thus could actually be defined with def. It would just look funny.) You only have to change one number here to make new polygon classes, rather than accumulating parameter lint or explicitly subclassing and redefining something implicit just for the derived class [2]. In a way it the exact analogue of the imperative vs. tap style described above. There is quite a bit of aesthetics involved; one way is not "right". Apart from the syntactical wart, I like being able to do things this way. Ruby is, as they say, optimized for programmer happiness and the principle of least surprise. Still, I'm sure it is quite slow, and I don't particularly need it for any real-world application right now. For an intro to OO it's way too much complexity to have lurking unexplained beneath the surface and still requires getting bogged down in the language you happen to be using. But hey! It's neat.
[1]I basically try to do this whenever possible, anyway. Design decisions are like sex: they're better when they're free.
[2]Of course, if Ruby class variables were not leaked across the entire inheritance hierarchy, something as trivial as this one-variable example would be... trivial. At least, few people would actually miss metaprogramming features. Blub blub blub.

30 October 2008

Decklin Foster: Pumpkins for Obama

http://www.rupamsunyata.org/decklin/photos/2008-10-30/pumpkins-for-obama.jpg So basically, my girlfriend and I are huge geeks.

Decklin Foster: A long-overdue explanation

Regarding that last post. I received the list via email from Alan Sondheim earlier in the week, and felt like putting up a link to it. No particular reason; it seemed appropriate for one of those sui-generis enigmatic tumblelog things. I was disappointed to find that none of the mailing lists where I might have been able to find it had public archives. (One way of dealing with people who complain about spammers harvesting their address, I suppose...) So, I said to myself, I've got 8+ years of this guy's email work, I'd like to share it with the world, and I have Mnemosyne, which I had always thought, in the back of my mind, would be a neat way to archive email written as email rather than written as a "blog". So, a little hacking later... The Alan Sondheim Mail Archive (Warning: some parts are NSFW). It has already given me some ideas for improving the example templates; I'll release 0.12 soon with the changes I had to make to get this running. (Final tally: 4500 messages, 75 seconds from a clean htdocs, 12 to no-op. On whatever the cheapest Linode [1] [2] is. Eh. I can do better.)
[1]Incidentally: Linode has been great. Highly recommended.
[2]Also: yeah, I know my footnotes are broken. Working on it.

Decklin Foster: Some Events in New York City in 1902

Some Events in New York City in 1902.

Next.