07 Sep 2011Ever tried to select the checked radio
button of a form using jQuery ? Well I did, and hundreds of time before, and never ran into any issues.
Today's the first time, and it involves my dear friend IE8. Seems like either jQuery or IE8 had trouble with my radio
button selection.
I finally managed to get what I wanted but with a sightly different syntax for IE8.
First, the markup
<form id="myForm">
<input name="data[Payment][value]" id="payment_1500" type="radio" value="1500" />
<label for="payment_1500">1500</label><br />
<input name="data[Payment][value]" id="payment_2000" type="radio" value="2000" />
<label for="payment_2000">2000</label><br />
<input name="data[Payment][value]" id="payment_5000" type="radio" value="5000" />
<label for="payment_5000">5000</label><br />
<button id="test">Select</button>
</form>
Pretty simple, isn't it ? I only have three radio
buttons, and I would like to get the selected value when pressing the Select button
.
What should work everywhere
The following code is pretty straightforward and I expected it to just work.
var selected1 = $('#myForm input[name="data\\[Payment\\]\\[value\\]"]:checked');
var value1 = selected1.val();
console.log(value1);
Note that we have to double escape the [
and ]
characters and wrap in quotes the name
value. Nothing fancy, just classic string protection. This code works perfectly on Firefox and Chrome, and I deployed it in production for a few weeks.
Then, I got report of users that would have love to use the form, but got an error because no value was selected. I tested and tested it again without finding the cause. Then it occurs to me that all those reports came from user using IE8.
So I rebooted my VM, launched IE8 and was able to reproduce the bug on my first try.
What the heck is IE8 doing ?
Well, that's a deep question, and I've ask this myself countless times before. Once more, IE is doing things in its own weird way.
After some fiddling, I managed to make it work, by just slightly altering the syntax.
var selected2 = $('#myForm input[name="data\\[Payment\\]\\[value\\]"]').filter(':checked');
var value2 = selected2.val();
console.log(value2);
Yep, that's right, I simply moved the :checked
selector in its own filter
call and it worked. Took me a while to figure, but this finally turned out to be an easy fix.
You can test it yourself with this jsFiddle example. Don't forget to enable the log panel in IE8 by pressing F12 before running the code.
30 Aug 2011Default Flash security config prevents you from loading content from an external website when running a Flash file locally.
To prevent this, you have to go to this link and add the directory where your file is located.
Fortunatly, you can specify a top level directory and all subdirectories will inherit the authorization.
If you don't do this step, your Flash file will refuse to download external content.
26 Aug 2011I've never used puppet before this project. It in installed on our main server, monitoring the whole pool of servers used to host our game. It includes php hosts, sql data nodes and sql front servers as well as a couple of load balancer.
Puppet keeps a snapshot of the config that must be deployed on each server and keep them in sync. We just have to edit the puppet files, and the same config is replicated on each server.
I did not install puppet, our sysadmin did it, and I have no idea how he did it actually, I'm just fiddling with specific configurations here. But as it took me quite a bit to grasp, here is how it works, and some quirks to be aware of.
The puppet files
Puppet is installed in /etc/puppet
. Our different modules are stored in/etc/puppet/modules
. A module is a set of programs and config files that work together.
For example, we have one for the loadbalancers
, one for the http
part, one for mysql
, and even one for vim
. I'm going to focus on the http
one, because it contains config files for memcache, lighttpd and php.
Modules are split in two parts : the manifest and the files. You can find each in /etc/puppet/modules/{modulename}/manifests
and in /etc/puppet/modules/{modulename}/files
.
As you can imagine, the manifest contains a list of directive telling puppet what programs need to be installed, what directories need to be present (with specific user and group) as well as what files need to be copied.
The files/
directory contains all the files that puppet might need for this module. It will copy them from this directory to the final servers, according to the manifest config.
Manifest syntax
The manifest uses a very simple syntax, using blocks of key => value
to define config. I've mostly use the simple file
directives, to copy files and create directories.
One can also use package
directories, to install things and handle dependencies, but I haven't messed with that (yet) so I won't talk about them here.
Here are a couple of simple file orders :
file { "/etc/lighttpd/scripts":
ensure => directory,
owner => root,
group => root,
mode => 550
}
file { "/etc/lighttpd/lighttpd.conf":
owner => root,
group => root,
mode => 644,
source => "puppet:///http/$target/lighttpd/lighttpd.conf",
require => Package["lighttpd"],
notify => Service["lighttpd"],
}
The first part will create a/etc/lighttpd/scripts
directory on the final server, with a user and group root
and 550 permissions mode.
The second part will copy the file stored in /etc/puppet/modules/http/{target}/lighttpd/lighttpd.conf
on the puppet server to /etc/lighttpd/lighttpd.conf
on the final server. I'm not quite sure what the require
and notify
lines are for.
$target
is a special variable. In my case it can take either the value valid
or prod
, allowing me to define custom config based on the environment. Valid and prod environment are supposed to have the exact same config, but separating them allow for testing a config change in one environment without potentially breaking the working prod environment. I'm not exactly sure how this $target
var is set, this is one more thing our sysadmin did. Thanks to him.
Pulling changes
Puppet is configured to check its servers every 30mn. If some config files changed, or a directory isn't there anymore, it will recreate them, according to the manifest. But you can force the pulling of changes from the final servers themselves. Just log to one of the servers puppet is monitoring and type
The --test
argument is a bit misleading. It actually does not seem to do any test at all, it simply pulls the puppet files from the puppet server and updates its files accordingly. It will notify you of any errors it might find, and even print a diff
of the files changed.
One last thing
I've lost 30mn checking and re-checking my puppet file because my php.ini
changes weren't updated. In fact, I just forgot to reload lighttpd
...
26 Aug 2011On HappyLife, we used to use Memcache to handle the php sessions. We discovered a while back after much trouble that Memcache might not have been the best solution for sessions on a high traffic site. But this will be the subject of another post.
Anyway, I switched back to classical session, stored on disk.
Here's the relevant php.ini
config
[Session]
session.save_handler = files
session.save_path = '/tmp/php'
/tmp/php
should be writable by www-data
, so
$ chown www-data:www-data /tmp/php
24 Aug 2011A while back, I ordered a Dreamplug. It's a micro computer more or less the size of a hard drive. It uses very little electricity, has ethernet, usb, sata and sd ports as well as bluetooth and wifi connectivity.
I wanted one to primarily work as a git and mercurial server to host my repos, as well as host a few websites I might be working on. As it's not the more powerful of device and will be using my home ADSL connection, it's also a good fit for testing web performance optimisations.
Actually, I have a lot of ideas for this plug (ssh tunnel, dropox-like server, etc).
First, let's establish a connection
To do any of that, you first have to log into your device. This first step might have been the one that caused me the much trouble.
I simply wanted to connect to the plug through SSH. I didn't want to plug it to my router because it would then have emit an open wifi network. Instead, I wanted to access it through a simple ethernet cable.
To do that I had to plug the Ethernet cable on the second slot (the one the closer to the lights) on the plug, then turn it on. I didn't manage to connect with the cable in the other plug nor if I plugged it after turning the device on.
What took me a while to figure is that the Dreamplug have its own ip address : 10.0.0.1
. To enable transfer between your host and the plug, you have to set a fixed ip address to your ethernet (mine was eth0
)
$ sudo ifconfig eth0 10.0.0.2
I chose 10.0.0.2
because it's close to the Dreamplug address and I will remember it better, but you can actually choose anything you want, it absolutely does not matter.
Connect through ssh
Once the first step is done, you are now able to connect to the device through SSH.
The default password is nosoup4u
. I chose to keep it for now, as I'll have to type it a lot, but I'll change it once everything is installed.
First thing you should do is create a new user. Using root
for the day to day activities is a bad habit.
You'll be prompted for password and other misc informations. Once done, we will give sudo power to this user.
On others Ubuntu machines, I usually do a adduser pixelastic admin
or adduser pixelastic sudo
, but this didn't work here. There wasn't even an admin
group, and adding to sudo
didn't seem to change anything.
Instead, I had to edit the /etc/sudoers
file, using visudo
.
If you've never used vi
before, press i
to enter edit mode, and vi
will acts as a common editor. Press Esc
when you've finished, then :x
to save and exit
I added a new line just below the one starting with root
and copied it, simply by changing the user name.
root ALL=(ALL) ALL
pixelastic ALL=(ALL) ALL
Your new user should now have the sudo powers.
Unfortunatly, on my build one more thing was missing. It might not be the case on your plug, but we'd better check.
$ which sudo
/usr/bin/sudo
$ ls -l /usr/bin/sudo
-rwxr-xr-x 1 root root 114976 2011-02-14 08:08 /usr/bin/sudo
Check the permissions, if you have this output, then something is wrong. The sudo
executable should have the s
permissions, otherwise it won't work.
$ chmod u+s /usr/bin/sudo
Now, we can logout from the root
session, and login back with our new user
$ ssh pixelastic@10.0.0.1
$ sudo pwd
/home/pixelastic
What next ?
Now, one last sensible thing to do is to change the root password for something stronger, and even disable login as root
through ssh. To do that, simply edit the /etc/ssh/sshd_config
file and set PermitRootLogin
to no
.
You could also add your keys to the plug to avoid typing your password, install your softs, and link the plug to your router to make it accessible from the outside.
I plan to replace Ubuntu for Debian on my Dreamplug, I'll post about that later.