usable in any place a human can be used

20101102

Second System

[caption id="attachment_883" align="alignright" width="300" caption="Just a little more to the left...."]Cargo ship with containers crashing[/caption]

I've been working, both on the job and as a freelancer, right now my 9-5 is starting to enter that ever so exciting phase where one project enters maintenance mode and another project starts to spin up. The new project starting to spin up is to rewrite the CMS system that powers our various publication websites. Now for those of you not in the know, a CMS system is crazy complicated. I'm writing this current blogpost in a very specialized CMS system called WordPress, just for blogging. This software probably has had somewhere upwards of eleventy-billion man hours go into it (once you factor in themes and plugins and all that jazz).


Of course whenever you are faced with a complex problem that's been solved to death your best bet is to look around and see if anyone has done the heavy lifting for you already. So I started looking around, WordPress, Joomla, Drupal, all mature PHP CMS systems, none that I'm familiar with even a little. I started reading, out of the three I ended up liking Drupal the best, it seems well engineered, flexible, and very stable. I read and read and read about nodes and the way Drupal thinks about content and I started to think, ok maybe we can use this. The problem is that management is used to a level a flexibility that I won't have with Drupal for months, maybe even years. They want this flexibility from the beginning and that was making using Drupal seem less and less likely. Then some requirements filtered in and out and it looks like a custom solution is going to be best... c'est la vie.


Now this is in no way a slight against Drupal or Joomla or any other CMS system out there, I'm sure in the hands of Drupal Ninja or Joomla Gurus this would be easy as pie. For me though, building a solid system on my time tested and well worn toolbox of PHP, SQL, and Flourish will probably be the best way to deal with the constantly changing features and requirements that we need to support. Now I'm going to start building, start sketching out how things will work, how data will be thought of in the system, user models, documents, revisions, all kinds of fun stuff. It's a bit intimidating, but as a Software Developer this is really the stuff I live for, big gnarly problem, blank TextMate buffer, well known tools, get cracking.


The big problem I'm fighting right now is the dreaded Second-system effect. Watch out for this my dear friends, it's not a nice trap to get caught in. It works a little something like this.



  1. Build a system, this is the First-system, it works, celebrate and have a shot of Jack.

  2. That stupid "real world" comes in and messes up your data model, you add a little hack to compensate.

  3. Oh, it has to be able to do what? No, you said it didn't have to do that!! Fine, hack hack hack.

  4. Maybe some cleanup, probably some more hacking and bending.

  5. You've tried your best to keep First-system shiny and new, but the shine's off the apple.

  6. Crazy requirement, requires rewriting huge parts of the system, oh noes!

  7. Alright, now that we know so much more let's build Second-system.


Herein lies the danger. You get the go ahead to build Second-system, and you have all this experience from First-system, you really think you understand the problem domain a lot better, so far so good. Here's where things get hairy though, you start thinking about how you would build the system so that you don't run into the same problem First-system had, you start abstracting and abstracting, it gets more and more complicated, but you are now a master in this domain, you can handle it. Users who have been frustrated for months or years using First-system start throwing in their advice, things that have always bugged them, their nice-to-haves, and hell, you haven't built anything yet, chuck that automated-frog-boiler in your system plan.


Before you know it you've got a beautiful masterpiece, it handles everything, all the problems encountered in the First-system, all the nice-to-haves, everything, it is a theoretical thing of beauty. Now all you need is 6 maybe 7 years to build it.... oh shit. But there's something even more wrong about the Second-system, it has buried in it a dirty lie, that you understand the problem domain. You probably have a pretty good understanding of the problem domain, as it stands right now. Having already tackled this problem once does not make you clairvoyant, there will be changes to the system and your system will have to adapt to those new realities. This compounds the Second-system effect, because your Second-system is almost inevitably more complicated than the First-system and therefore harder to change and bend and make work in that pesky "real-world."


Right now I have to fight the urge to over-engineer, to outwit this problem, because I'm just not smart enough to solve this problem forever and ever into the future. So I'm going to focus on building a solid platform that let's me build out modules and snap them off when they aren't needed anymore. Something robust but simpler than the current system. Doing this will mean trading things off, it will mean making hard decisions upfront about architecture to meet our current needs but also look to the future, and this is what a good Software Developer is supposed to do. And once my current project is safely resting in maintenance mode, quietly humming along making my company money, I will begin building a Second-system and hopefully avoid Second-system effect.

20101029

Auto-timestamps in Flourish

This will be a short post, I just wanted to share a protip for anyone using Flourish and in particular using the fActiveRecord ORM. fActiveRecord is pretty awesome, with it and fRecordSet I find myself very rarely having to drop down to raw SQL, although for some reports it can't be avoided and then fORMDatabase::retrieve()->query() is your friend. Anyways one of the really cool features about fActiveRecord is that it has a plug in module based off of hooks and I wanted to pass along something that I've started doing as a convention that's worked out really well for me.


I like all of my model classes to have a created_at and an updated_at timestamp that automatically do the correct thing to provide a very basic audit trail. This does not let you off the hook for doing actual auditing, but it is a nice way to do sanity checks and get some instant information about records. fActiveRecord makes this pretty easy to do, let's dive into some code.


[php]
class someModel extends fActiveRecord {
/**
* The fActiveRecord class provides the configure function to let you set-up Active Record hooks. Let's do that now
*/
protected function configure() {
fORMDate::configureDateCreatedColumn($this, 'created_at');
fORMDate::configureDateUpdatedColumn($this, 'updated_at');
}
}
[/php]

Easy as that you now have your created_ats and updated_ats working great for the someModel class. Here's the cool part though, because boilerplate code is stupid ugly code, if you want to use this across your project simply do the following.
[php]
class ActiveRecord extends fActiveRecord {
/**
* For this project every model should record created_at and updated_at
*/
protected function configure() {
fORMDate::configureDateCreatedColumn($this, 'created_at');
fORMDate::configureDateUpdatedColumn($this, 'updated_at');
}
}
[/php]

Now when you need to create a new model simply extend ActiveRecord instead of fActiveRecord and auto timestamps are done.

20101022

freelance

[caption id="attachment_875" align="alignright" width="300" caption="I\'m not sure what this has to do with Freelancing, thanks Google!"]House made out of $50 bills[/caption]

I've been hard at work making sure that Faveroo keeps humming along, pushing out new features here and there and making sure everyone is happy. The growth we've seen has been steady and we are continuing to push forward. Work has been a little hectic lately, if anyone ever tells you that growing a product and a brand is easy, you just punch them right in the face, I don't care that it's your Grandma, do what I say, I'm a blogger. Anyways this blog post isn't about my 9-5 it's about my 6-10.


I do a lot of cool side projects, some of them like Prosper are just for fun, they scratch an itch I have and I think I might be able to use them later. Some never see the light of day, they lose momentum or turn into something that I can't figure out how to make any money or help anyone with so they just languish on my hard drive. Lately I've been trying to break into the freelancing stuff, I thought, how hard could this be, I have years of experience, I'm willing to work for a reasonable price, I write high quality code really fast, and I tend to think that I'm a fairly nice person to work with. Well its been an uphill battle so far, but I want to share my thoughts with you.


Online freelance boards just suck. They do, I hate you, I hate them, hate is really the only emotion I can feel about these boards. 90% of the posts are for ridiculously ill-defined or impossible jobs, 99.999% of the replies are someone in some third world country willing to write the new facebook for a shiny new nickel, and there is no way to get the point across to anyone that you get what you pay for. I've yet to find any useful paying work from these sites.


Craigslist is a mixed bag so far. When I started on this journey I got some help from a friend who basically just spammed me with every blog post they could find on the subject of freelance consulting. A nice thread on Hacker News, here's a Two part series by Ryan Waggoner on freelancing. One of the pieces of advice was to use Craigslist, so I tried that, there is a ridiculous volume of stuff in the Computer Gigs section, and since you aren't constrained by geography you can apply to a huge amount of them. I tried this for a few weeks but after sending out tens of emails, most custom written specifically for the project and only receiving one reply, I decided to try another avenue. I'm not giving up on the Craigslist approach yet, if for no other reason then the absolute mass of postings there.


Friends of friends. Right now this is where I've seen my only success. I'm currently working on a project for a friend of a friend and so far things are looking great. I'm enjoying the work, sweet lady PHP with my favorite unframework, Flourish, and I'm making great progress. It's been a good project to work on so far, the client has been great to work with, and the code has been flowing. I'm sure there's a downside to to this course, friends of friends will want you to do extra work for them for free or change things up as a solid, but so far so good.


Future plans. This blog post is part of it, but basically some self-promotion is in order. I'm going to be putting together an online portfolio of stuff I've done, Prosper is a nice large code base for someone to look at to see the quality of my PHP code. I realized I just need to get my name out there as an option, the current project I got was because I happened to tell my friend I'm trying to start consulting on the side, bam, first paying gig. I'm going to keep fighting the good fight on Craigslist, maybe come up with a more efficient system for submitting my name into consideration for postings.


If you need a good PHP programmer drop me a line in the comments or send an email to ihumanable@gmail.com and maybe I can write some awesome code for you.

20101004

sanity please

I'm sure this tale of woe will be shared by many of my web development colleagues, it has to do with Facebook. As a web developer I have to pay a Facebook Tax, Facebook has an API (I think they might be up to their fourth API now) and they have 500 million people, and so management everywhere wants their product to integrate with Facebook. Here's the problem, Facebook fucking sucks at documentation.


There are as far as I can tell at least 4 competing technology stacks going on over at Facebook: FBML, Javascript SDK, Old REST API, and Graph API. There could be more, some of those might not be fully APIs and are just components, I'm not really sure. I've been reading the pages at the official documentation website for weeks now trying to wrap my head around this thing. Maybe it's just me, maybe I'm dense or something, but I can't seem to figure out what the fuck is going on or what I should be using.


Here's a great example, let's say you want to integrate Facebook Connect (if that's still a thing, I'm not really sure), but more succinctly you just want to allow users to sign in using Facebook as their identity provider. How are you supposed to do this. Well there is this section on Single Sign-On that looks promising. But then later on if you follow the link to learn more about the Graph API you will find a section about Authorization that will lead you to this page on Authentication which I think is showing you how to do the same thing, maybe.


The problem seems to be too many ways and pages explaining the same thing or slightly different things, I just want one canonical authoritative place to look that says, "This is the API you MUST use for Authentication." I don't think that is asking too much. But I'm sure once I finish my primer on OAuth 2.0 and figure out how the extended permissions works it will all be more clear.


The API could be forgiven for giving you more than one way to skin a cat, if it then gave you specific details on how this stuff actually works. Let's say I want to build a website that displays events and then let's people use my website to mark themselves as attending that event in Facebook, is that possible... I have no idea. Here's a section of the Graph API about Publishing data to Facebook and it might be in there somewhere, but I can't tell. I guess I'll have to play around with how this supposedly RESTful API works when I have some state, maybe if I've successfully negotiated an OAuth Secret Consumer Token UID then I can call /EVENT_ID/attending and that will mark me as attending the Event with EVENT_ID, it seems to make sense a little bit, but the extremely helpful description of "attend the given event" and the arguments of none make me have to play around to figure it out.


Seth Call is similarly frustrated and makes a very nice detailed case for why the Facebook API is so woefully inadequate. I couldn't agree more, and I hope that help is on the way.


The problem I'm running into now is frustration and discoverability. There doesn't seem to be a particular flow to these documents, they are just disparate documents hyperlinked together in no particular order. I'm new is there a tutorial or common use case section, maybe somewhere buried 9 links deep. If you are going to embrace and extend then at least have the common courtesy to make our lives easier as you try to take over the world.


Facebook seems to have too many developers working in the API space not communicating, the "Old" and "New" APIs overlap in some areas and miss each other in other areas, there's no definitive answer on when support will end for any of the "Old" APIs, is it safe to use the "Old" REST API for functionality the Graph API doesn't support or will they turn off the tap at some point. When trying to navigate through the documentation it just feels like they made someone do a braindump and didn't try to organize it in any fashion.


Well I'm going to head back in because with the draw of 500,000,000 potential users we can't afford not to pay the Facebook Tax, which is a real shame considering how shitty the Tax Code they published is.

20100831

2 months

[caption id="attachment_864" align="aligncenter" width="590" caption="that\'s a pretty sweet logo"]Faveroo Logo[/caption]

On June 3rd I typed the following into my Terminal.



mkdir faveroo
cd faveroo
git init

On August 15th I typed the following into my Terminal.



cd ~/src/faveroo
php deploy.php production

Then I watched and held my breath as my carefully crafted deployment script worked its way through 2 months of code and pushed them across the line. Then I fired up my browser and went to http://faveroo.com and waited for the now familiar homepage to show up... I'm sure it took less than a second to load, but it seemed like an eternity, finally it was up. I worked for the next few hours exercising the application through all its various operations, squishing little bugs here and there that popped up from being in a different environment. Two months of development, and finally it was live, and it actually worked. A few hours later we started hooking up people with sweet deals and putting money in the bank, mission accomplished.


Faveroo.com is my first project at my new job at 614 Media Group. It was a skunkworks project up through the end of July, so I couldn't come to this blog and talk about it. I did all of the coding for Faveroo and my colleague Jeff Guciardo did all the design work to make it look so pretty (it's probably the prettiest thing I've ever had the opportunity to create). The basic premise of the website is that our team of dedicated sales people go and find great local deals, then we throw them up on Faveroo.com, and then we help people save some cash. When you buy a Faveroo you save some money, the business makes money, we make money, and 3% of the sale automatically goes to charity, it's win-win-win-win. But I'm neither marketing nor sales, so I will stick with what I know and what I knows is tech, so let's talk about that for a minute.


Faveroo is a PHP website that includes the public frontend, a private backend, and a series of maintenance scripts that make sure everything works like clockwork. When I was starting up the Faveroo project I was given carte blanche as to how to build it. All of our other web properties use the classic LAMP stack, so to keep operations fairly sane and because I deeply love PHP, I decided to build out Faveroo on a classic LAMP stack as well. The code is Object Oriented, nearly MVC, PHP 5.2 code. I looked around, had been for a long time, at various PHP Web Frameworks. I had just come off of working with rails on a side project and so I knew the joy and frustration of work with a framework.


As you may be aware, I'm crazy in love with Flourish and decided that I would use it as a major component. I have been a fan of Flourish for a while now, probably over a year, but this was the first big application I was going to use it on, and really the first large scale from scratch application I have ever written. Now don't get me wrong, I'm no rookie to this slinging code stuff, I've maintained huge legacy applications, built middle-ware up from nothing, and even rewritten large applications to the point that almost all of the original code has been replaced. But this would be the first time that if I didn't like something in my code, it was because I was the stupid jack-ass that made such a boneheaded decision. Well, not the first time, but the first time I couldn't easily blame someone else ;)


I want to say that the decision to go with Flourish is probably what made the rapid turn around time possible. It's a wonderful library that helps you do things right but doesn't force you to do anything you don't need. The thing that amazed me as I used it is I started off only wanting to pull bits and pieces, maybe some fCryptography here and a little fMessaging there, but as I got familiar (and boy howdy did I get familiar) with the docs, I just found more and more amazing functionality and a beautifully coherent system. Flourish was clearly written to be used, and by the end of the project I found I was using almost every class. It's clearly a tool written for real world use from real world experience.


Flourish and jQuery are really the 2 big external dependencies for Faveroo, the rest of the code was written by me. I found this minimal system worked very well for me. I wrote a router in about 100 lines of PHP code, it's nothing earth shattering but I think it has a few novel ideas. I've since built a new version of this router that is shorter and has less complex code paths. At some point in the future I may try to make a more generic version of this router and release it open source. All of the model classes are fairly straightforward using the excellent fActiveRecord as a base.


I spent about a week spinning up my minimalist framework, but it paid off big. I knew how the system worked every step of the way, and more importantly, I could alter it in minor ways to achieve dramatic results. All of this is possible with established frameworks, but here I got away without having to climb up a learning curve. This gave me more time to understand the problem domain and to learn how to best use Flourish.


With this experience under my belt I'm looking forward to continuing to learn, and hopefully to contribute back to, Flourish and PHP development in general. This project has shown me that in 2 months I can go from nothing to cash hitting the bank account. I feel reinvigorated to make cool things that add value to people's lives and reassured of my abilities as a programmer. After a long period feeling burned out and wasted, I remember why I love programming, and why I love creating, and why this is the only thing I can ever see myself doing.

20100805

Flourishing

[caption id="attachment_859" align="alignleft" width="204" caption="Flourish\'s sweet logo"]flourish logo[/caption]

Looks like my claim of writing fast and furious didn't come to pass, oh well. For the last few weeks I've been working on a hush hush project for work. It's not done yet but we are nearing the end of the road for the MVP to be released soon. I wanted to write a quick update to discuss what it's been like getting back on my developer feet after burning out.


First off I'm engaged in my work again. For months I had felt disconnected, on autopilot, just trying to get through the day and hit some milestones. Secondly, I enjoyed playing cowboy for a while but have cone to realize that not all the habits I picked up in Corporate America were bad. Thirdly, I'm back to getting serious amounts of work done and am solving interesting problems.


Enough touchy feely hippie bullshit, let's get down to some business. I've been building a largish scale php application for the last couple of weeks and I wanted to reflect on the process.


I started off looking at various php frameworks a few weeks before starting this new project. I had kept an ear to the ground in the php framework area since forever so I knew the big players. I had also dabbled in some ruby on rails so I knew the bliss of when a framework gets it right, and the frustration of running up against something you would have done differently.


I looked into several frameworks CakePHP, Symfony, Zend Framework, Fat Free PHP, Yii, Noloh, and Recess! to name a few. They were all quite interesting but they all felt oddly foreign. Imagine if ruby on rails came out and it was a port of .Net web forms for ruby, blech. All the frameworks I looked at felt like they were trying to pave over PHP and make it into ruby lite or .Net lite. Each one was well put together on the whole and with some effort I'm sure I could have become proficient in any of them. Then I remembered an old friend I had played with before, Will Bond's unframework Flourish.


If you haven't taken a look before I urge you to do so now. Flourish is exactly what PHP needs, a strong core of classes geared toward common web tasks, with an eye towards simplicity, security, and consistency. After having used Flourish for a few weeks I can do nothing but sing its praises. Let me say that working with the combination of Flourish + PHP is a completely different experience than just writing PHP. Flourish gives you a sane construct around which you can build high quality applications. Here's a list of things Flourish provides:



  • Cross Database support for DB2, MSSQL, MySQL, Oracle, PostgreSQL, and SQLite

  • An extremely good ActiveRecord implementation

  • Filesystem with psuedo-transactions

  • Safe consistent handling of Requests

  • Session handling with special resources for Authentication

  • Classes for creating HTML including a quality Templating mechanism

  • Great Utility classes (think ActiveRecord rails people)

  • Amazing Email support

  • Classes for handling Dates, Numbers, and Money

  • High quality Exceptions and Handling Facilities


The thing that you will notice with Flourish is that it's a library that's seen real world use. It provides a high quality core but understands that it shouldn't try to be everything to everyone. The components interoperate nicely but you also function in isolation quite capably. It truly hits all the high points you would want from a library, but there is even more.


DOCUMENTATION: The Flourish documentation is some of the best I've ever seen. There is full PHPDoc for every class, which is nice, but more importantly there is "Class Documentation" that explains how the class is intended to be used, with code examples to get you started. The documentation is written very well and is incredibly in-depth. You could sit down and read it like a book, it's that good.


COMMUNITY: The Flourish discussion boards are active and I have never seen a posting go unanswered. Will Bond himself usually answers within a few hours of your posting, I know he has more than once helped me fix some tricky bug or better explained how something worked to me. The ability to reach out and communicate with the author is amazing, it also makes you want to provide support for the project.


CONSISTENCY: The Flourish API is amazingly consistent. I keep finding myself saying, "That's how I would have written it" while using it. If you learn how fCookie works, you know how fSession works, and you even know how fRequest works. Flourish simplifies things to the point that it's worth it to go the extra mile to add a bit of security to your code, because it makes it dead simple. Then you look back and realize that it also has helped you write self-documenting code, and jesus, did it just fucking cut my lawn too, what the hell?


SECURITY: As I alluded to earlier, Flourish encourages writing secure code. Flourish takes security seriously enough to have an entire compendium on just that topic and how it relates to PHP. My code is definitely more secure now that I use Flourish, from better security in parsing Request variables, to easily preventing Cross Site Request Forgeries, Flourish has you covered.




Ok, so I didn't really expect this to turn into a huge love fest about how much I love Flourish but that's where we are at. I want to over the course of a few blog posts really explore Flourish. I haven't fully figured out what format these are going to take, but if you are a PHP developer who wants to take their code to the next level, stay tuned.

20100625

Out of the world

[caption id="attachment_853" align="alignright" width="400" caption="This smarmy bastard\'s finally come back to Ohio"]The sun[/caption]

I've been out of the world for a little while, approximately 40 some days. I unplugged from a lot of things to focus on some big changes in my life. There was a kind of, cliche alert, perfect storm of events that made me feel like dropping off the grid for a while and just relaxing.


First off, this blog, which brings immeasurable joy to so many (for free I might add, actually the hosting costs me money) got 1337 h4x0rz pwned. You see I made this mistake where by I tried to express myself... foolish bastard that I am. Someone decided that all the good will and popularity I had generated through months of concerted effort and promotion would be better used to sell some scummy fake AV software. I quickly fired up my ftp and an editor to find out what was going on, I was able to detect the hack right away (normally don't have giant evals at the top of my pages), some quick regex work and I was able to disable the attack within less than an hour of detecting it. But I had been compromised, and even though I had been able to address the immediate issue steps needed to be taken to protect my blog and the fine people that come and read it. I decided to take it down until I could get it cleaned up properly. I felt terrible, but I had taken steps to prevent such a hack from occurring, I couldn't figure out what had happened. Then I saw this and realized that is exactly what had happened to my site. I felt a little bit better that this wasn't my fault and I wasn't alone.


The second major thing is that I quit my job. I worked at HMB for just over 2 years, and I learned an immense amount, worked with some of the best people I've ever met, and went from Ramen Noodles on milkcrates to my comfy house and filet mignon (if Burger King is still putting filet mignon in their hamburgers). It was a fantastic place to work right out of college but towards the end I felt like it wasn't where I wanted to be in life. I found an opportunity to go work for a young media company in town that needed an experienced PHP programmer to take over for their "web guru." I'm incredibly happy to report that I took that opportunity and am currently running with it.


The third major thing is that I was burned out. My last project at HMB was difficult, difficult for me personally in every way I can think of. I realized near the end that I was at my breaking point, I was frustrated, angry, and plain unhappy. I felt like I had nothing left to give, I felt completely worn down, and I knew that something would have to give.


That's where I found myself a month and half ago, in a strange position where I was frazzled and broken but ahead of me sat this golden opportunity. I decided that I would unplug, unwind, and rebuild. I had become that proverbial Windows XP box with the 2 years expired Kapersky that was slowing to a crawl. My hardware was fine, but I needed to reformat (preferably with Ubuntu or maybe into a Hackintosh) and recover. One of the things that went on the chopping block was blogging, not intentionally at first, but once I stopped, well an object at rest tends to remain at rest...


I'm happy to announce with the relaunch of this blog that the "reformat" worked. What you see before your eyes is a fresh WordPress 3.0 install (with all the old content recovered from my numerous backups), a visual reboot (this is an out of the box cleanr right now, I'm planning on adding my own personality in gradually), but most importantly, a happy blogger typing. I've spent the last month and half settling into my new job at 614 Media Group. I'm not sure what my title is, on one piece of paper it says Web Guru, which I don't much care for, I've seen and heard "Head of Web Development and New Media" which I like, even though its a bit wordy. I can tell you though that I'm in charge of PHP development (along with iPad and iPhone development to come) and that I'm loving it. I have a swanky new office, I get to work in TextMate and git and PHP all day, it's a dream come true. I work with friendly, hip, funny people. I'm still finding my place there, but I have already figured out it's where I belong. For the first time in a long time, I'm building things, and it feels so good to build again.


I'm not really sure what else to put in this post, I plan to start up the blogging again, refreshed and reinvigorated. You can expect to see a heavier focus on PHP and Cocoa, but I will still go off on my other loves, Ruby, LISP, JavaScript, and probably fall in love with some more. And even though I'm happy and contented I will still have amusing rants (just ask Heather if you don't believe me.) I haven't decided how much I'm going to blog yet, I'm hoping for a couple posts a week, but I'm sure I have plenty of topics to cover from the time I was gone so it might be coming a bit fast and furious for a while.


In conclusion I just want to thank all the people that helped support me when I wasn't feeling at my best. My wonderful girl Heather, my dear friend Jeremiah, my family, and so many others. I also want to thank HMB for giving me a place to grow and learn, I'm still proud of the work I did there and proud of the work they do. I'm sorry I was gone for so long, if you come back and give me your eyeballs I hope to open them a little and maybe make you smile.