CSV Parser in Ruby

So, I have written like a million versions of these in different languages, then I found out that Ruby has an in built CSV parser, so I said cool. I read through the Pickaxe, got the info, plugged it in and boom, no worky, I hate it when things no worky. So I spent the next hour on google looking for how to fix it, people seem to not be having the problems I am having, that is, it just doesn’t work…weird, but fuck it, I have written enough of them to do it my damn self, all I wanted was to parse a csv and maintain the column headers so that I could dynamically create activerecord objects in my rails app and not have to worryabout enforcing column order, or parsing it in the controller. It took me about 20 minutes to write, and yes it shows it, heres the kicker, it works.

That is, like out of the box, plug and play, whatever, it works for me, so maybe it will work for you, that is, unless you want to stick with the poorly documented and wholly mystical ruby version, you might be better off, but I like doing things myself, that way, I completely understand how it works, why, and if I want to make a change I can.

Anyway, so here it is:

Using a csv like this for the example:

"Song Name","Artist","Album","Duration"
"Girls And Boys","Good Charlotte","The Young and the Hopeless",03:05
"Meet Virginia","Train","Unknown",03:59
"Sacrifice","Elton John","Unknown",05:06
class Kcsv
	def initialize(file, options)
		@file = file
		@options = options
		options[:header] == true ? @headers = parse_header_line : @headers = false
		@children = []
		parse
	end
 
	def parse
		i = 0
		@file.rewind
 
		@file.each_line do |line|
			#looping through each line of the file
			if i > 0 then # we are past the first line
				x = 0 #we set the index for our position in the headers.
				row = {} #the row
				if not line.include? ',' then #Here we check to see if it is a single column
					row[@headers[x][0]] = clean(line) if not @headers[x].nil?
					row[x] = clean(line) if @headers[x].nil?
				else #we have multiple columns
					line.split(',').each do |column|
						#now we need to know if we have headers
						if !@headers.nil? then
								row[@headers[x][0]] = clean(column) if not @headers[x].nil?
						else
							row[x] = clean(column)
						end
						x = x + 1
					end
				end
				@children << row
			else
				i = i + 1 #just there to skipp the first line
			end
		end
		return true
	end
 
	def to_a
		@children
	end
 
	protected
	def clean(string)
		return string.gsub('"','').strip
	end
	def parse_header_line
		headers = []
		accepted_headers = ["ebay_id","image","price","quantity","title","desc","id","action","is_live","is_type"]
		@file.rewind
		@file.each_line do |line|
			x = 0
			if not line.include? ',' then
				headers << [clean(line),x] if accepted_headers.include? clean(line)
				headers << [x,x] if not accepted_headers.include? clean(line)
			else
				line.split(',').each do |col|
					headers << [clean(col).strip,x]
					x = x + 1
				end
			end
			break
		end
		return headers
	end
end
#this is just to test that it works as expected, returning and array of arrays with to_a
csv = Kcsv.new(File.open('songs.csv','r'), :header =&gt; true)
csv.to_a.each do |row|
	puts "#{row["Song Name"]} by #{row["Artist"]} from #{row["Album"]} -- #{row["Duration"]}"
end

Anyway, there it is, have some fun. There maybe a better, or faster way, however, this is working, so I am happy enough.

Comments

Ebay File Exchange .csv Gotcha Fixed with gsub

Hey All

Been awhile, I know. While working on one of my projects, it came up that we would need to import thousands of ebay items into the shop, and be able to export them using the ebay file exchange, which dumps out a .csv file. It seems .csv parsers are the most common task I perform, I have written like a million for rails and php over the years, so I slapped something together quickly to do it and oops, I didn’t notice that ebay lists the item’s price with a comma. Ack, Comma Separated File, and there is a comma in one of the values, it’s easy to forget, but can really mess you up. Well, since I was using rails which means ruby, this is not such a problem, I can fix that easy enough with some regex.

To make a long story short, and since the price field was and will only forever be the offending field, I got away clean with a simple quick fix:

fields = line.gsub((\d+),(\d)/,"\\1\\2").split(',')

Got rid of that offending , and the £ sign to boot since we save currency amounts as floats, the £ sign is independent of that, and we use number_to_currency for that. Simple I know, but man, what an effing pain sometimes.

Well, gotta get back to relearning Rails, with the release of 2.0, there’s so much new stuff that it’s driving me nuts. Personally, I don’t see 2.0 as an improvement, except for the caching stuff, and the sexy migrations, those are cool…but that’s it.

Comments

Scriptaculous Inline Edit in a php script to create mp3 playlists

Well,

I got tired of linux mp3 players and their inability to manage mp3s, so I broke down
and started doing what I have been planning for along time, creating a mp3 playlist
creator to run on my local server.

I may release it as a script, as so far, it’s pretty useful, but one thing
that came up was, some of the mp3 filenames were not exactly clear. Like Track 13.mp3.
I firmly believe in the mp3 naming standard of Artist ft. Others - Song Name - Alblum.mp3.

In order to do this with the script I got a fantabulous idea, let’s use scriptaculous to edit
them in place, making it feel a bit more like an application.

I load all the files into an array, choosing the chunk I am going to list. Note, if you
have thousands of mp3s, do not try to create thousands of inline edits, it will crash, seriously
I tried it.

Instead, I paginate my files so I can surf through them easily, checking off the ones I want
to add to my playlist, then the script moves them to the specified folder.

  <script src="javascripts/prototype.js"></script>
  <script src="javascripts/scriptaculous.js?load=effects,controls"></script>
  <script type="text/javascript" language="javascript">
    // <![CDATA[
      Event.observe(window, 'load' , function() {
        <?php 
        for ($i = $start; $i <= $limit;$i++) {
          ?>
            new Ajax.InPlaceEditor('song_<?php echo $i;?>', 'rename.php?action=rename&dir=<?php echo $_GET['dir'];?>');
          <?php
        }
        ?>
      });
 
    // ]]>
  </script>

There we go, now, I just loop through the array of songs, and place things in a paragraph
with the necessary id:

  <table>
    	<?php 
        //This is the equivalent of say for instance cycle. It can
        //be done a million different ways, this is one of them.
        //It allows for an alternating background color.
        $x = 0;
        for ($i = $start; $i <= $limit;$i++) {
          switch ($x) {
            case 0:
              $x = 1;
              $style = 'table-odd';
              break;
            case 1:
              $x = 0;
              $style = 'table-even';
              break;
          }
          ?>
            <tr>
              <td valign="top" colspan="3">
              <a name="<?php echo $i;?>" />
                <table class="<?php echo $style;?>" width="100%">
                  <tr>
                    <td valign="top" width="3%"><?php echo $i;?></td>
                    <td valign="top" width="2%"><input type="checkbox" name="song_nums[]" value="<?php echo $i;?>"> </td>
                    <td valign="top" width="45%">
                      <p id="song_<?php echo $i;?>"><?php echo $songs[$i];?></p></td>
                    <td valign="top"><a href="delete.php?dir=<?php echo $_GET['dir'];?>&file=<?php echo urlencode($songs[$i]);?>&anchor=<?php echo $i;?>">Delete</a></td>
                  </tr>
                </table>
              </td>
            </tr>
      <?php
        }
      ?>
    </table>

If someone wants me to post the whole script when it’s done, I will.

Comments

Rails: Caching and Autocomplete with Scriptaculous

I have seen some really great autocompleter tutorials around, but they all require constant database access, which is no good for me.

I have a table with customer’s info in it, there will never be more than a 100 or so, and I don’t add new ones that often.

I have a search method that lets me pull up any customers details simply by typing all or part of his/her name, I want to add an autocompleter to this field, here is what I did:

  #helpers/application_helper.rb
  def autocompleter(args)
    args[:div] ||= args[:field] + '_div'
    args[:class] ||= 'auto_complete'
    scriptaculous = "Event.observe(window, 'load', 
                    function() {new Autocompleter.Local('#{args[:field]}', 
                    '#{args[:div]}', #{args[:values]});});"
    return "<script>\n#{scriptaculous}\n</script>\n
            <div id=\"#{args[:div]}\" class=\"#{args[:class]}\"></div>"
  end
  #views/customers/list.rhtml
  <% cache "customer_autocomplete_list" do %>
  <script>
    var customer_names = [
    <% Customer.find(:all).each do |customer| %>
      '<%= customer.name %>',
    <% end %>
    ''];
  </script>
  <% end %>
  #or, alternatively more ruby-like, you could also turn this into a helper...:
  <% cache "customer_autocomplete_list" do 
    values = Customer.find(:all).map do |c|
     "'#{c.name}'"
    end
  %>
  <script>
    var customer_names = [
      <%= values.join(',') %>
    ];
  </script>
  <% end %> 
  #Then, I create the form
  <% form_tag :action => 'search' do %>
    <input type="text" name="search" value="" id="search_field"> 
    <%= autocompleter :field => 'search_field', 
                      :div => 'search_names', 
                      :values => 'customer_names', 
                      :class => 'auto_complete' %>
    <%= submit_tag 'Search' %>
  <% end %>

Explanation:

First I create a js helper method in the application_helper.rb file, it’s generic, so
I can resuse it for anything I like, then in the view, I create a cached js fragment called
customer_autocomplete_list, then I create the form and call the helper method.

In my controller, when I add a new customer, I can simply use:

  def create
    @customer = Customer.new(params[:customer])
    expire_fragment "customer_autocomplete_list"
  end

And it will be regenerated the next time the page is loaded.

Now even with 1000 names, this should still be a viable solution, hell,
for 10000 names, you are still better off loading a fragment than generating
that list from live queries, or on each load.

Anyway, for my small project it works, it’s great, and it makes the client happy
to have one of those gravy effects.

Just to be thorough, here is the auto_complete css:

  div.auto_complete {
    width: 350px;
    background: #fff;
  }
 
  div.auto_complete ul {
    border:1px solid #888;
    margin:0;
    padding:0;
    width:100%;
    list-style-type:none;
  }
 
  div.auto_complete ul li {
    margin:0;
    padding:3px;
  }
 
  div.auto_complete ul li.selected {
    background-color: #ffb;
  }
 
  div.auto_complete ul strong.highlight {
    color: #800;
    margin:0;
    padding:0;
  }

Earlier on, I said you could convert it into a helper, that is the mapping thing, well, I decided to come back and add that
in so you can see how it is done:

  #in application_helper.rb
  def single_field_map(args)
    args[:left] ||= "'"
    args[:right] ||= "'"
    values = args[:collection].map do |obj|
      "#{args[:left]}#{obj.send(args[:field])}#{args[:right]}"
    end
    return values
  end
  #views/customers/list.rhtml
  <% cache "customer_autocomplete_list" do %>
  <script>
    var customer_names = [
      <%= single_field_map(:field => 'name',:collection => Customer.find(:all)).join(',') %>
    ];
  </script>
  <% end %>

Now, that’s more concise, of course, that is in addition to the first helper and view code…just an addition
and a replacement, more ruby like.

It’s hard for me to not think like a PHP programmer, I am getting there, but remember,
working code is better than working theory. In Arnis we have a saying, “If it works, it’s Arnis.”
In a fight, if you win, you’re right, in coding, if your software works like you planned it, then
it’s right.

I have tested all of this code to make sure it works, however, I don’t guarantee anything.

The golden rule of code copying is, if you don’t understand it, don’t copy it. Figure it out first.

Comments

Current Project 29/09/2007 Rails and eBay Trading API

Show Stamp

A nice screenshot from my current project, as always, I am cursed with creating intranet like apps, which aren’t publicly accessible. This one is from a project integrating a Rails database management system with eBay using their trading API. The whole thing is built from scratch in Rails.

So far it features automatic thumbnail generation and image sorting into directories, c39 and EAN barcode generation, and a connection to eBay that allows stock to be automatically listed.

When it’s finished, I might just create a publicly accessible version just for future client to peruse.

Comments

Rails: Helper Tutorial Now with 20% more Scriptaculous.

I am no rails expert, in fact, I had a bit of a problem with helpers, had to look into it and eventually, ask some questions and get answer on the Rails group before I could fix the issue, I thought I would give back to the community on what I learned.

Helpers Rule.

That’s right, helpers are you friend, there isn’t much to say else, except they are dead useful ideas. Helpers save you loads of time and keep your code DRY, as opposed to wet :) As you may not know, DRY stands for Don’t Repeat Yourself, and along with Convention over Configuration, it’s the core mantra of every rails programmer. How do they work? Simple enough, they are basically functions held in a centralized location, either in a controllers helper, or in the main Application helper, based on where you will need them.

If you only need them to be accessible from on controller and it’s helpers, then you put them in the ControllerName_helper.rb file that you either create, or was created for you with scaffold.

If you need something that is accessible from any view/controller, then you will be putting it in helpers/application_helper.rb

Let’s take an example. Let’s say you have a central image location for thumbnails and one for large images on another server, instead of hard coding that path into every view, you can create a helper that does it for you.

def my_image_path(args)  
  args[:version] ||= ''
  return_value = ''
  if args[:version] == 'thumb' then
    return_value = 'http://images.myhost.com/thumbnails/'
  elsif args[:version] == 'large' then
    return_value = 'http://images.myhost.com/large_images/'
  end
  return return_value
end

Then you just pass it in a view like this:

<%= my_image_path :version => ‘thumb’ %>

Pretty cool huh?

You’ll notice something, in this example, I could have just done it this way:

def my_image_path(args)  
  args[:version] ||= ''
  if args[:version] == 'thumb' then
    'http://images.myhost.com/thumbnails/'
  elsif args[:version] == 'large' then
    'http://images.myhost.com/large_images/'
  end
end

You can do that, however, as I learned the hard way, it can have unexpected results. I will tell you what wasn’t told to me,
just because you can do it, doesn’t mean you should, you should always explicitly return something!

Of course the above example isn’t so powerful, but, with this one, you can see the greatness of the helper.

I have a website that has products separated into categories of an infinite depth, that means, it can have as many subcategories
as it needs or wants. You can add products to multiple categories, so I need to generate a list box. Because of the
various different extra goodies, I need to be able to display this list box in several different views, for instance
the new and edit pages, but also a move, and list page so that multiple items can be moved into a selected category at once.

Now, I could write that code in every single view, but when I need to make a change, I have to edit like 5 different files, that’s
no good, and all of a sudden, my code is no longer DRY, enter Helpers to the rescue.

  def category_list_options(args)
    args[:category_id]  ||= 0
    args[:spacer] ||= '&nbsp;'
    @categories = Category.find(:all,:conditions => ['category_id = ?',args[:category_id]])
    ret = ''
    for category in @categories do
     ret = ret + '<option value="' + category.id.to_s + '">' + args[:spacer] + ' ' + category.title + '</option>'
      if category.categories.length > 0 then
       ret = ret + category_list_options(:category_id => category.id,:spacer => args[:spacer] + args[:spacer])
      end
    end
    return ret
   end

Then I just call it in a view with:

  <%= category_list_options :spacer => '&nbsp;' %>

Now of course, I can expand that, for instance, I can rewrite it to show them as table rows, like this:

  def category_list_table(args)
    args[:category_id]  ||= 0
    args[:spacer] ||= '&nbsp;'
    @categories = Category.find(:all,:conditions => ['category_id = ?',args[:category_id]])
    ret = ''
    for category in @categories do
     ret = ret + "<tr><td>#{args[:spacer]} #{category.title}</td></tr>\n"
      if category.categories.length > 0 then
       ret = ret + category_list_table(:category_id => category.id,:spacer => args[:spacer] + args[:spacer])
      end
    end
    return ret
   end

I can call the whole tree like so:

  <%= category_list_table :spacer => ‘ ’ %>

Or, when customers are browsing a certain category, I can grab all the ones down that chain like so:

  <%= category_list_table :category_id => current_category.id,:spacer => ‘ ’ %>

This all assumes that you have a model that belongs to itself, with a foreign key in the table.

  class Category < ActiveRecord::Base
    has_many :categories
    belongs_to :category
  end

From then on, whenever we create a category, we set main categories as having a :category_id of 0, that way
we know this category belongs to no category, everything else is a subcategory :)

So you see, helpers really really rule. And this isn’t the half of it, I am sure I will be back to add more
code samples and ideas when I find new uses for helpers…

Ohh, I forgot…

One other cool factor, is javascript helpers, writing js is a total bitch. On a Pizza Shop’s website, which is basicallly
an online menu, I needed to have a js popup window to show the product and ingredients. I just added a little helper in
application_helper.rb like this:

  def javascript_popup_window(args)
    args[:height] ||= '600'
    args[:width] ||= '300'
    args[:name] ||= 'javascript_window'
    args[:toolbar] ||= '0'
    args[:statusbar] ||= '0'
    args[:location] ||= ''
    return "window.open('#{args[:location]}','#{args[:name]}','status=#{args[:statusbar]},toolbar=#{args[:toolbar]},height=#{args[:height]},width=#{args[:width]}')"
  end

I know, the world’s longest line, but it’s straight forward. All I need to do is call it with:

  <a onclick="<%= javascript_popup_window :location => '/controller/action/id' %>">Open</a>

Baddaboom, works like a charm.

You can also do something like this for a scriptaculous autocompleter:

  def scriptaculous_autocomplete(args)
    scriptaculous = "Event.observe(window, 'load' , function() {new Autocompleter.Local('#{args[:field]}' , '#{args[:div]}' , #{args[:name]});});"
    array = ''
    args[:values].each do |v|
      array = array + "'#{v}',"
    end
    return "<script>#{scriptaculous}\n var #{args[:name]} = [#{array}''];</script>\n<div id=\"#{args[:div]}\"></div>"
  end

Then you call it like so:

  <%= scriptaculous_autocomplete (:field => 'item_title',
                                 :div => 'item_title_choices', 
                                 :name => 'item_titles', 
                                 :values => ['and','one','two','three','four']) %>

Of course, that’s just off the top of my head. To learn more about scriptaculous, and Prototype, why no head over
to pragmatic programmer and pick up their book on the subject.
As for Helpers, check out prag’s agile webdevelopment book, it’s a scorcher.

Anyway, that’s all for now, let me know if there are any errors, most of this code is used by me in actual projects.
Also, if you have a better, cleaner, more robust way, send it to me and I’ll plop it up here.

Comments