Category Archives: Web Development

Migrating a PHP database to a new Rails application

After rewriting most of a PHP application in Ruby on Rails, I realized I had a problem with the database.  How to I get all the data from the database and put it into my rails database?  The easy answer would probably be to copy it all into the /db/seeds.rb file and just migrate.  But that sounds like a lot of typing!  And typing means more mistakes.  Also, how would I do this in the future?  The app wasn’t that big, but it was big enough that a little strategy was called for.

I started following this post.  This told me that I really needed to create a plugin to do it.  Looking a bit further, I found Tim Riley’s acts-as-importable. That looked very promising and was what I eventually used.  This app is on Rails 2.5.  Here’s what I did:

Step 1 – Get the code!

First up, I got the data.

After that I didn’t know where to put it.  Turns out, you just expand it so that you have a directory called /vendor/plugins/acts-as-importable.

From there, you should have a directory structure that looks like this:

While we’re here, I don’t remember if I modified the init.rb, but mine looks like this:

Step 2 add database config and back it up!

Then I figured I’d add the database of my legacy system to the config/database.yml file:

Since this database was active and on the other server, I backed it up just in case!

Then I took some time to make sure I could indeed access it with that username and password remotely, since I develop remotely.  This took some fun mysql-foo.  It turned out that my Mac didn’t have any mysql client on there.  Not a problem:

Next up, we had to create some classes.

step 3: Create classes

This was well documented on Tim’s blog.  What wasn’t obvious to me was where to put the classes.  Luckily, in the comments section there was somebody who asked the question.  Thanks for asking  questions!  They really help!

So I created app/models/legacy.rb Here it is in all its glory.  I broke up the fille into 3 sections, but its all just one file.

Now for some explanations.  This is a really simple app.  Its just a cooking web site with users, recipes, and categories.  Nothing more fancy than that.   Starting with the Category class, we imported all the old ‘cat’ columns into the new Category.title attribute.  In addition, we did a find_by_title to make sure it wasn’t already in the application.  This made it so we didn’t have duplicate entries and I could run this as much as I want while letting the legacy application stay in place until I was finished developing.

Next up the user stuff.  This was pretty much the same as the Category, but added a few more things.  I lost the users passwords this way because I set up a new password system with bcrypt that was more secure.  It was ok, I notified all the users later by generating default passwords.

The last model class is the recipe.  This was a bit more tricky because of errors in the way the first database was created.  Notice we had to use the belongs_to in this class and the has_many in the previous classes.  One of the issues I had was that in the legacy database, there was no foreign key from the Recipes to the Users.  Who designed this thing anyway?  So I couldn’t use the lookup class that acts_as_importable uses.  Bummer!  That’s ok, I just used the Rails stuff to see if it was defined to get the new users ID.

The last thing there is the Legacy::Importer.  That was where we do all the importing.  Tim’s blog does a good job explaining that.

step 4: add some columns to the tables.

This filled in the entries so that the foreign key look up stuff would work.

step 5: Set up rake task

This was tricky for me too.  I ended up creating vendor/plugins/acts-as-importable/tasks/acts_as_importable_tasks.rake.  The contents are as follows:

Step 6: Ok, now do it!… oh no…

To import the data, we run:

But this kept giving me som AMC errors…

step7: Fix/modify the plugin

After reading up more on plugins and all that jazz I dared change the code.  I changed the file /vendor/plugin/acts-as-importable/lib/acts_as_importable.rb. There was a reference to the AMC class (which was the origninal author’s project.).  So I removed that so that from the acts_as_importable method.  It now looks like this:

(Notice I simply copied the 2 lines from above and removed the AMC:: portion from them.  That seemed to get me further.

Step 8: Run it again!

After all that, it finally happened.  It was incredible!  Thanks so much Tim!  All the tables were pulled and the new site took on all the attributes of the old site.   But now I wonder what I did wrong or if there were easier steps I could have taken.  Probably, but it worked, and that’s the important part.  I launched the new site, patted myself on the back and went to bed.  Hope this helps you migrate data!

HAML and jEditable

One of the problems with jEditable and HAML is that HAML puts whitespace in the form automatically.  So my HAML script looks like this:

The problem with this in jEditable is that now once the user clicks on it, there is gobs of white space around the word.  To get rid of this you use HAML’s fancy no whitespace flag ‘<‘.  This looks like:

This is an example of how one simple character can really help improve the user experience :-)

jquery ui tabs and the back button

For the site I help out with, xcat.sf.net, I used jquery-ui tabs.  The only issue I had was that I couldn’t do back button, and I started getting a few complaints from it…

So, after about 20 minutes of investigation, here is the code I used to make it work.  It works pretty well for cases when you navigate externally, but doesn’t work when you press the back button while on the page, expecting to go to the previous tab.  The jQuery team says that they’re working on this but its not in there today. Also, my approach refreshes the entire page with each click, that’s how I get the right page.  So it may not be optimal for many places.  Anyway, here is what I do:

<script type=”text/javascript”>
$(document).ready(function(){
var start = window.location.href.lastIndexOf(“#”);
var tabIndex = 0;
if (start !== -1){
tabIndex = window.location.href.substring(start+1, start+2);
}
$(‘#tabs’).tabs({
selected: tabIndex,
spinner: ‘Retrieving data…’,
select: function(event,ui){
window.location = “#” +  ui.index;
window.location.reload();
},
});
});
</script>

Then, down below in the HTML, I have:

<div id=’tabs’>
<ul>
<li><a href=”about.html”>About</a></li>
<li><a href=”download.html”>Download</a></li>
<li><a href=”start.html”>Getting Started</a></li>
<li><a href=”docs.html”>Documentation</a></li>
<li><a href=”support.html”>Support</a></li>
<li><a href=”test.html”>Testimonials</a></li>
</ul>
</div>

That makes it so that the back button is supported.  It changes the URL a bit, by giving it a nasty #<tabindex> but it does the trick for me.

CSS container tricks

In CSS I always forget that when creating a box to put things in you want the box to expand with the contents that are inside of it.  The secret to this I found out last year was to put overflow: auto in.

Example:

The other trick was to put margin: 0px auto.  That makes the element float in the middle of a page.  This is the standard container CSS that I use.

php system log in

I’m trying to make a php program that will authenticate users based on if they have a userid on the system.  In my environment my system has a number of users who can just ssh into the machine if they want.  But I am trying to make some applications available via a web interface.

I first saw this link and tried to explore the posix_getpwnam function.  This looked promising, but unfortunately Linux puts the password in /etc/shadow so you can’t parse the hash that’s returned to do pattern matching.

I also didn’t want to change any file permissions on the system.  I saw a number of posts that suggested that approach.

So I just did a simple expect script:

lib/passwdV.expect

Now I just take my calling script and pass the parameters in:

You can then take the user and set the session logged in portion if you want.  This is how I did it.  This way I don’t need to store user information in a database since its already part of the system.

There is still the security concern that when this exec happens the ps -ef output will actually print the password and userid.  A better way would be to encode this. I hope to get back to this soon and fix it.