AOL appears to be considering rolling out jabber access to AIM.
That would be pretty awesome.
AOL appears to be considering rolling out jabber access to AIM.
That would be pretty awesome.
Last night I thought to myself “hey, I know, I’ll try out Dungeon Runners!” It’s a pseudo-free pseudo-MMO, which could be described as a Diablo parody – that’s the sort of game that sounds appealing to me.
Sadly, I discovered that Dungeon Runners is not a game I can play.
You see, it requires that inbound port 80 be open for your initial connection to a world server. This is a mind-bogglingly weird decision.
Port 80 is an entrenched-by-standards ports – it’s what webservers listen on.
Thus Dungeon Runners is off-limits to two classes of people:
People whose ISP block port 80 inbound, because they really don’t want people running servers. (Verizon did this for a while on my connection.)
People who run a server. (That’s me.)
Okay, I could shuffle ports around, temporarily assigning 80 to my games computer whenever I wanted to log in. But that’s more effort than I want to put into this. Thus I effectively cannot play Dungeon Runners.
The title is a bit of a mouthful. Sorry.
Before we begin, I present the caveat that this code should not be used on a production system. It launches a java runtime for every single request, which would cripple you. This would need (a) output caching, and (b) some sort of persistent FOP server process before it could be considered usable.
But if you just need to generate PDFs on an intranet app, say, then this could be handy.
Step 1: Put FOP somewhere it can be found. Specifically, its “build” and “lib” folders. I created a “fop” directory in my project, and stuck everything in there. (I don’t promise that this is ideologically sound – I’m new to the whole Rails thing.)
Step 2: Add Mime::Type.register "application/pdf", :pdf
to config/initializers/mime_types.rb
(this gleaned from Dynamic Graphics with Rails 1.2).
Step 3: Use a controller action something like this:
# GET /documents/1
# GET /documents/1.xml
# GET /documents/1.pdf
def show
@document = Document.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => @document }
format.pdf do
# We generate the classpath by scanning the FOP lib directory
command = "java -cp #{Dir.getwd}/fop/build/fop.jar"
Dir.foreach("fop/lib") do |file|
command << ":#{Dir.getwd}/fop/lib/#{file}" if (file.match(/.jar/))
end
command << " org.apache.fop.cli.Main "
command << " -xml #{Dir.getwd}/fop/xml/#{@document.file}"
command << " -xsl #{Dir.getwd}/fop/xslt/doc2fo.xsl"
command << " -pdf #{Dir.getwd}/fop/tmp/#{@document.id}.pdf"
if(Kernel.system command) then
send_file "#{Dir.getwd}/fop/tmp/#{@document.id}.pdf",
:type => "application/pdf"
else
render :text => command
end
end
end
end
Then whenever someone asks for “documents/17.pdf” it’ll make a PDF and serve it right on up. In the event that something goes wrong it’ll just display the command it ran, for some rough-and-ready debugging.
For a proof-of-concept you could try this with the example XML and XSLT that comes with FOP. Look for “projectteam2fo.xsl” in the examples directory.
As I said above, this works, but should not be put anywhere near a publicly accessible site.
I started looking at Rails (leading to my talking about scaffolding) because I wanted to try writing my next work-project in it.
I don’t know about others… but I hate learning a language/framework in isolation from a project. Writing an insipid tutorial project that I don’t care about doesn’t involve me, and so I don’t learn as well. Also, the applications written alongside tutorials tend to be very carefully chosen to hit all the good parts of a framework, while ignoring the rough spots. Thus I’ve historically viewed “prototyping my next project” as a great time to pick up something new.
So. I started Rails tutorials on Friday, didn’t touch it at all over the weekend, and by mid-morning on Monday I had my proof-of-concept app. I’m storing legal documents described in XML, allowing people to fill in some defined fields on them, then generating custom PDFs/RTFs/PNGs/whatevers using Apache FOP. (That sounds more complex than it is. I’ll post an example later.)
Rails itself is being reasonably unobtrusive, which I approve of. I had to do minor research into how to set up custom mime types for response formats, but it turned out to be quite simple.
I’m learning Ruby on Rails starting with 2.0. This is occasionally problematic, as it was only released a few days ago, and the tutorials are still all for 1.2.
So, to help others, something not mentioned in the release notes, which causes errors if you’re following the official tutorial.
Scaffolding has changed.
The 1.2 way was to stick scaffold :modelname
into a controller.
The 2.0 way is to run ./script/generate scaffold ModelName field1:type field2:type field3:type
on the command line.
The new way is more useful, I think, as it reduces the initial hurdle of moving from a scaffolded controller to a slightly custom one. It gives you controllers filled with code ready to be tweaked, and sets up a migration to create your model. You’re left with a working site filled with examples of how to do things in rails, instead of a magical “scaffold :foo”.
It’s just that it’s a wee bit undocumented.
An aside: I felt that I needed to post this because googling for “rails 2.0 scaffolding” didn’t actually produce anything helpful on the first page or so. Lots of talk about whether it’s ideologically pure, but not so much on the “this is how to do it” front.
[Update: I get the impression, from seeing others talk about this, that it’s not so much that scaffolding has changed as that one scaffolding option has been removed. It looks like the scaffold generator was there pre-2.0, and the only change is that scaffold :Foo
is no longer available. Still breaks every “getting started with Rails” article I’ve ever seen, though. :P ]
* NEW - freeSlots, bagType = GetContainerNumFreeSlots(bagIndex) -- Returns the number of free slots in a bag, and the type of items that can go in the bag. For bagType, 0 means any item can go in the bag. * NEW - bagType = GetItemFamily(itemID | "name" | "itemLink") -- When used with a container, this returns the type of container. When used with an item, it returns the type of container the item can go in. However, bagType is a bitflag, so GetItemFamily for something that could go in a quiver (bagType 1) and an ammo pouch (bagType 2) would return 3. A bag that can hold both arrows and bullets would also return 3.
This does mark the first time I’ve asked for API functionality and had it implemented. :D
I just watched Linus Torvalds talking about git, the distributed version control system he wrote.
What struck me here is that several times in this talk he was asked by Google employees variations on the theme of “why should we use git?”, and he didn’t have a compelling answer. His statements boiled down to “we have an alternative way of visualizing branches, and subtly different workflows”.
Many of his “git is awesome” points were rooted in specific objections to CVS. In particular, the complaints about branching and merging, and finding changes to a particular subset of a repository. I’m not saying git isn’t good at these things, I just think he should be comparing to SVN, which enables less cheap shots.
None of this is to imply that I don’t like git. I’ve been playing with it recently, and it seems impressive. I can’t say that its distributed nature feels very essential to me – but then, I’m not a member of a massive pseudo-anarchistic project like the Linux kernel. I suspect that until you hit some critical mass of independent developers on a project, git and SVN are fundamentally interchangeable.
The things that have actually made me go “ooh” about git thus far are offline commits, and content-tracking across files.
As an amusing personal idiosyncrasy I run a fiction archive called FicWad. (I believe much Katamari Damacy had been played just before the name was chosen.)
I call this idiosyncratic because I don’t use it myself. I’m not, generally speaking, a fanfic-reading sort of person. I run it because my wife wanted to start a fiction archive, and I was dragged in to provide the technical side of it. She’s since drifted away, leaving me to play as tinpot dictator over the writing masses. (I am a very laissez-faire dictator, so this works pretty well for them.)
I treat it as a coding hobby project. It doesn’t actually make any noticeable money from the ads, so I don’t feel compelled to put effort in apart from when I feel interested.
I’ve come to the conclusion that this sort of hobby project is a really good thing to have. When you’re writing something that thousands of people use, they’ll scream at you if it doesn’t work. It provides incentive to work out how to do things right.
In particular, it provides incentive to work out how to do things yourself. A solo project like this doesn’t let you get away with passing the buck to someone else on your team who’s done something like this before. If it turns out that you need to optimize your SQL, or use caching, or write a prioritized mailing queue, or whatever, you have to learn about the problem area.
Yes, you’ll write some awful code. In fact, I had to rewrite the whole site from scratch earlier this year because back when I first wrote it I really didn’t understand SQL performance, and I had to choose between throwing money at it (better server, etc.) or fixing the real problem.
But I know I’m a better programmer for having it around. It forces me to confront issues outside of my comfort zone, and that can only be a good thing.
If you have some programming experience then there is one particular feature of Python that is likely to turn you off.
Whitespace matters. Indentation is significant.
This comes as a shock to many people who are used to it being meaningless. Most languages in common use designate code blocks with braces ({ ... }
), or in some cases special keywords (e.g. Lua’s do/then ... end
). If you want to write your program without any indentation whatsoever then it won’t stop you.
So having to pay attention to whitespace worries people. Puts them off trying Python. Makes them chafe at the thought of using it. Their freedom is being abridged!
This is really weird, since everyone agrees that consistent indentation is a good idea. In fact, it’s about the easiest and most effective thing you can do to make your code legible.
This is why objecting to syntactically meaningful whitespace in Python is a straw-man. All it’s doing is requiring you to not write unreadable and misleading code. In fact, if you have good habits already then you will never notice it. Ever.
(This post is an example of low-hanging fruit for a programming blog. Something to post about early on, for the sake of getting your opinion out there.)
I’d like to take a moment to (a) completely alienate my audience, and (b) ruin my credibility. I will do this by discussing the semantics of a particular operation of the Subversion version control system, and explaining how poorly I initially understood it.
It took me a while to realize how svn merge
should be used. My intuitive sense of the usage of the command diverged significantly from its actual use.
When I think of it as “merge” the syntax that intuitively appears is svn merge [source] [target]
, with the intent of merging “source” into “target”.
This led to my attempting to merge from branch HEAD to trunk HEAD, and be puzzled at the lack of effect.
I find that everything fits together if I think of svn merge
as a synonym for svn repeat
. You’re asking for the changes between two revisions to be applied to the current working copy.
Thus svn merge branches/coolfeature trunk
doesn’t do anything because it doesn’t describe any changes.
What’s required is svn merge branches/coolfeature@73 branches/coolfeature@HEAD
, which takes the changes made to the coolfeature branch between revision 73 and the latest revision, and applies them to the working copy you’re currently in.
(It’s much shorter to write this as: svn merge -r 73:HEAD branches/coolfeature
)
The awkward bit is finding out when you should start merging from (the “73” in my example). To do this you have to read the logs and find either the revision where you created the branch or the last revision you merged from, whichever is more recent.
All this should become irrelevant shortly, though. Subversion 1.5 (according to an aside in this developer blog post) will automatically track much merge metadata so the requirement to specify revision ranges of changes to merge should be eliminated in the common case. I look forward to it.