Blocks

  1. Do one thing.
  2. Do it well.
  3. Keep it concise.
  4. Use only what is given.
Today we continue from Comments and explore how to refactor out code from blocks.  In Comments, we learned to extract code out into it’s own method whenever we use a comment to describe a block of code.  Today, we are going to look at how to do the same thing with blocks.
A block of code is any code that exists within a if, a loop, or a switch statement.  A good rule of thumb is every time you indent your code.  Our goal is to keep indents inside your code from going beyond 2 levels.  Let’s look at a typical method where we break this rule.
public function createAction()
{
    $adminForm = new Form_User();
    $adminForm->removeElement('dealership_id');

    if ($this->getRequest()->isPost()) {
        if ($adminForm->isValid($_POST)) {
            $mAdmin = new Application_Model_DbTable_Users();
            $result = $mAdmin->createAdmin(
                $adminForm->getValue('name'),
                $adminForm->getValue('email'),
                $adminForm->getValue('password'),
                $adminForm->getValue('role')
            );

            if ($adminForm->getValue('notify') === 'yes') {
                if ($adminForm->getValue('notifyPhone') === 'yes') {
                    $notification = new Application_Model_DbTable_PhoneNotification($mAdmin);
                } else {
                    $notification = new Application_Model_DbTable_Notification($mAdmin);
                }
                $notification->sendNotification();
            }

            if ($result)
                $this->_forward ('confirm');
        }
    }

    $adminForm->setAction('/admin/create');
    $adminForm->setMethod('post');
    $this->view->form = $adminForm;
}

Before we start refactoring this, I want to cover a couple things.  First, the above is a small example, but it can still be used to explain our need.  Secondly, what we learn here can be applied to code, regardless of the size or complexity.

Complexity

Before we set out on this road, we need to define complexity.  Specifically, I’m referring to Cyclomatic Complexity.  The best definition of Cyclomatic Complexity that I’ve found is from the PHPMD page on Cyclomatic Complexity.

Complexity is determined by the number of decision points in a method plus one for the method entry. The decision points are ‘if’, ‘while’, ‘for’, and ‘case labels’. Generally, 1-4 is low complexity, 5-7 indicates moderate complexity, 8-10 is high complexity, and 11+ is very high complexity.

Our goal is to keep what we write in the low complexity category.  At this point, we should go through the sample code from above, and figure out how much complexity there is.

public function createAction()  // Complexity 1
{
    $adminForm = new Form_User();
    $adminForm->removeElement('dealership_id');

    if ($this->getRequest()->isPost()) {  // Complexity 2
        if ($adminForm->isValid($_POST)) {  // Complexity 3
            $mAdmin = new Application_Model_DbTable_Users();
            $result = $mAdmin->createAdmin(
                $adminForm->getValue('name'),
                $adminForm->getValue('email'),
                $adminForm->getValue('password'),
                $adminForm->getValue('role')
            );

            if ($adminForm->getValue('notify') === 'yes') {  // Complexity 4
                if ($adminForm->getValue('notifyPhone') === 'yes') {  // Complexity 5
                    $notification = new Application_Model_DbTable_PhoneNotification($mAdmin);
                } else {  // Complexity 6
                    $notification = new Application_Model_DbTable_Notification($mAdmin);
                }
                $notification->sendNotification();
            }

            if ($result)  // Complexity 7
                $this->_forward ('confirm');
        }
    }

    $adminForm->setAction('/admin/create');
    $adminForm->setMethod('post');
    $this->view->form = $adminForm;
}

A total of 7 complexity, on the high end of moderate complexity.  You might be looking at this code and wondering what makes it so complex.  It is simple enough.  It’s built to handle a form.  It checks input, creates a new user if the form was submitted in a valid format, and sets up notifications that were obviously a choice in the form.  It finally forwards off to the confirmation.

But, that is actually a lot of complexity.  To test this function, we’d need at least 7 test cases.  Even more so, this method violates some of our rules.  Do one thing.  It does many.  It’s not concise.  And it’s also not just using what is given.

We are going to focus on the complexity portion today.  We are going to bring down the size of this method step by step.

Indents are intents to modularize

Every time you indent your code, you should be thinking about modularizing.  In other words, if you indent your code into a block, you should consider moving that code into it’s own method.

Let’s start with the extremely large if in the middle there.  The one that checks to see if the request is a post.  When we indent that block of code inside, we should ask ourselves if that code inside can be better moved to it’s own method.  The answer is a resounding yes.  Within that block of code, we have 5 levels of complexity.  By moving all that complexity out of the createAction method, we will help decrease the complexity of that single method.  More so, we make that method easier to read, and we are able to compartmentalize the code even more.  We make our code even more reusable.

Where with comments we used the comment as an indicator of a new method, here we use the ideated block of code as an indicator.

public function createAction() // Complexity 1
{
    $adminForm = new Form_User();
    $adminForm->removeElement('dealership_id');

    if ($this->getRequest()->isPost()) { // Complexity 2
        $this->letFormHandleInput($adminForm);
    }

    $adminForm->setAction('/admin/create');
    $adminForm->setMethod('post');
    $this->view->form = $adminForm;
}

protected function letFormHandleInput ($adminForm) // Complexity 1
{
    if ($adminForm->isValid($_POST)) { // Complexity 2

        $mAdmin = new Application_Model_DbTable_Users();
        $result = $mAdmin->createAdmin(
            $adminForm->getValue('name'),
            $adminForm->getValue('email'),
            $adminForm->getValue('password'),
            $adminForm->getValue('role')
        );

        if ($adminForm->getValue('notify') === 'yes') { // Complexity 3
            if ($adminForm->getValue('notifyPhone') === 'yes') { // Complexity 4
                $notification = new Application_Model_DbTable_PhoneNotification($mAdmin);
            } else { // Complexity 5
                $notification = new Application_Model_DbTable_Notification($mAdmin);
            }

            $notification->sendNotification();
        }

        if ($result) // Complexity 6
            $this->_forward('confirm');
    }
}

Referring back to createAction, we see that the complexity has dropped to a mere 2 points.  Granted, letFormHandleInput is 6 points, but we aren’t done yet.  Each method should be easy to understand, and easy to test.  createAction is now incredibly easy to understand.  It creates a new form for display, and if it receives a post request, it let’s the form handle the input.

I’ll admit, I’m not too enamoured with the name of the new method, but it’s simple, and defines exactly what the method does.  More importantly, we’re also passing the form in.  This means that we can pass this method any form.  If something fails, we come out of the method, and continue with the display of the form.  Normally, I’d want to also define letFormhandleInput as

protected function letFormHandleInput(Zend_Validate_Interface $adminForm, array $data)

Here, $adminForm would be explicit in what it is, and I’d pass in the $_POST data, rather than calling it.  In the method itself, I’d swap out the calls to $adminForm->getValue, and instead call directly from $data.  However, this goes beyond our discussion here.  However, I make mention of this because those simple changes are not only easy to make, but value in terms of maintainability, testing, and longevity.

Because letFormHandleInput is still complex, we need to extract some methods out from it as well.  letFormHandleInput is actually wrapped directly around a giant if.  A simple check to see if the $_POST data is valid.  After that point, everything is indented. It also happens that the form doesn’t do much after that.  We grab data from it, but as I noted above, that’s easily removed. Regardless, the sign is fairly clear: indented blocks are what we are looking for.

public function createAction() // Complexity 1
{
    $adminForm = new Form_User();

    $adminForm->removeElement('dealership_id');

    if ($this->getRequest()->isPost()) { // Complexity 2
      $this->validateNewUserDataInput($adminForm, $_POST);
    }

    $adminForm->setAction('/admin/create');
    $adminForm->setMethod('post');
    $this->view->form = $adminForm;
}

protected function validateNewUserDataInput (Zend_Validate_Interface $adminForm, $data)  // Complexity 1
{
    if ($adminForm->isValid($data)) { // Complexity 2
        $this->addNewUser($data);
    }
}

protected function addNewUser ($data)  // Complexity 1
{
    $mAdmin = new Application_Model_DbTable_Users();
    $result = $mAdmin->createAdmin(
        $data['name'],
        $data['email'],
        $data['password'],
        $data['role']
    );

    if ($data['notify'] === 'yes') {  // Complexity 2
        if ($data['notifyPhone'] === 'yes') {  // Complexity 3
            $notification = new Application_Model_DbTable_PhoneNotification($mAdmin);
        } else { // Complexity 4
            $notification = new Application_Model_DbTable_Notification($mAdmin);
        }

        $notification->sendNotification();
    }

    if ($result) // Complexity 5
        $this->_forward('confirm');
}

Once again, we’ve reduced complexity, and in our resulting method, our complexity has gone down.  As well, we’ve also remove the task of creating a new user to another method.  addNewUser does exactly that, it adds a new user and all it’s associate baggage.  I also took this time to better define what we needed, and what we didn’t.  You’ll see I’m pulling directly from $data now.  No need to pass $adminForm around.  Also, letFormHandleInput only requires a Zend_Validate_Interface object now, further decoupling ourselves.  I also took the liberty of renaming letFormHandleInput to validateNewUserDataInput.  This name better describes exactly what it does.  Granted, it also goes directly to creating a new user, but that’s something for another day.

Back on topic, lets take the next step, and refactor addNewUser in the same manner we’ve been.  Let’s see, we have another block of code.  This block sets up notifications.

protected function addNewUser ($data) // Complexity 1
{
    $mAdmin = new Application_Model_DbTable_Users();
    $result = $mAdmin->createAdmin(
        $data['name'],
        $data['email'],
        $data['password'],
        $data['role']
    );

    $this->setupNotificationsForUser($data, $mAdmin);

    if ($result) // Complexity 2
        $this->_forward('confirm');
}

protected function setupNotificationsForUser (array $data, Application_Model_DbTable_Users $mAdmin)  // Complexity 1
{
    if ($data['notify'] === 'yes') { // Complexity 2
        if ($data['notifyPhone'] === 'yes') { // Complexity 3
            $notification = new Application_Model_DbTable_PhoneNotification($mAdmin);
        } else { // Complexity 4
            $notification = new Application_Model_DbTable_Notification($mAdmin);
        }

        $notification->sendNotification();
    }
}

In this case, I just decided to encapsulate the entire notification code into it’s own method.  Here now, setupNotificaitonsForUser can be passed proper data, and from that, we can setup notifications for a users outside the creation of a new user.  This means when you write your code for editing a users notification preferences, you already have your notification setup.  Complexity is at 4, but that brings us to the low category of complexity.  There is one more thing we can do to simplify this code.  However, by this point, you should already see what we can do, and how it will simplify the code even further.

By extracting out the second if/else clause in setupNotificationsForUser, we abstract away the creation process.

All these methods can be applied to other forms of blocks of code.  Every case in a switch should probably have its own method.  Any loop should call out to a method.  At every level of complexity, you want to keep things as simple as possible.  When you read a block of code, it should be simple.  It should do something specific.  It should do is quickly, and it should do it well.

Comments

I once posted  “comments indicate future refactoring.” I want to reaffirm my belief in that, and clearly explain. In order to fully appreciate this, let’s lay down some fundamental beliefs. These are beliefs I have built on experience, and later, I’ll apply them to more general fundamental beliefs like SOLID, but for now, these are simple rules I feel are good to follow.

  1. Do one thing.
  2. Do it well.
  3. Keep it concise.
  4. Use only what is given.

These aren’t overly complex rules. In fact, they are pretty simple. We are going to aim to meet these rules in a rather simple manner: using comments.

Now, let’s define what type of comments I’m referring to. I’m not talking about DocBlock comments. These are comments that allow for parsing by specific tools and allow us to generate documents about the code.

I’m also not referring to comments that repeat the code.

// increment a by 1
$a++;

That is a poor comment, and should be removed. It does nothing except add to confusion.

The comments I’m referring to, however, are a bit more involved. But let’s start with an exercise.

Code should explain what it does. It doesn’t always do this. For example, what’s the purpose of the following code?

if ( preg_match(EMAIL_REGEX, $input) || 
     is_numeric($input) || 
     preg_match(USERNAME_REGEX, $input) ) { /* ... */ }

It’s easy to see what the code is doing, but not the purpose. If we add a comment, it will help matters.

// Check to see if the user input matches one of the searchable data sets
if ( preg_match(EMAIL_REGEX, $input) ||
     is_numeric($input) || 
     preg_match(USERNAME_REGEX, $input) ) { /* ... */ }

Hey! That helps. The code checks to see if the user input matches a pattern for searching a data set. Okay, that helps. But it’s still problematic. The comment isn’t clear. Is it referring to just the if-clause, or is it referring to the block inside the if-statement. Can you trust the comment? After all, as time progresses and code enters maintenance phase, things can and will change. Is it easy to read? It’s not difficult, but when you are read the comment, you then have to mentally associate that with the code. It takes extra effort. And for what? Saying something explicit.

Maybe it’s better that we ask a simple question: What is the comment trying to tell us?

If the input matches a searchable data set, run the blocked code.

Luckily, we can say that succinctly.

if ( $this->inputMatchesSearchableData( $input ) ) { /* ... */ }

// ...
function inputMatchesSearchableData ( $input )
{
    return preg_match(EMAIL_REGEX, $input) ||
           is_numeric($input) ||
           preg_match(USERNAME_REGEX, $input);
}

Using the extra method refactoring recipe, we’ve made our code much, much easier to read. inputMatchesSearchableData says exactly what the method does, and in our original block of code, it reads much easier than the comment did. We’ve also refactored the code that checks to see if the input is searchable. We’ve split responsibility, and suddenly, our original method does one less thing.

Every time I use a comment to explain what code is doing, I always ask myself if it’s code that should be removed. If what the code is doing isn’t directly related to the parent method, the answer is most likely yes.

Often times, the comment itself is a good indicator of the name of the function. Extract the method out, and keep each method small, precise, and simple.

How to get an invite to Forrst

If you don’t know, Forrst is a invite-online developer and designer community started by Kyle Bragger (@kylebragger).  It’s a fairly friendly community, with lots of great feedback from a wide variety of people with all levels of skill.  However, one thing Forrst strives for is a community for all levels of people.  From beginners to experts, designers and developers, Windows/Mac/Linux users, all are invited.  At least, as long as you are willing to put forth the effort.

To get an invite, an existing member must invite you (and invites only come to those who contribute back to the community), or you have to be voted in after having applied.  To join, you go to the “I want to join” page.  On that page, it asks for a few pieces of information, including a  link to something you’ve done.  A github page, open source project, some fancy design.  Something to show that you are a developer or designer oriented person.

The second part, however, is the most important part.  The Forrst join page asks you to provide sample feedback for a random post.

Provide Good Feedback

Listen.  Forrst is all about communication.  Communication between all types of people.  This means feedback is important.  Poor feedback is bad.  We don’t like it.  However, good feedback is cherished, loved, and rewarded.  So, when Forrst asks you to provide feedback for a post on the Join page, realise that real Forrst members are going to review this feedback.  It takes 3 votes to get you in, and chances of finding 3 people to agree that “That looks awesome” is good feedback is slim.

Let me try to make this clear: the feedback you provide on the join page indicates to us the type of feedback you’ll provide once a member.  If the feedback provided is essentially worthless, then making you a member won’t help the community.  A community built up around feedback.  What this means is that you should put some effort into your feedback.

So, here are some tips on how to write good feedback.

1. Pick a post you can answer

Forrst provides a nice little button that you can click on.  It will reload the post to provide feedback for.  Don’t just settle for the first post you see.  Instead, find something you are actually interested in commenting on.  You aren’t expected to comment on everything that gets posted to Forrst.  Not everyone that gets posted to Forrst is going to get good comments.  So, click a way and find something that will allow you to provide good feedback.

2. Why, not what

“That looks awesome!”

The person you are providing feedback too is probably not going to see your post, so you aren’t going to win brownie points for complimenting work.  Though, if the work is good, providing compliments is fine.

However, in this case, “That looks awesome” is fairly useless.  It just explains what you feel.  It doesn’t explain “why” it looks awesome.  Take a few minutes to go into detail about what you like and why.  Even if you can’t express it clearly, try.  Remember, effort is key here.  Explaining why you like something takes a bit more than just saying you like it, but it provides a lot more value.  Others can learn from it, the original poster can focus on those areas.  Who knows, in explaining why you like something, you might learn a little about yourself.

3. We need Effortmore, not Effortless

Okay, that was bad.  But, the point is, your feedback should take a bit of effort.  It is, after all, an application to join.  And regular members like me are going to read this feedback.  I’ll by frank, if I see a 10 word comment, I don’t bother looking at anything else and move on to the next person.  Maybe it’s unfair, but I figure it’s the same amount of effort put into the comment.

It doesn’t take long.  A couple minutes at most.  Ask yourself after you’ve finished the comment if it’s something you’d like to receive.  Does it actually share a unique idea.  Does it express an opinion? Is that opinion explained?

The Elite

Forrst isn’t about being among the elite.  It’s about being among people who are just like you.  Frankly, if you feel applying to the site and actually putting effort into the feedback is too much work, then Forrst probably won’t benefit you.  That’s fine.  However, if you truly want to join, then maybe this little piece of advice will help you avoid a pitfall you might not think about.  From the Forrst join page:

Since Forrst is a very critique- and discussion-oriented community, we ask prospective users to leave sample feedback on a post from one of our members.

We review this.  The members.  And I imagine those of us that review these potential members are very interested in keeping Forrst’s quality high.  We will be picky.  After all, we want to log into Forrst and enjoy what we read there.  We want to be part of a community of people that we’d go out drinking with.  A little bit of effort will go a long way.

Join us!  We aren’t elitist.  We just want you to know that this is the place for you, and we want to know that you’ll get a lot out of this community.  But only as long as you are willing to give.

Of course, as mentioned, the other way to join is to be invited by an existing member with invites.  Like myself.  In that case, beer is ALWAYS a welcomed bribe.

ssh connection sharing

ControlMaster and ControlPath are the ssh config options you are looking to use here.  You’ll want to add them to your ssh config file, which is located in

~/.ssh/config

You simply want to add the following lines to your config file.

Host *
ControlMaster auto
ControlPath ~/.ssh/control_%r@%h:%p

The Host command tells ssh that everything that follows are rules for whatever host is defined.  In this case, the asterisk tells ssh that all hosts should use the conditions present.  ControlMaster enables the sharing of multiple sessions over a single network connection.  ControlPath defines the location the socket connections should be located.  In this case, we are placing them in ~/.ssh/.  %r is token for remote user login, %h is for the host, and %p is the port.  You’ll basically have files that look like this:

~/.ssh/control_username@example.com:22

Normalisation of data

This post used to be called Don’t confuse validation with filtering.  This was meant to be about normalising data, but at the time, filtering was at the forefront of my mine due to work that did some of the things discussed here.  Filtering, validation, and normalising are all essential parts of handling data.  Finally, I want to stress that while normalisation is good, it’s not easy.  Making assumptions is fine as long as you make educated assumptions.  The reason you don’t see normalisation as much as you do is because it’s difficult to get right.  I’ve attempted to be careful in my examples, and have suggested seeking confirmation from the user before continuing.

Brandon Savage posted an interesting article today about how validation blind spots hurt real people.  He gave four succinct rules.

  1. Accept valid data in any form provided by the user.
  2. Where possible, use well-developed validation libraries.
  3. Do not place artificial limits on valid data.
  4. Do place valid limits on specific data.

All good advice.  However, it misses out on filtering.  You see, filtering is different from validation.  Filtering is the art of taking bad data and turning it into good data.  It might sound difficult, but it’s actually easier than you think, and will ease the pain in using your software.  Let’s see some examples.

Usernames

Passwords are secret.  Usernames, however, are usually not.  Even if they are private, like an email address, they are usually not the secure part of the application.  Couple this with mobile devices that auto-capitalize, if you’re requiring case-sensitive usernames, you are making things more difficult then they should be for your users.  It’s a pain having to go back, lower case a single letter at the beginning, and then retype a fairly long password just to log in because your site decided that ‘Jasonlotito’ and ‘jasonlotito’ are not the same user.

At the same time, if I do enter my email address, and you ask for a username, then just go with what I give you.  I presume that their is only one email address per account.  If I’ve forgotten my username for your site, but I know I have an email address tied to an account, just let me use that.  You’ve seen I’ve entered my email address, you can then look up by email address, get my username from there, and proceed to validate.

Credit cards

Credit card numbers follow simple rules.  If I enter my credit card number, you know what card I’m using, so asking me is silly.  You don’t even need to know the full number on the card to figure out the card type.  Once I’ve entered enough numbers in for you to know, simply change the pull down to the correct type, and change the icon.  It will give visual feedback that yes, we know what card you are using.  Their is no reason not to do this.

Also, if I put in dashes, or dots, or spaces, or whatever, you can deal with it.  Ignore them.  Simply use the numbers.  You don’t need to exclude characters, simply white list numbers.

While we are here, expiration dates are easy, and yet, somehow, people get them wrong.  Maybe I’m missing out on some details here, but every credit card I’ve ever seen lists expiration dates as MM/YY.  This is a sign that maybe your forms should follow suit.  This means your month should be ’03’, not March (03).  Just ‘3’ would suffice.  The year should be equally clear: ’15’, not ‘2015’.  I promise you, no one is assuming 1915, or even 2115.

Phone numbers

Filtering phone numbers is hard.  I know.  I’ve had to do it.  The system needed a filtered number because we had to call back in real time from an automated system located in the US.  This mean we needed a properly formatted number that could be dialed.  This isn’t easy, because when you ask for a phone number, people are going to start adding in country codes (but they might).  My suggestion is, unless you are having to do some automated system is to simple not filter numbers unless you can be sure, 100%, of what you are doing.  Even then, you probably don’t need to do it.

Simply ask the user for their phone number, and store whatever they put.  If you need to call them, you can find their country code, or area code if you need to.

Email addresses

Email addresses can be tricky.  You want to make sure they enter in the right email address. A simple mistake can cause problems.  I’ve, on occasion, type in jasonlottio instead of jasonlotito, and the problem was painful to resolve.  On the username side, you can’t do much.  However, after you hit the ampersand, you can start some simple checks.  If someone types in gmail or hotmail or yahoo and leaves off the .com, you can make assumptions.  You can validate these assumptions with the user on the next page.  Transposing letters (htomail) might be something you should look out for, too.  Compile a list of common email providers, and then check how close they are to the users non-common provider name.  If they are close, verify with the user if they didn’t misspell the name.

Assumption

The key to all these filtering techniques is assumption.  It’s not always perfect, but it’s better than failing.  After all, if I’m in Canada, and my phone number is a 10 digit phone number, you can make the assumption that to call me long distance, you’ll need to add a 1.  You can assume that if my credit card number starts with 4111, I’m using a Visa.  Their are lots of assumptions you can make that will make things easier for the user.  Go ahead.  It’s okay.

Object Oriented Programming: Data Mapping

The What

Just because you use objects doesn’t mean you are programming in an object oriented manner.  A function like this defeats the purpose:

public function init()
{
    $user = new User();
    return $user;
}

It immediate creates a dependency.  Whatever object init() is in, we know it relies on User.  It also prevents us from testing init() without User.  User needs to be in place, and it needs to work.  If init() fails, we don’t know if it’s init() or User.

So why bring this up here and now?  It’s in response to a post on Forrst (which you can’t see because it’s probably behind a login wall).  I don’t mean to pick on the person posting the original thread.  It’s simple a case of not knowing.  Anyways, the follow up response to my reply included: “I guess we could also put the database code within the User class itself”.

The basic question revolved around creating a new User object.  Here were my suggestions.

My suggestions

Actually, no, you want to keep your DB code in a different object. You want a Data Mapper instead to actually send the Model into a DB object. Basically:

$user = new User("jasonlotito@gmail.com","password");
$mapper = new UserMapper($user, new MapperStore());
$mapper->save();

Now, you could move that into User.

$user = new User();
$user->save();

You’d have save() accept two optional parameters.

public function save(Mapper $mapper = null, MapperStore $store = null)

Mapper and MapperStore are interfaces, of course. That way a user could send along a different Mapper if need be. I’ll get to why you might want to do this in a bit. Anyways, if $mapper wasn’t set, save would Reflect upon the class name (User) and grab the appropriate mapper: UserMapper, and use that.

MapperStore could default to a DB Connection, and essentially do the same thing, reflect upon the User class, and save the User information to the Database.

So, how does this all become useful?

$card = new Creditcard();
$card->save();

Simple enough. But let’s say you want to store certain information in a different location. Rather than change your Model, or your mapper, or your Store, you do this:

$card->save(new SafeCardMapper(), new FileStore());

So, your Model remains the same, and the mapper merely strips out information that shouldn’t be passed on to the storage object, in this case a FileStore.

Of course, you can set this stuff when you construct Creditcard as well, and it’s a fair argument to make. Personally, I like keeping the stuff close to where it’s being used. It means I can also do this:

$card->save();
$card->save(new SafeCardMapper(), new FileStore());

(Of course, even that smells bad to me, and I’d want to consider chaining mappers and stores together somehow so I could just call save() once. Maybe something like:)

function chainStorage(MapperStore $store, Mapper $mapper = null);
...
$card->chainStorage(new FileStore(), new SafeCardMapper());
$card->save(); // This would save both DB and SafeCard mappers

Anyways, once again, this probably goes above and beyond what you want. =)

Retrospect

Even still,chainStorage() and save() bug me.  I was branching from the OP’s original comment.  I’d much prefer to turn this around, something along the lines of a real Storage system.

$storage = new Storage(array(new FileStore(), new SafeCardMapper()), array(new DatabaseStore(), new CardMapper()));
$storage->store($card);

In this case, Storage simply accepts Store/Mapper pairs (or we could have a addStorage() method which accept both a storage and mapper.  Options are obvious.  Point is, You simply pass along what to store, Storage loads the Store, and uses the mapper to pull in the data.  Card doesn’t know it’s being stored.  Nothing changes.  Mapper would hand Card an updated ID if needed after the update.

Anyways, this is why they invented refactoring.

4JC.IN

4JC.IN is useless for anyone but me, but it’s still useful for me.  A simple tool, a weekend project, and a little exploratory on the web design side of things.  The point is to have ready my own HTML bootstrapping system.  With a quick call from the web directory, I can have everything I need downloaded and setup.  The command is short, and easy to remember.

curl 4jc.in/to | sh

Yes, I’m running a remote file from the shell.  Evil me!

Really though, it’s simple.  The name is meaningful in several ways, too.

  • JC.IN sounds like Jason.  Putting the 4 in front makes it “For Jason”
  • JC is JavaScript and CSS (the original meaning).  For JavaScript and CSS In To here.

I eventually plan on adding some additional things.  Zend Framework download and set up with some basic things I always include, other libraries I’ll pick up.  Font’s I like.  A basic template for HTML.  robots.txt, humans.txt, and privacy.txt files.

All sorts of little things.

In the grand scheme of the web, 4JC.IN doesn’t do anything for anyone.  It’s a tool for me to use.  But it was fun.  It is fun.  It’s a place to put the tools I use.  I may link them into git, so a quick command will fetch me the latest files and what not from a git repository.  I don’t know.  But it’s easy. And I like it.

Eclipse with FTP Support

FTP in Eclipse? This question gets asked a lot.  An FTP plugin for Eclipse that will put your file up onto the server with a save of the file.  I don’t use it much, but for fast projects on my home server, it makes things easy.

The plugin is called ESFTP and still works.  The files were last updated in 2007, but installs and runs just fine on a current Eclipse install.  It has few features, but all the important ones.  The key bindings in the plugin don’t work.  However, you can use the Eclipse Keys options under General preferences to set them.  Just search for esftp.  You’ll probably want to edit the Put File to Server and Save functionality.  That will save the file locally, and FTP it up to the remote server.

It currently only supports FTP and SFTP.  Passwords are stored locally as plain text.  But it works.