The best way to add a million names to your sandwich marketing list is to give out a million sandwiches. More marketing dollars should be spent like this. Not only do you get a lot of names to market to, but you get a lot of foot traffic in your stores. All for less money than any decent ad buy would cost. It may not be as clever as deleting friends on Facebook, but I bet it’s more effective at getting actual traffic.
No.
This is part of Safari’s newish support for a database storage feature that’s part of the HTML 5 spec.
This message pops up twice for every page load on the InfusionSoft “ideas website“. Here’s an idea: stop asking to store a megabyte on my computer on every goddamn page load. Technically this is the fault of UserVoice, but I don’t miss many chances to make fun of amateurs like InfusionSoft (and hey, it’s their logo on the page right?).
I finally caved and accepted the databases so I could inspect them and see what was so important that I had to be asked twice on every page load… It’s to use a library called PersistJS. Only they aren’t really using the tables for anything, other than creating them and keeping basic session data. Session data that would easily fit in the 4KB limit of a cookie. In fact there is also a cookie set that contains the same exact data. Good thing we have those databases!
At the moment these databases are technically slick (SQL in JavaScript!), but functionally useless thanks to lack of support and the dire warnings for those that do support it. I’m all for the nerd-cred, but please at least store the fact that I rejected your DB in my cookie session so that you don’t ask repeatedly. Once is annoying, every page load is intolerable.
UserVoice fail.
I needed a simple file-based cache for a PHP project the other day and figured I would share. The production code is slightly different, made into a CodeIgniter library*, but this version is now live up on the Mobile Drudgereport. It’s working well there, serving up millions of hits per month with no noticeable server load.
If you’re looking to cache rendered templates or total site caching, this is probably not the code for you. I wrote this to cache an XML data feed and then render the rest of the page dynamically. It can cache any PHP data type though so a good use case is to cache the results of database queries.
Usage is dead simple, initiate the class with a path to your cache location and then use JG_Cache::get() and JG_Cache::set() to play with your data. Each cache has a key, which you set. If you’re caching something with user data, make sure to put their unique ID in it. This is Memcached style, really basic. In fact you should be able to move to Memcached without much trouble if in the future you need higher performance. Cache data defaults to last an hour, but that’s flexible in the get() method (the second argument is the number of seconds you want the data to stick around).
Example usage
$cache = new JG_Cache('/path/to/cache'); //Make sure it exists and is writeable
$data = $cache->get('key');
if ($data === FALSE)
{
$data = 'This will be cached';
$cache->set('key', $data);
}
//Do something with $data
While we’re using a string here, the class uses serialize() and unserialize(), so you can cache whatever PHP data types you want. Objects are especially handy, at least for what I needed to use it for. The cache files themselves are stored as a hash of the key name so you don’t have to worry about file naming rules.
It’s designed to fail silently since your code should always be able to access the data without a cache. The beauty of it is there are only a few extra lines to implement the caching and the rest of your code can remain the same.
Cache class source code (also on Github)
class JG_Cache {
function __construct($dir)
{
$this->dir = $dir;
}
private function _name($key)
{
return sprintf("%s/%s", $this->dir, sha1($key));
}
public function get($key, $expiration = 3600)
{
if ( !is_dir($this->dir) OR !is_writable($this->dir))
{
return FALSE;
}
$cache_path = $this->_name($key);
if (!@file_exists($cache_path))
{
return FALSE;
}
if (filemtime($cache_path) < (time() - $expiration))
{
$this->clear($key);
return FALSE;
}
if (!$fp = @fopen($cache_path, 'rb'))
{
return FALSE;
}
flock($fp, LOCK_SH);
$cache = '';
if (filesize($cache_path) > 0)
{
$cache = unserialize(fread($fp, filesize($cache_path)));
}
else
{
$cache = NULL;
}
flock($fp, LOCK_UN);
fclose($fp);
return $cache;
}
public function set($key, $data)
{
if ( !is_dir($this->dir) OR !is_writable($this->dir))
{
return FALSE;
}
$cache_path = $this->_name($key);
if ( ! $fp = fopen($cache_path, 'wb'))
{
return FALSE;
}
if (flock($fp, LOCK_EX))
{
fwrite($fp, serialize($data));
flock($fp, LOCK_UN);
}
else
{
return FALSE;
}
fclose($fp);
@chmod($cache_path, 0777);
return TRUE;
}
public function clear($key)
{
$cache_path = $this->_name($key);
if (file_exists($cache_path))
{
unlink($cache_path);
return TRUE;
}
return FALSE;
}
}
I got quite a kick out of the Ad Council’s latest campaign, That’s Not Cool. The whole site is oddly entertaining, but the Callout Cards are gold. I can almost imagine adults in a boardroom voting on what cards get put on the site. They probably even consider this legitimate work. It’s 501(c)(3) masturbation–nothing actually gets done but it makes you feel good to be involved in.
My personal favorite, the statue of David (oddly horizontally flipped) with a bath towel covering his penis. If that won’t stop kids from sending naked photos, what will? They should have just created a “bully me” sign and given it out with promise rings.
I have a Larry David like knack for finding myself in interesting situations without trying. On the way home from food shopping this afternoon I witnessed a hit and run. An old junker vs. an E-Class. Karma was working perfectly today as one of the two E-Class passengers had a digital camera and running shoes. A green light meant I didn’t see the final conclusion, but the junker got chased down on foot and photographed at very close range. Justice in action.
I had to set up a simple threaded comments system in a PHP project yesterday and didn’t find any standout options. The presentation requirements were pretty simple and my only real code requirement was to use a single SQL query (using a self-referencing parent_id foreign key field) to fetch the comments. I didn’t want to mess with a full-on tree either (that would be overkill as I’d never need to walk up the tree, just needed to show all the comments for a single object).
What I found was either laughably pathetic or various WordPress plug-ins that were even more hard to parse than the main WordPress codebase.
I ended up writing a simple class that accomplishes it without getting too fancy. The main meat of the class is a recursively called method (Threaded_comment::print_parent()
) that prints a comment and then its children. The class as I have shared below will need modification depending on how you use it, for my use I modified it to be a CodeIgniter library and output comments using a View file. Simple enough. It’s set-up as an example, not a full-on third party library. I am sure I’ll have to hack on it more, but hopefully it helps someone.
Update: Note that this code is not running the comments you see below–that’s powered by WordPress’ native comment feature (which also does threading). The code below is if you want to setup a threaded comment system in your own PHP application. You could certainly attempt to run it on a WordPress install, but there would be no point because the feature is already seamlessly built-in.
class Threaded_comments
{
public $parents = array();
public $children = array();
/**
* @param array $comments
*/
function __construct($comments)
{
foreach ($comments as $comment)
{
if ($comment['parent_id'] === NULL)
{
$this->parents[$comment['id']][] = $comment;
}
else
{
$this->children[$comment['parent_id']][] = $comment;
}
}
}
/**
* @param array $comment
* @param int $depth
*/
private function format_comment($comment, $depth)
{
for ($depth; $depth > 0; $depth--)
{
echo "\t";
}
echo $comment['text'];
echo "\n";
}
/**
* @param array $comment
* @param int $depth
*/
private function print_parent($comment, $depth = 0)
{
foreach ($comment as $c)
{
$this->format_comment($c, $depth);
if (isset($this->children[$c['id']]))
{
$this->print_parent($this->children[$c['id']], $depth + 1);
}
}
}
public function print_comments()
{
foreach ($this->parents as $c)
{
$this->print_parent($c);
}
}
}
Here’s the example usage with the data provided as an array. Remember that if your data is in another format you’ll have to modify the class.
$comments = array( array('id'=>1, 'parent_id'=>NULL, 'text'=>'Parent'),
array('id'=>2, 'parent_id'=>1, 'text'=>'Child'),
array('id'=>3, 'parent_id'=>2, 'text'=>'Child Third level'),
array('id'=>4, 'parent_id'=>NULL, 'text'=>'Second Parent'),
array('id'=>5, 'parent_id'=>4, 'text'=>'Second Child')
);
$threaded_comments = new Threaded_comments($comments);
$threaded_comments->print_comments();
Example Output:
Parent
Child
Child Third level
Second Parent
Second Child
Email feedback loops (FBL) allow you to receive reports when a message your domain has sent gets marked as spam. As this directly affects your domain’s deliverability, it’s a great tool to have. AOL users especially are known to use the spam button instead of “delete”. If you treat FBL reports the same as if the user clicked unsubscribe, you’re way ahead of the game.
To that end and because I’m always having to look up this data, here are the forms to get hooked up with feedback loops for the top email providers:
Don’t forget to set up a SPF record too. Email deliverability is still way harder than it needs to be, but that’s not going to change anytime soon.
The ultimate show of love for a product is when you buy it again. That’s what I just did with the OXO POP storage line. They are quite simply brilliantly designed food storage containers. I had set of glass containers that worked fine until dropping one and slicing my wrist open wide enough to get a view of my ulna. Another storage vessel, this time plastic, was required. Enter the OXO POP line:
It’s airtight, but releases with a tap on the centered round button (which when sealed lays flush, the above photo is of an unsealed container). Reverse the process to seal. I had some smaller Michael Graves designed containers from Target, but OXO has the edge for being square (more volume in less space) and a seal/unseal mechanism that opens up for easy cleaning. The Target containers are great until you wash the lid and water gets stuck inside. The OXO’s are also designed to play nice with each other, having dimensions that stack evenly. It’s also nice that you can have the lid in place and not have it sealed (which you can’t do with the Graves design).
I have one on my counter now (AP flour), two more on their way (bread flour, white sugar) and I wouldn’t be surprised if I order a couple more as I find uses for them (brown sugar? legumes? cookies?). Little bits of happy.
I have been on a bread making spree as of late, even going so far as to make it a point to not buy bread anymore. This is my first stab at a baguette form factor. It’s a high-hydration (80%) dough, counter risen and then cold stored. It was cooked on pre-heated cast iron in a humid oven. The same dough works fine for a boule or even smaller roll type applications. I have yet to try it for pizza crust, but it seems like it would work (with some extra effort fighting the stickiness that comes with a high-hydration dough).
Quite happy with it and I find this shape perfect for sandwiches. Even better that I can make a stubby baguette like this and not have to throw out the part that always goes stale on me if I buy a full-size loaf.
I have been programming exclusively in Python (mostly with Django) for a while now, but my newest project is PHP. Getting my feet wet again in PHP made me realize how spoiled I had gotten so I did some framework shopping to help ease the pain. I settled on CodeIgniter thanks mostly to it being light-weight and easily expandable. While there are a lot of nice parts there is also a lot of unnecessary typing, mostly centering around models.
Models seem like an afterthought. Example A: they are advertised as a place to keep your methods to interact with the DB, not as a model of your data. This means tons of repetition by making wrapper methods for all your data. Example B: models aren’t hooked up with forms or validations even though most likely you’ll want both for every model you have. With a little bit of cleverness these annoyances can be greatly reduced, I’m not sure why this hasn’t been done officially.
Here’s what I’ve done so far, but this is only after a couple days of tinkering so I’m sure there will be changes:
I extended the native Model and Form libraries to work together. Each Model class has a public member declaration detailing the fields along with some meta information, namely validation rules. Using that info it only takes a few abstracted methods to automatically create and validate forms from models. For example, my registration method:
function register()
{
require_anonymous_login();
if ($this->User->is_valid()) {
$this->User->create();
}
render_to_response('auth/register', array('title'=>'Register'));
}
That saves quite a bit of time, but we also have to construct the form. The built-in helpers for this are pretty pathetic, you end up calling tons of functions with nasty arrays for options (not CodeIgniter’s fault, PHP doesn’t support calling functions with named parameters). There’s one function for the form field, another for a possible error message and yet another for the value in case there was an error. For each field. You end up with something obscene like:
echo form_label('Username', 'username');
echo form_error('username');
$data = array( 'name'=>'username',
'value'=>set_value('username'),
'size'=>'25');
echo form_input($data);
That can be automated somewhat, but you need to call the right ending function for each field type (form_input(), form_checkbox(), form_password(), etc). Half the fun of having a data model is this kind of stuff should be automated for you, a boolean column should know to represent itself as a checkbox, a varchar as an input, text as textarea, etc. Not to mention you have to create (and update!) the forms by hand since CodeIgniter “models” don’t describe your data.
To ease the pain I created a method that returns the fields in a model and then a global wrapper function that detects the data type and calls the right form helper function. For forms where you need some customization (fields next to each other and so forth), you can still get most of the benefit just without the savings of the loop. The entire registration form (labels, errors, different field types, etc) is now this:
<?php foreach(form_fields_from_model('User') as $field): ?>
<div class="form-row">
<label for="<?=$field['slug']?>"><?=$field['name']?></label>
<?= full_field($field); ?>
</div>
<?php endforeach; ?>
It’s still rough around the edges, but it’s nice to have the validation in the model and to have model forms update automatically with schema changes. Still not close to the elegance of Django, but it should save quite a bit of time (which is all I’m looking for frankly).