October 2007

Convert has_and_belongs_to_many to a has_many :through association

So there are plenty of resources out there to learn how to use has_many :through associations.

I followed them over and over again but couldn’t get my code to work. I knew I had the basic structure setup correctly, since the examples are pretty straightforward, and the concept is not difficult. My has_and_belongs_to_many code originally looked like this:

class Soda < ActiveRecord::Base
  has_and_belongs_to_many :distributors
end
 
class Distributor < ActiveRecord::Base
  has_and_belongs_to_many :sodas
end

Of course there was also a many-to-many join table migration:

class DistributorsSodasJoinTable < ActiveRecord::Migration
  def self.up
    create_table :distributors_sodas, :id => false do |t|
      t.column :soda_id, :int
      t.column :distributor_id, :int
    end
  end  
 
  def self.down
    drop_table :distributors_sodas
  end
end

This works quite nicely:

>> Soda.find(1).distributors
=> []

Later found that I needed to add attributes in the join table to associate extra fields on the Distributors <-> Sodas relationship. has_and_belongs_to_many does not have a Rails way to access those extra fields in the join table. I’ve successfully done it through SQL, but much guilt and remorse lead me to finally learn has_many :through.

This was my best initial attempt:

class Soda < ActiveRecord::Base
  has_many :distributors_sodas
  has_many :distributors, :through =>; :distributors_sodas
end
 
class Distributor < ActiveRecord::Base
  has_many :distributors_sodas
  has_many :distributors, :through => :distributors_sodas
end
 
class DistributorsSodas < ActiveRecord::Base
  belongs_to :soda
  belongs_to :distributor
end

All goes well until I try to do a quick test:

>> Soda.find(1).distributors
NameError: uninitialized constant Soda::DistributorsSoda
...
        from (irb):4

Umm… what? I never tried to instantiate an object of the type Soda::DistributorsSoda. Instead, I was simply trying to use the DistributorsSodas ActiveRecord object, right?

It turns out that has_many :through (apparently) can’t handle using the join tables created by has_and_belongs_to_many. Its just a naming issue - has_many :through will work fine using a one-to-many join table like distributor_sodas (note the missing ’s’ on distributor). If you need a many-to-many join, you have to rename the table to fix the (pluralization?) problem. I deleted the DistributorsSodas model and created the Store model.

class Soda < ActiveRecord::Base
  has_many :stores
  has_many :distributors, :through => :stores
end
 
class Distributor < ActiveRecord::Base
  has_many :stores
  has_many :sodas, :through => :stores
end
 
class Store < ActiveRecord::Base
  belongs_to :soda
  belongs_to :distributor
end

This proved a much better result:

>> Soda.find(1).distributors
=> []

In the end, the association naming convention actually make more sense. I was bummed to have to change the table/model names though.

Please comment if you know how to create the association without changing the model name.

rails

Comments (6)

Permalink

Use Mozilla Firefox under WINE to reach those Windows-only sites

Being denied based on operating system

Its long been known that you can use something like the User Agent Switcher to make it appear like you’re using a different operating system than you really are. This can be useful on some websites that insist you need to run Windows - when in fact Linux or practically any other operating system will work fine.

But for those sites that are actually telling the truth, you can often run Firefox under WINE with very good results. Just head over to www.getfirefox.com and download the Windows binary. The site actually does its own OS detection, so you’ll probably want to choose “Other Systems and Languages.” Once you have it, make sure you have WINE installed. I also installed the MS truetype fonts because it looks terrible otherwise.

# apt-get install wine msttcorefonts

And then start the installer

$ wine Firefox\ Setup\ 2.0.0.7.exe

You’ll go through the normal install process, and when you’re finished you’ll see a new Firefox icon on your Linux desktop:

Windows Firefox on my Linux desktop

Double-click it and you should be good to go! If you want to visit a site with audio, you’ll want to run winecfg, visit the Audio tab, choose your settings (I just left the defaults) and click Apply. Otherwise you might run into some issues with Firefox crashing. If its any consolation, Firefox will crash on certain sites powered by Move Networks in Windows, too, if there is no audio driver installed. This is probably related to the Firefox extension you must install to view the site.

linux
wine

Comments (0)

Permalink

Apt-get DOES have an option for automatic security updates

I recently wrote about the cron job that I run to keep my Debian and Debian-like servers up to date automatically.

It turns out that apt-get (or libapt, more specifically) has a mechanism to automatically download and install security updates. This will work across package management applications (apt-get, synaptic, aptitude, etc) and is very simple to setup. Edit /etc/apt/apt.conf.d/10periodic to include the following:

APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Download-Upgradeable-Packages "1";
APT::Periodic::AutocleanInterval "0";
APT::Periodic::Unattended-Upgrade "1";

Your distribution should already have a cron job (usually in /etc/cron.daily/apt) that runs every day and updates as per these configuration options. To see the entire config, run

$ apt-config dump

For those interested, this configuration setting is what Synaptic sets when using its “Install security updates without confirmation” option.

Thanks to g for pointing this out.

linux

Comments (1)

Permalink

The new Ubuntu is coming already

If you didn’t already know, Ubuntu 7.10 Gutsy Gibbon will be coming soon!

Maybe I’ll finally get suspend support for my dual core Dell XPS desktop?

Any guesses if the Ruby and Rails packaging will be any better?

linux

Comments (0)

Permalink

Using syntax enable and other vim commands in Ubuntu and Debian

So I ran into the strangest thing today. Vim complains because I tried to use ’syntax enable’ in my .vimrc.

$ vim myfile.rb
Error detected while processing /home/solipsistic/.vimrc:
line    1:
E319: Sorry, the command is not available in this version: syntax enable
Press ENTER or type command to continue

Apparently Debian-like distributions don’t package the full version of vim by default. They pack a ‘lite’ version.

# apt-get install vim

And you’ll be rolling again.

linux

Comments (0)

Permalink

Installing Mongrel on Ubuntu Feisty with rubygems

When I recently re-installed Ubuntu 7.04 (Feisty), I found that I couldn’t build the native fastthread gem (a required dependency). If you get this error:

Building native extensions. This could take a while...
extconf.rb:1:in `require': no such file to load -- mkmf (LoadError)
from extconf.rb:1


ERROR: While executing gem ... (RuntimeError)
ERROR: Failed to build gem native extension.
Gem files will remain installed in /var/lib/gems/1.8/gems/fastthread-1.0 for inspection.

then you need the ruby development libraries and the build-essential package. I’ve found that these commands will make sure mongrel (and rails) is installed correctly:

$ sudo apt-get install ruby1.8-dev rubygems build-essential rails
$ sudo gem install mongrel --include-dependencies

linux
rails
ruby

Comments (0)

Permalink

Using the Ubuntu Feisty Live CD as your primary OS

I had one of the most dreaded computer woes happen last week - my hard drive crashed.

Well, it didn’t crash completely. I have about a 70% boot rate. It makes a load noise like the bearings aren’t well lubricated when the drive won’t boot. I figured it was better (and less frustrating) to stop using the drive and just wait until the new hard drive arrived.

Luckily, I use unison over SSH to backup my files, so everything was already mirrored on my Debian file server at home, even though I was across the country when the failure occured. I’ve reminded myself that I needed to have my data backed up if something catastrophic ever happened to my laptop. In my mind the scenario was that my laptop was going to be run over by a truck (gotta think big), but I knew the hard drive crash was probably inevitable. Unison is a good tool to sync two file systems, however it does work best if you manually sync. If you want something to automatically back up files, set up rsync as a cron job on your system.

Coping with the problem

At first, I thought I should just go without my laptop for a few days (oh the horror!). Newegg ships fast, right? I should have known myself better than that. I immediately started thinking of how to run my system with a broken hard drive. Naturally, the idea of using a Linux Live CD came to mind.

To preserve the old hard drive, I physically removed it from the laptop. After that, I remembered I had ordered some Ubuntu 7.04 pressed CDs a while back so I popped one in.

To my surprise, Ubuntu 7.04 (Feisty) as a Live CD rivals my normal hard drive install in terms of performance! The bootup time is quite slow, but once it has started, it runs nicely. Since I spend a lot of time using Firefox, I don’t experience the spin-up-spin-down as often as I remember in previous Live CD usage. I think Feisty must also do a good job of caching programs in RAM when you first access them because I hardly ever hear the CD drive spin up unless I’m starting a new program.

The other nice thing is that the laptop fan almost never turns on. The old hard drive ran somewhat noisy and warm, causing the fan system to kick on about 75% of the time. I’ve really enjoyed the cool, quiet performance of running on a Live CD. The laptop is absolutely silent.

The only real obstacle I’ve faced is that I can’t shutdown my laptop unless I want to spend time re-setting up my Thunderbird profile and re-installing Firefox extensions. I guess you can’t have everything.

Why not switch completely?

If this setup works so well, I thought to myself, why not just switch to always using a Live CD and save $60 on a new hard drive? After thinking about it, I’ve decided that I would be willing to switch if a few conditions were met:

  1. A decent amount of persistent storage were possible
  2. I could install updates and new software without losing them each time I reboot
  3. Most or all of the OS could be loaded in RAM at boot

I could probably accomplish #1 with a USB drive and a persistent home directory. I think #2 would be harder with the USB drive - like what happens when you move between systems with the same flash drive - but still possible. In the end, I think I would be happiest by buying a Compact Flash to IDE adapter and a large (16GB or more) Compact Flash card. These are nice because you get the cool, quiet operation and persistent operation. I considered doing this instead of getting a new hard drive, but in the end I figured I would get much more storage but the price (80GB @ $60 or 16GB @ $150 or more). In the future I think many laptop hard drives will be completely flash based memory.

As for #3, I wish that Ubuntu Feisty had the ability to use the “toram” kernel boot option and load the OS into RAM during boot. I’ve done this with Knoppix CDs in the past. The speed increase is amazing. Granted, less RAM is available for program execution, but RAM is cheap enough these days that the productivity gain is worth the extra couple of dollars.

All in all, I give the Ubuntu Feisty Linux Live CD a surprisingly high thumbs up for everyday use.

linux

Comments (0)

Permalink

Silly Verizon and their creepy new router

I recently signed up for the Verizon FIOS service. Its nice, but probably not in the way you’d think. I only have 5 mbps down / 2 mbps up, which is the basic plan, and in my opinion, the only plan that makes financial sense. I just can’t see myself paying $180 per month for home internet service.

The nice part about the service is the latency. I have a 12ms ping to Google. I doubt most people get that at home on their cable or DSL lines. The best I’ve ever had at home was 40ms. At any rate, the low latency helps web pages load that extra bit faster (and it is noticeable).

The one thing about their service is their router. Apparently, I’m not the first one that thinks it is a little creepy. Verizon has customized it to provide their IPTV (cable) service as well as perform the normal router functions. I decided to use my Linksys WRT54G instead.

If you want to use their IPTV services, I’m willing to bet that you can chain their router behind yours. Just make sure to either change your router’s DHCP subnet to something other than 192.168.1.0/24, or change the Verizon router to use a different subnet (ie. 192.168.2.0/24). Most consumer routers will puke if you try to give them a non-routable IP address on their WAN interface in the same subnet as the one they are supposed to give to their own clients. Rightly so, I guess. Please comment about your experience with chaining the Verizon router below your own.

While setting up my own router, I noticed a few things:

  1. Verizon sets up your router with a random ESSID (network name) and WEP encryption. My tech wrote down the WEP key when she installed the service.
  2. Even if you do stick with the Verizon router, please switch to WPA. WEP is easy enough to crack and won’t keep your data secure from a moderately skilled attacker.
  3. The default login credentials for the router are admin/password. I’ve also heard some people having success with using “password1″ as the password. Please change this the first time you log in.
  4. Verizon uses MAC filtering, so you must spoof/clone the MAC on your router to match the Verizon router.
  5. There is a MAC address printed on the outside of the router. Unfortunately, this is NOT the MAC address used by the WAN interface on the router. Instead, you must log into the router and choose the “System Monitoring” section (upper-right corner). This will give you what they call a “Broadband MAC Address.”

Verizon Router Broadband MAC

Once I used the correct MAC address, I was able to get onto the FIOS network using my own router with no problems. They say your own router may not perform as well as theirs, but I didn’t notice any difference.

One last thing: You might notice that each time you type a character in the password field, several characters will appear. A brief look at the code suggests that they are hashing the data as you type. While it is incredibly annoying to have those characters randomly appear, at least they are using BSD-licensed code from a reputable source. Thank you Paul Johnston!

fios
network

Comments (1)

Permalink