i like oak

May 15 2013

oak

 

show maintenance page with flag file using .htaccess

Apr 12 2013

Just a small trick I picked up while planning for site downtime and wanted to remember for myself.

The following snippet will redirect to a (pre-existing) maintenance page if it detects the existence of another file:

RewriteEngine on

RewriteCond %{DOCUMENT_ROOT}/maintenance.flag -f
RewriteCond %{SCRIPT_FILENAME} !maintenance.flag
RewriteRule ^(.*)$ /503.html [L]

This will look for the existence of the file ‘maintenance.flag’ and will put the entire site into maintenance mode by showing a 503 page for all URLs.

To activate it, either create a file called maintenance.flag or run the command:

touch maintenance.flag

In order to deactivate it, just delete the file:

rm -f maintenance.flag

This will bring the site back up.

media center 1.0

Feb 10 2013

Here is my current media center setup:

  • 47″ Vizio LCD TV
  • Apple Mac Mini (Early 2009 Model) running OS X 10.8.2
    • 120GB HDD
    • 250GB External HDD
  • Apple TV

Both the Mac Mini and the Apple TV are hooked up to the TV via HDMI. Movies/music are ripped from DVDs/CDs and stored on the Mac Mini in MP4 format, then added to iTunes with Computer Sharing.

The Apple TV points to the Mac Mini iTunes collection.

Life is good.

 

great design: nathanhoad.net

Jan 17 2013

http://nathanhoad.net/

I randomly came across this site today while searching for ‘git delete tag’ on Google (just making sure I had the right syntax) and ended up really liking the design. What really struck me is that it only has what’s necessary — a truly minimalist layout.

So Nathan, if you ever read this, know that imitation is the best form of flattery. Don’t be surprised if this site ends up borrowing facets from your design.

In any case — great job.

python: states and capitals

Jan 16 2013

So I think I’ve covered the basics of Python pretty well and actually made something useful for myself.

After messing with higher or lower, I thought I’d try my hand at a script geared more towards learning.

Like most people, I’d memorized my states and capitals in grade school but I personally had forgotten some. So it seemed States and Capitals would be the next best thing to come up with.

Thanks to this script, I am back to 100%.

Now I wrote this last week, but in retrospect, I still think it looks great. What that tells me is that I either don’t know any better or Python is a language that is conducive to readability and understanding right out of the box. Either way, I like it.

After I’d written this little beauty, I decided to move onto something a bit more complex.

I spent some time coming up with a minimalistic version of Texas Hold’em but decided against it as a first attempt. On the recommendation of a friend, I decided to give Blackjack a try instead. While it would be great to expand it into a server/client version with proper accounting and such, I figured I should stick to the very basics.

What I’ve done so far is come up with a set of classes — Card, Deck, Dealer, Player, Game (so far). ‘Game’ gets run from the init and determines the number of players (in this case, one) as well as the bet from the player. This is, naturally, where most of the interactivity happens. It is also where ‘Dealer’ is instantiated.

‘Dealer’ gets assigned a ‘Deck,’ and has a variety of methods for interacting with the Deck (deal, reset, shuffle, etc.).

‘Deck’ initiates by going through the ranks and suits to build a full deck of 52 cards:

    def reset(self):
             self.cards = []
             for suit in suits:
                      for rank in ranks:
                               card = Card(rank, suit)
                               self.cards.append(card)

Each card is appended to the Deck’s cards array. The other thing is, since you are able to have multiple decks at a Blackjack table and since you are cycling cards out, I made it so that once the Dealer has dealt his 52nd card, it will generate a completely new deck and then carry on from there.

And that’s where I stopped.

Maybe it was because of something I’d read earlier or someone had said; or perhaps it was something funny I hate, but I just had the strange urge to rewrite the thing in C++. So that’s exactly what I did.

Of course, I haven’t finished but I’ve made some good progress. Currently, I’m building the “option” and “hand” logic for the game.

What’s really nice is how well things translate from one language to the other:

    /**
     Initializes the deck
     */
    void initDeck() {
        this->cards.clear();

        for (unsigned int i = 0; i < this->suits.size(); i++) {
            for (unsigned int x = 0; x < this->ranks.size(); x++) {
                this->cards.push_back(Card(this->ranks[x], this->suits[i]));
            }
        }
    }

This doesn’t come without some adjustment in thinking, however; I went from attempting to use a std::array, to using std::vector but ended up using std:deque. I hadn’t heard of such a thing before, but it seemed to fit best:

deque (usually pronounced like “deck”) is an irregular acronym of double-ended queue. Double-ended queues are sequence containers with dynamic sizes that can be expanded or contracted on both ends (either its front or its back).

The flexibility of these libraries is astounding. I’m sure Java has an equivalent but something about the Java syntax bothers me. I’m not sure why.

In any case, I will see how far I can go with C++ but will probably end up back on the Python path — and for no particular reason.

python: higher or lower

Dec 31 2012

As I said in my previous post, I wanted to try to replicate one of the programs my dad made for me as a kid using Python.

I decided to implement Higher or Lower.

What I learned this time:

  • How functions are defined
  • Putting variables into the raw_input() prompt caused an error, so I moved the text above it
  • Heredoc-style printing is accomplished with triple single-quotes
  • How to build a list and then determine whether a given string is in the hash values

Next I want to do some more exploration into lists/collections.

python: a first attempt

Dec 31 2012

I’ve messed with other people’s Python code with success, but I decided I’d try to try to get comfortable with the basics myself and start churning out a few example scripts the same way I did when I was learning BASIC and Perl.

One of the first things I ever made with BASIC was a “Guess My Number” game, so I made the same thing using Python 2.7:

import sys
import random

r = random.randint(1,10)
guesses = 0

while True:
        n = raw_input("Please guess a number between 1 and 10: ")
        n = n.strip()

        try:
                n = int(n)
        except ValueError:
                print "Please enter an integer!"
                continue

        if 1 <= n <= 10:
                guesses += 1
                if n == r:
                        print "That's right! And it only took you {guesses} guesses!".format(guesses = guesses)
                        break
                else:
                        print "Try again!"

        print "This is guess #{guesses}!".format(guesses = guesses)

Please excuse any errors I may have made in my first attempt here; I am still learning.

What I learned from this exercise:

  • The input() function allows raw input that can be executed, so raw_input() is highly preferable
  • As expected, Python is still dynamic but not as lenient as PHP when it comes to type conversion
  • Print formatting makes sense to me. I’m sure there’s a bit of a hill to climb in terms of understanding, but the basics are great
  • Comparing integers isn’t like the languages I’m used to — (if 1 <= n <= 10) — it’s more like Math, which makes me feel smarter already
  • The random library has more features than I can shake a stick at
  • True and False are capitalized, as opposed to C-style languages, just as most other constants are

All in all, I’m happy with these results.

Next, I want to try to implement “Higher or Lower,” or some sort of math game like my Dad made for me when I was younger.

P.S. In case anyone was wondering, this is what the sample output looks like:

$ python bar.py
Please guess a number between 1 and 10: 3
Try again!
This is guess #1!
Please guess a number between 1 and 10: 1
Try again!
This is guess #2!
Please guess a number between 1 and 10: 8
Try again!
This is guess #3!
Please guess a number between 1 and 10: 10
Try again!
This is guess #4!
Please guess a number between 1 and 10: 3
Try again!
This is guess #5!
Please guess a number between 1 and 10: 2
That's right! And it only took you 6 guesses!

the importance of keeping environments as similar as possible

Jun 09 2012

Yesterday, I was asked to solve a problem on a production site where mobile users were being improperly redirected. Aside from the human factor, this should have been an issue that could have been resolved in 30-45 minutes, tops. Unfortunately, it ended up taking around five hours.

The reason it took so long was simply that my development environment was significantly different from the production environment and would have to be altered to match. Whether it be due to lack of information or a loose set of standards for developers, sadly this is the case for most of the devs working on the site as well. Our environments are only set up to work for the feature we are developing.

So once I determined that I’d have to take a number of steps to bring my development environment up to speed and began, I started to run into trouble that would take its own time to troubleshoot. Rather than waste time stepping through code to figure out that issue, I decided to undo the damage to my local environment and to try debugging the issue on a QA server. This meant getting SSH access, using VIM to edit each file while keeping close track of everything I’d changed so I could mimic it locally, then committing those changes up to a hotfix branch once I’d determined the solution.

While this did end up working, it is a horrible way to fix things. Sure, it works (most of the time) during an emergency, but it just means we aren’t prepared. If I hadn’t been careful, I could have missed vital information when bringing the changes onto my local machine and bad things might happen (or I’d just have to spend more precious time figuring out what happened).

To make a long story short, this issue can be avoided if developers are either given development machines/slices that are as close to production as possible, or if their environments are kept the same way. Moving forward, I will make it a point to demand this is our basic practice.

JIRAssic Park: Subtaskadon

May 24 2012

JIRAssic Park: Subtaskadon

Subtaskadon is a little Chrome plugin for use on JIRA. This enables you to drag and drop subtasks rather than using the annoying arrows on the side.

Download Subtaskadon 1.0.1

If you notice any issues, please let me know.

magento: adding / deleting attribute options programmatically

Apr 24 2012

I spent about a half an hour getting Magento to properly add attribute options programmatically and then a couple more hours trying to get it to properly remove the default set. I’m in the midst of updating the RMA module for Magento Enterprise 1.11 and one of my tasks is to change the “Reason for Return” options that appear in the dropdown for individual return items.

So in an effort to save myself (and perhaps others) the trouble in the future, I wanted to quickly lay out how I got it working.

Setup

Perhaps one of the most important parts of making this sort of update easy is modifying your migration scripts to use the right model for setup. In this case, I used the setup model included with the RMA module itself — Enterprise_Rma_Model_Resource_Setup. The following example assumes you have your own RMA module in the “namespace” namespace.

config.xml:

<resources>
    <namespace_rma_setup>
        <setup>
            <module>Namespace_Rma</module>
            <class>Enterprise_Rma_Model_Resource_Setup</class>
        </setup>
        <connection>
            <use>core_setup</use>
        </connection>
    </namespace_rma_setup>
</resources>

The Install Script

Now we’re going to delete the old options and then add the new ones. Note how this process is wrapped in a database transaction, just in case. Moreover, look at the highlighted lines. On line 29, you’ll notice that the setup model we’ve chosen allows us to use the addAttributeOption method (as well as various other useful methods), which lets us easily delete the existing options.

Next, on line 43 the setup script also gives us the getDefaultEntities method which returns the array representation of the attribute structure for RMA’s. From this, we can fill in the blanks that Magento expects to be there for our option array. Then on line 53, we merge our arrays together to feed Magento what it needs to complete the deletion of the previous options and to add the new options in the order we give it.

/** @var $installer Enterprise_Rma_Model_Resource_Setup */
$installer = $this;

/** @var $connection Varien_Db_Adapter_Pdo_Mysql */
$connection = $installer->getConnection();

$installer->startSetup();

try {
    $connection->beginTransaction();

    // Delete existing options

    /** @var $rmaItems Enterprise_Rma_Model_Item_Attribute */
    $rmaItems = Mage::getModel('enterprise_rma/item_attribute');

    $reason = $rmaItems->loadByCode('rma_item', 'reason');

    $selectOptions = $reason->getFrontend()->getSelectOptions();
    $optionsDelete = array();

    foreach($selectOptions as $option) {
        if ($option['value'] != "") {
            $optionsDelete['delete'][$option['value']] = true;
            $optionsDelete['value'][$option['value']] = true;
        }
    }

    $installer->addAttributeOption($optionsDelete);

    // Add new options

    $optionsArray = array(
        'Wrong size',
        'Wrong item',
        'Does not fit',
        'Wrong SKU',
        'Warranty Claim',
        'Did not order',
        'Exchange',
    );

    $defaultEntities = $installer->getDefaultEntities();

    $reason = $defaultEntities['rma_item']['attributes']['reason'];
    $reasonOptions = array(
        'option' => array(
            'values' => $optionsArray,
            'delete' => $optionsDelete,
        ),
    );

    $reasonAttribute = array_merge($reason, $reasonOptions);

    $installer->addAttribute('rma_item', 'reason', $reasonAttribute);

    $connection->commit();

} catch (Exception $e) {
    $connection->rollBack();
    Mage::logException($e);
}

$installer->endSetup();

Older »