short thoughts on D (like my twitter)

Adam D. Ruppe destructionator at gmail.com
Fri Jun 10 21:09:44 PDT 2011


Another thing that I'm not even ready to write html about,
but it works in great part thanks to alias.

===
import arsd.web;

class TestObject : ApiObject {
	this(Site site, string identifier) {
		id = identifier;
	}

	string id;

	string sayHello() {
		return "hello from " ~ id;
	}
}

class Site : ApiProvider {
	alias .TestObject TestObject;
}

mixin FancyMain!(Site);

===

http://arsdnet.net/cgi-bin/objdemo/TestObject/adam/say-hello

That url calls:

auto obj = new TestObject(site, "adam");
auto result = obj.sayHello();

// output the return value to the user in a suitable format...
cgi.write(wrapIn("document", formatAs("html", result)));

behind the scenes. That formatAs() function can do some magical
things too. I'll write about it when I do my second html templating
article. override Element makeHtmlElement() can do some magical
things...

Wow, sidetracking!

class ApiProvider is the centerpiece of web.d. It uses D's
reflection capabilities to make a collection of standard D
methods, structs, and enums available to the outside world.

Your functions are accessible through a web browser interface
(plain html documents and forms) a javascript interface, and
whatever other external programs you want; even a shell script
can access unauthenticated functions in about 20 lines if you
have curl.


Anyway, until today, the access was for the most part, only procedural style.

http://arsdnet.net/cgi-bin/apidemo/get-a-box?color=red

Translates directly to getABox(Color.red); That's what you'd do
for all the site's functions.


Now, you can have classes too, as seen above. Previously, I'd
write things like

void powerOnVm(int vmId) { auto vm = VM.get(vmId); vm.powerOn(); }

Now, I should be able to just expose that VM object directly to
the user, without having to manually write that wrapper code!


I'll save the why details for when I do the full writeup (which
will be after I try actually using this for something - so I can
be sure it's actually useful the way it is now).

But the how has one thing I find very cool: the alias.



When using web.d, the magic reflection is only called on a single
class, and all a classes members must exist in one file...

...unless you add more with alias!


D's alias lets you introduce any kind of name into your class,
from anywhere. When the derivedMembers loop is run to generate
the reflection info, it sees those alias members too.

===
int foo() { return 10; }

class CoolApi : ApiProvider {
  alias .foo foo;
}

mixin FancyMain!(CoolApi);
===

http://arsdnet.net/cgi-bin/objdemo/foo

Prints 10 - the free function becomes a part of the class, sort
of. Of course, being a free function, it can't actually access
the classes instance variables, and passing parameters to it
through the web interface is probably no good (suppose it needs
a database handle... the website user certainly can't provide
that!).

So, aliasing free functions isn't of much use.


Aliasing structs, enums, and classes though? Very nice. Write
your structs in another file and expose them to the public site
with a single alias line. Share enums from another module with
the world just by aliasing it in.


For the object demo, I did a class this way. The TestObject
could be moved to another module, and include functions the
whole thing can use. Yay! (This is why the first argument to the
constructor is your ApiProvider instance btw, so it can access
those instance variables, putting it ahead of free functions.)


The ApiProvider module might be nothing more* than a list of
imports and aliases, exposing certain portions of your
application to the public. Currently though, it ensures the
exposed classes all derive from ApiObject, but there's actually
no technical reason for this; I just thought it looked cleaner.
It might change as I actually use it.

structs and enums have no such restriction - you can alias them
in to your heart's content. But, their methods aren't accessible
from the outside like classes.

(On the other hand, their data members are. Class data members
are never exposed here; part of that is the procedural roots,
and part of it is that I assume instance variables are more likely
database handles and such than things the end user ought to have
access to.

Similarly, http://arsdnet.net/cgi-bin/objdemo/TestObject/adam/
gives no such method instead of dumping the variables. I'm
considering tying that into a particular method which can return
a struct or something.)


* Well, ApiProvider is also responsible for custom data formatting
for output to the browser, so it'd probably still do that. But
it could be limited to pure view like activities, without any
site logic function implementations.


Speaking of alias, another cool thing: alias one class method
to another name. The reflection picks it up, so it's now accessible
to the browser user both ways too! This surprised me actually -
I figured alias wouldn't show up in the reflection, but it did.
(of course, my current implementation generates separate wrapper
delegates for each alias... but meh, my implementation is ugly.
the results are beautiful :) )



Anyway, this ended up being pretty long! I'm just continually
amazed with the cool things D's most mundane features allow.
First enum and now alias... will the fun never end?


More information about the Digitalmars-d-announce mailing list