I must've been converted to a Pastafarian along the way, but I must say, my code looks and is 'orrible.
I thought to convert my SQL abstraction (not the ORM part) from native functions to PDO, which is PHP's own data-access abstraction. I simply noticed that Models and that sql.php is way too intertwined. But I'm the first to admit that my code is a mess, but I'm partly blaming the differences between RDMBS:es.
Nevertheless, I'm starting to think that I've chomped into something too big to swallow. If I ever want to release a first official release, even if it would be a developer release, I need to dumb down the framework - cut out features - or I'll end up rewriting the code 'till my fingers bleed. So, yes, despair is on the rise. It feels like I'm treading water but just sinking deeper by each kick.
It would be SO neat to get a release on LightFrame. A working release, that, while maybe crude, would be a working framework with a rigid set of features. UMTV, ORM, all kinds of buzzwords. Heck, maybe even get PHP's name out of the mud (although I might, now, have something to say about this).
Maybe a good pause in coding (again) would do the trick. I could browse the code, see what could be done better (yes yes, my coding methodology bit me in the ass. Har. Har.) and just post tickets for a while.
Crap. I need to be up in a few hours. I need an off-switch...
Monday, April 28, 2008
Sunday, April 27, 2008
OutOfJuiceException
Oh, gods. If you don't have a life, do yourself a favor and don't get one or at least postpone it as much as you can.
So, I started my new job on Wednesday. The great thing about this job is that we have no work horus. Sure, we have to clock in the usual amount of week-hours, but how and when and in what chunks we do them doesn't matter. So, I decided to be smart and do long days to start with, now that I still have the enthusiasm. Mistake. I've been quite dead since I started.
Not that it would matter. Once I get home, I unwind, get something to eat, act social and then it's already time for bed. Not that this would matter either, as I'm mentally spent after trying to get familiar with the new tools I'm going to fiddle with.
So, no, LightFrame has been at an stand-still for some time now. From what I remember, I'm trying to abstract a boolean datatype for the SQL, which, surprisingly, exists only in PostgreSQL natively. MySQL loves ENUM(), and SQLite doesn't seem to care much. And I've lived in a small fear of the vision of having to rewrite Fields. They're clunky, at best. But at least I can take some pride in having implemented a nicer O/R mapping than what Hibernate seems to be.
So, I started my new job on Wednesday. The great thing about this job is that we have no work horus. Sure, we have to clock in the usual amount of week-hours, but how and when and in what chunks we do them doesn't matter. So, I decided to be smart and do long days to start with, now that I still have the enthusiasm. Mistake. I've been quite dead since I started.
Not that it would matter. Once I get home, I unwind, get something to eat, act social and then it's already time for bed. Not that this would matter either, as I'm mentally spent after trying to get familiar with the new tools I'm going to fiddle with.
So, no, LightFrame has been at an stand-still for some time now. From what I remember, I'm trying to abstract a boolean datatype for the SQL, which, surprisingly, exists only in PostgreSQL natively. MySQL loves ENUM(), and SQLite doesn't seem to care much. And I've lived in a small fear of the vision of having to rewrite Fields. They're clunky, at best. But at least I can take some pride in having implemented a nicer O/R mapping than what Hibernate seems to be.
Monday, April 21, 2008
Character Sets are a Pain
I decided to give this a blog post of its own.
Time for another revelation: Charsets are really a pain in the ass. Correction: Multiple charsets are really a pain in the ass.
As I've mostly done single scripts for myself previously, it really hasn't hit me before. I guess my subconscious has always been aware of the problem, and naturally I've encountered some glitches when transferring stuff from a server to another, correcting them individually without giving it much thought. Only now have I started to realize its utter, shall we say, idiocy.
To give a brief and very redundant recap, charsets (or character sets) are (mostly) a set of mappings between zero/one combinations (bits) and symbols. Each charset has its own idea of which combination of bits represent each symbol. The de-facto charset in the HTML realm is called ISO-8859-1, and all documents are assumed to be of that charset, unless specified otherwise.
Figuratively speaking, the website throws the client a heap of bits, saying that those contain whatever you should read on a website. It also says which set of mappings you should use to interpret those zeros and ones to get the intended text.
So, it's easy to see that if you change either one, the contents of that website changes. If you alter the heap of bits, but still interpret it with the same charset, you get different characters where the bits have changed in the heap. If you change the charset, you change potentially the whole text, and most probably into garbage.
The obvious problems, like knowing which charset a document is, and telling it to the webserver, aside, there's one great problem that really could wreak havoc: If you blindly combine documents of different charsets into one, it's next to impossible to get sensible text out of that. It might sound like a far fetched scenario, but it's not all that obscure.
There are two prevalent charsets in use when it comes to web development, ISO-8859-1 and UTF-8, and those are totally incompatible. Now, imagine that I want to be future proof, and write all my code in UTF-8, as it's more expansive and includes more characters than ISO-8859-1. But then I get another developer that writes all his code in the old standard, ISO-8859-1. It takes only one include-statement, and that's it.
Contain the character set - obvious. Let's say that LightFrame forces everyone to use UTF-8. Ok, that's settled. Then, when LF ships, a user has ISO-8859-1 set, and all his templates look funny. Or vice versa, we use ISO-8859-1, and a user has UTF-8. Either way, it's screwed. If, miraculously, that gets a consensus, along comes a site visitor who's browser says "nuh-uh, I can't/won't read your character encoding". What then?
One solution would be to screw it all, and have the user keep the tabs on stuff. If it doesn't work, it's not my problem. But I wouldn't feel happy about that. LightFrame tries to be the magic wand that fixed the tedious. LF has abstracted SQL, it even has O/R mapping on top of that. It will have user authorization and authentication. In a nutshell, LF is designed to make the developer's life easier. Fixing charsets would definitely make a web developer's life easier.
So, now I'm forced to incorporate text normalization, somehow. Something that would make everything click. Let everyone make a mess out of their code, and still get everything look as it was intended from the start. There are no silver-bullets. LF requires a minimal feature set on the server side, while supporting a much larger feature set. So, PHP does provide two libraries, so a "if you have it, we support it. Otherwise, you're on your own"-approach might be a possibility. But then again, some servers might have one, but not the other. *sigh* I need a lie-down.
I hear that PHP6 is going to take a stab at fixing this, and I tip my hat off for that. But it won't help me, perhaps on the contrary. PHP5 barely has a foothold, as PHP4 still is the prevalent major PHP version out there, so there's no chance that PHP6 gets adopted as soon as I'd like. Not that I would know when PHP6 is scheduled for publication.
Time for another revelation: Charsets are really a pain in the ass. Correction: Multiple charsets are really a pain in the ass.
As I've mostly done single scripts for myself previously, it really hasn't hit me before. I guess my subconscious has always been aware of the problem, and naturally I've encountered some glitches when transferring stuff from a server to another, correcting them individually without giving it much thought. Only now have I started to realize its utter, shall we say, idiocy.
To give a brief and very redundant recap, charsets (or character sets) are (mostly) a set of mappings between zero/one combinations (bits) and symbols. Each charset has its own idea of which combination of bits represent each symbol. The de-facto charset in the HTML realm is called ISO-8859-1, and all documents are assumed to be of that charset, unless specified otherwise.
Figuratively speaking, the website throws the client a heap of bits, saying that those contain whatever you should read on a website. It also says which set of mappings you should use to interpret those zeros and ones to get the intended text.
So, it's easy to see that if you change either one, the contents of that website changes. If you alter the heap of bits, but still interpret it with the same charset, you get different characters where the bits have changed in the heap. If you change the charset, you change potentially the whole text, and most probably into garbage.
The obvious problems, like knowing which charset a document is, and telling it to the webserver, aside, there's one great problem that really could wreak havoc: If you blindly combine documents of different charsets into one, it's next to impossible to get sensible text out of that. It might sound like a far fetched scenario, but it's not all that obscure.
There are two prevalent charsets in use when it comes to web development, ISO-8859-1 and UTF-8, and those are totally incompatible. Now, imagine that I want to be future proof, and write all my code in UTF-8, as it's more expansive and includes more characters than ISO-8859-1. But then I get another developer that writes all his code in the old standard, ISO-8859-1. It takes only one include-statement, and that's it.
Contain the character set - obvious. Let's say that LightFrame forces everyone to use UTF-8. Ok, that's settled. Then, when LF ships, a user has ISO-8859-1 set, and all his templates look funny. Or vice versa, we use ISO-8859-1, and a user has UTF-8. Either way, it's screwed. If, miraculously, that gets a consensus, along comes a site visitor who's browser says "nuh-uh, I can't/won't read your character encoding". What then?
One solution would be to screw it all, and have the user keep the tabs on stuff. If it doesn't work, it's not my problem. But I wouldn't feel happy about that. LightFrame tries to be the magic wand that fixed the tedious. LF has abstracted SQL, it even has O/R mapping on top of that. It will have user authorization and authentication. In a nutshell, LF is designed to make the developer's life easier. Fixing charsets would definitely make a web developer's life easier.
So, now I'm forced to incorporate text normalization, somehow. Something that would make everything click. Let everyone make a mess out of their code, and still get everything look as it was intended from the start. There are no silver-bullets. LF requires a minimal feature set on the server side, while supporting a much larger feature set. So, PHP does provide two libraries, so a "if you have it, we support it. Otherwise, you're on your own"-approach might be a possibility. But then again, some servers might have one, but not the other. *sigh* I need a lie-down.
I hear that PHP6 is going to take a stab at fixing this, and I tip my hat off for that. But it won't help me, perhaps on the contrary. PHP5 barely has a foothold, as PHP4 still is the prevalent major PHP version out there, so there's no chance that PHP6 gets adopted as soon as I'd like. Not that I would know when PHP6 is scheduled for publication.
Change is Inherently Bad
I'm a lazy person. I always try to go the path of least resistance. I beg to notice that "least resistance" doesn't equal to "no resistance", but that's picky.
Anyhow. I'm partly glad that I chose Assembla (crap, I never remember the name). It has all kinds of tools that Google Code simply doesn't provide. But it's a bit messy with its features. Assembla has issues, tasks and a wiki. Now, if I think of something that I want to be done, it's a bit of a puzzle to know where to put it. It would be easy if the wiki would be reserved for brainstorming, issues are for reporting when something goes wrong and needs fixing, and tasks are just stuff to do. But it's not like that. Tasks are stripped issues, and always are associated with a milestone. Issues accept a markup language (think BBCode), but tasks don't. Tasks are easily marked and saved as 'complete' just by ticking a box, which makes tasks seem "light" and not noteworthy. Issues are commented upon, as tasks are replied to. Additionally, tasks are displayed in a "Flow"-section, which I think is supposed to be a list of various message. If you create a new entry in the Flow-section, you create just a message for others to read, with no magic abilities. Also, tasks don't seem to count towards milestone completion, only issues...
In a word, it needs getting used to.
Anyhow. I'm partly glad that I chose Assembla (crap, I never remember the name). It has all kinds of tools that Google Code simply doesn't provide. But it's a bit messy with its features. Assembla has issues, tasks and a wiki. Now, if I think of something that I want to be done, it's a bit of a puzzle to know where to put it. It would be easy if the wiki would be reserved for brainstorming, issues are for reporting when something goes wrong and needs fixing, and tasks are just stuff to do. But it's not like that. Tasks are stripped issues, and always are associated with a milestone. Issues accept a markup language (think BBCode), but tasks don't. Tasks are easily marked and saved as 'complete' just by ticking a box, which makes tasks seem "light" and not noteworthy. Issues are commented upon, as tasks are replied to. Additionally, tasks are displayed in a "Flow"-section, which I think is supposed to be a list of various message. If you create a new entry in the Flow-section, you create just a message for others to read, with no magic abilities. Also, tasks don't seem to count towards milestone completion, only issues...
In a word, it needs getting used to.
Tuesday, April 15, 2008
I'm Ditching Google Code
I'm currently checking out Assembla as an alternative to Google Code. Why? because they don't enforce any licenses upon the projects (although OSS seem to get more leeway when it comes to certain limitations) and most importantly, they support other version control products besides SVN. I'm giving Git a good look. While the tools seem a lot more rough and rugged than SVN's GUI:s, Git seems to get rid of most of the problems I have been having, but all that remains to be seen.
I'm still keeping the Google Code project for a while, and keeping the Assembla one under the cover.
Development updates continue to be posted here, no matter what - for the time being, that is *nudge*.
I'm still keeping the Google Code project for a while, and keeping the Assembla one under the cover.
Development updates continue to be posted here, no matter what - for the time being, that is *nudge*.
...But I'm Still Alive
(oh god, how I love the song)
So, development. It's there, although a bit weak at the time. Lots of non-coding stuff going on. We just got our second puppy (as in dog), which requires its fair share of the free time. Fortunately I've taken the exams I needed for now, so that's over with. Unfortunately, in two weeks, I'm starting my new full-time job. Becoming a code-monkey probably doesn't exactly strengthen the motivation to get your own projects done. I say "probably", but I don't know - time will tell. Oh, and then there's (for some god damned reason) a rekindled interest in Cyberpunk 2.0.2.0 (the pen & paper RPG) and the notice of horrible parts of the ruleset, 'forcing' me to rewrite e.g. the whole netrunning part. *blah blah personal stuff*
On the topic of monkeys, I'm getting one off my back. I'm skipping the rest of the blog project. I've proved myself that it can be done. I've also proved myself that LF needs quite a few more helper things to get the development with LF as smooth an experience as I've envisioned it to be. Instead, I'm getting done a website I actually have need for, that has been suspended for the duration of the development of LightFrame, but can't postpone it much longer. So, while I work on that project, I keep patching up LF where it needs it.
After that, I most probably sit down, take a deep breath, plan some features (mostly built-in views, helpers and either scaffolding or an admin panel) and call a feature freeze. Then begins the tedious task of realizing the missing features and polishing the existing ones. When that's done, some user documentation, followed by Publish! The timeline continues from there, but this is far enough already.
The first publication of LightFrame will be When It's done. I have no producer, therefore I have no deadlines, which leads to no schedule. Ultimate freedom!
But, now you know what to expect: today a measly website, tomorrow the world. Narf.
PS: Do not buy Nokia cellphones. My ~month-old E90 Communicator has been fixed twice and it's still broken.
So, development. It's there, although a bit weak at the time. Lots of non-coding stuff going on. We just got our second puppy (as in dog), which requires its fair share of the free time. Fortunately I've taken the exams I needed for now, so that's over with. Unfortunately, in two weeks, I'm starting my new full-time job. Becoming a code-monkey probably doesn't exactly strengthen the motivation to get your own projects done. I say "probably", but I don't know - time will tell. Oh, and then there's (for some god damned reason) a rekindled interest in Cyberpunk 2.0.2.0 (the pen & paper RPG) and the notice of horrible parts of the ruleset, 'forcing' me to rewrite e.g. the whole netrunning part. *blah blah personal stuff*
On the topic of monkeys, I'm getting one off my back. I'm skipping the rest of the blog project. I've proved myself that it can be done. I've also proved myself that LF needs quite a few more helper things to get the development with LF as smooth an experience as I've envisioned it to be. Instead, I'm getting done a website I actually have need for, that has been suspended for the duration of the development of LightFrame, but can't postpone it much longer. So, while I work on that project, I keep patching up LF where it needs it.
After that, I most probably sit down, take a deep breath, plan some features (mostly built-in views, helpers and either scaffolding or an admin panel) and call a feature freeze. Then begins the tedious task of realizing the missing features and polishing the existing ones. When that's done, some user documentation, followed by Publish! The timeline continues from there, but this is far enough already.
The first publication of LightFrame will be When It's done. I have no producer, therefore I have no deadlines, which leads to no schedule. Ultimate freedom!
But, now you know what to expect: today a measly website, tomorrow the world. Narf.
PS: Do not buy Nokia cellphones. My ~month-old E90 Communicator has been fixed twice and it's still broken.
Wednesday, April 9, 2008
My Head Explodes!
I wonder why SVN has been made so totally unintuitive and hard to use. Sure, it might be handling my versions, but it's hardly increased my productivity.
"What's wrong this time?" I hear you ask. Let me tell you. I've heard people complaining about some kind of nightmare when merging two branches together. Well. I can't do it, as in I am unable to do it. My skillset is not complete enough for that particular task. I can has stupidz syndreom! Perhaps I need to participate in a course on SVN and finally get a diploma with silver edges and nice italics to be allowed to use SVN, but then it definitely isn't for me.
"Merge branch1 into branch2." That's what I want to do. But, what does SVN? I have no. fucking. clue. It spat out a lot of text, finished, and I have my local files all fucked up. I'm not sure what (if anything) it did to the repository. I finally got the right files back from the repository. So, I tried another way. I get some checksum errors, and most probably my repository is totally fucked up.
While writing the code for LightFrame indeed is dull, boring and even frustrating at times, fighting with problems like these just don't help the matter. I fight with the code, not with the maintenance of said code.
Update: I virtually redid what I did the first time around. Now everything seems to be working just as intended.... *kaboof*
Update2: Nope. Close, but no cigar. *kablam*
Update3: Ok. I give up. I seem to have just deleted the whole trunk, when I just tried to reorganize it. fuck this. I'm doing my own backups...
Update4: Seems like I fixed it and Subclipse is not to thank for that one... I just copied over the template branch to the trunk, and now I have stuff in my trunk. *sigh* Where's that diploma...
"What's wrong this time?" I hear you ask. Let me tell you. I've heard people complaining about some kind of nightmare when merging two branches together. Well. I can't do it, as in I am unable to do it. My skillset is not complete enough for that particular task. I can has stupidz syndreom! Perhaps I need to participate in a course on SVN and finally get a diploma with silver edges and nice italics to be allowed to use SVN, but then it definitely isn't for me.
"Merge branch1 into branch2." That's what I want to do. But, what does SVN? I have no. fucking. clue. It spat out a lot of text, finished, and I have my local files all fucked up. I'm not sure what (if anything) it did to the repository. I finally got the right files back from the repository. So, I tried another way. I get some checksum errors, and most probably my repository is totally fucked up.
While writing the code for LightFrame indeed is dull, boring and even frustrating at times, fighting with problems like these just don't help the matter. I fight with the code, not with the maintenance of said code.
Update: I virtually redid what I did the first time around. Now everything seems to be working just as intended.... *kaboof*
Update2: Nope. Close, but no cigar. *kablam*
Update3: Ok. I give up. I seem to have just deleted the whole trunk, when I just tried to reorganize it. fuck this. I'm doing my own backups...
Update4: Seems like I fixed it and Subclipse is not to thank for that one... I just copied over the template branch to the trunk, and now I have stuff in my trunk. *sigh* Where's that diploma...
Tuesday, April 8, 2008
Google in the Web Framework Business
Google launched Google App Engine today. From the intro video, it's a Python framework that has some niceties like distinct separation of get and post requests (want!) and uses Django templates (nice choice).
But that's not what's interesting. The interesting part is that they provide free hosting. That's kind of a bummer and partly rains on LightFrame's parade, as LF is intended to work as hassle-free as possible on any existing hosting. But I guess it rains even more on Django's parade, as they both work with Python. But, then again, it seems that Google rains on neither's parade, as (from what I gather) you can't deploy the software on your own server, but are tethered to Google.
But that's not what's interesting. The interesting part is that they provide free hosting. That's kind of a bummer and partly rains on LightFrame's parade, as LF is intended to work as hassle-free as possible on any existing hosting. But I guess it rains even more on Django's parade, as they both work with Python. But, then again, it seems that Google rains on neither's parade, as (from what I gather) you can't deploy the software on your own server, but are tethered to Google.
Huh? That Was It?
It's always an anti-climax when you tweak something that might work, and it fixes the whole goddamn problem! As weird as it might sound, I feel always somewhat deflated when that happens - a bit of tweaking here, a bit of fixing here, and *boom* it works, entirely unexpectedly!
I'm used to big fights. Hunting that off-by-one error, tracing variables through recursions of calls back and forth between classes and finally finding that logical error where I took the value out of the wrong method. No, not this time.
I just rewrote about five lines of code and the whole heap of tests just flies through the tests, passes each and every one of them. So, yes, I have if-then-else with elseif completely working. But I got it working before I even started trying. I guess I should not be disappointed. Not when I got the routine halved by the amount of lines from the original implementation. I've almost done everything that I did the previous round, and I've got rid of about a third of the old LOCs.
I guess I should scoot onwards, then, start fighting with the foreach-loops.
Update: Safari really sucks when it comes to rich text editing, it appears.
I'm used to big fights. Hunting that off-by-one error, tracing variables through recursions of calls back and forth between classes and finally finding that logical error where I took the value out of the wrong method. No, not this time.
I just rewrote about five lines of code and the whole heap of tests just flies through the tests, passes each and every one of them. So, yes, I have if-then-else with elseif completely working. But I got it working before I even started trying. I guess I should not be disappointed. Not when I got the routine halved by the amount of lines from the original implementation. I've almost done everything that I did the previous round, and I've got rid of about a third of the old LOCs.
I guess I should scoot onwards, then, start fighting with the foreach-loops.
Update: Safari really sucks when it comes to rich text editing, it appears.
Monday, April 7, 2008
Unsexy Tags
The dreaded if-tag I mentioned in the previous post is here haunting me with its unsexyness.
Even though I have sexed up the template engine itself, the tags are not much better. Well, actually, the normal and simple tags, doing simple things like making text uppercase or lowercase - they are very nice indeed. But the more complex ones, like if-elseif-else will look horrible. I guess it's unavoidable, as the tags aren't actually designed to support multipart-tags.
Also, while I do get the foreach-loops done in an intuitive order, I fear the thought of how the variables will be implemented.
Be how it may, I'm somewhat convinced that the new template engine will be neater and perhaps a smidgen faster than the old implementation. Huzzah!
Even though I have sexed up the template engine itself, the tags are not much better. Well, actually, the normal and simple tags, doing simple things like making text uppercase or lowercase - they are very nice indeed. But the more complex ones, like if-elseif-else will look horrible. I guess it's unavoidable, as the tags aren't actually designed to support multipart-tags.
Also, while I do get the foreach-loops done in an intuitive order, I fear the thought of how the variables will be implemented.
Be how it may, I'm somewhat convinced that the new template engine will be neater and perhaps a smidgen faster than the old implementation. Huzzah!
Friday, April 4, 2008
Sexy Templates
Yes, my templates are going to be sexy! Full of sass!
If things go as planned (pfft, do they ever?) my renewed Template engine is going to be streamlined as hell. Filters' structure will most probably be untouched (what do you expect, they get a value in and they're expected to return a value). I still have the TOM (Template Object Model) class left, but it's a whole different story now. The old TOM is kind of integrated into Template and perhaps partly in the new TOM, but tags are also a part of TOM now.
Tags are now also classes, instead of functions. I'm not sure what kind of performance implications this will have, but the Template object is skinned to the bone now, so perhaps they counter each other. But yes. Tags are expanded TOMs, that have a special method evaluate() that actually does the stuff. But, instead of having a "here's the tree, have fun!"-approach, it's much friendlier this time around.
Dead easy, dead sexy!
What's un-sexy, however, is the fact that I have to rewrite all tags, including if-then-else :(
If things go as planned (pfft, do they ever?) my renewed Template engine is going to be streamlined as hell. Filters' structure will most probably be untouched (what do you expect, they get a value in and they're expected to return a value). I still have the TOM (Template Object Model) class left, but it's a whole different story now. The old TOM is kind of integrated into Template and perhaps partly in the new TOM, but tags are also a part of TOM now.
Tags are now also classes, instead of functions. I'm not sure what kind of performance implications this will have, but the Template object is skinned to the bone now, so perhaps they counter each other. But yes. Tags are expanded TOMs, that have a special method evaluate() that actually does the stuff. But, instead of having a "here's the tree, have fun!"-approach, it's much friendlier this time around.
- The template is parsed in an intuitive top-down-nested order - just as it should be!
- The aforementioned evaluate() that acts as your main method, and you write all the actions inside this.
- Auxiliary method step() advances one 'step' in the tree, and returns false if the tree is already at the end, or otherwise a string with the contents from the tree and advances one step.
- Auxiliary method evaluateVariable($var) is your holy grail of variable evaluation. If the tag accepts a variable, just put it through this method, and out comes the evaluated value - they are handled identically to the {{ var }} -variable tags, filters and all!
- Tags can expect either nodes (a block tag) or a tag (simple tag) explicitly, by just calling either the auxiliary methods expectsNodes() or expectsTag().
Dead easy, dead sexy!
What's un-sexy, however, is the fact that I have to rewrite all tags, including if-then-else :(
Thursday, April 3, 2008
Reverting to Old Stuff
I'm afraid NetBeans doesn't cut the muster when it comes to PHP development. It can be quite hot later on, but today the PHP extension seems a bit buggy. NetBeans is perhaps too Java-y.
On a unrelated note, I now that I started rewriting the Template engine remember why I disliked it so much...
On a unrelated note, I now that I started rewriting the Template engine remember why I disliked it so much...
Subscribe to:
Posts (Atom)