Monday, September 1, 2008

Contributing to Open Source (Substruct)

At Coco, we are starting to build out some e-commerce sites. In order to expedite the process, we are using Substruct as a solution. While working with it, I have noticed a few things that needed to be fixed.

With engines setup properly, you are capable of extending routes beyond what's provided with the substruct engine. This was not the case beforehand. I was able to track down the bug and submit some patches to mend the problem.

If you are using substruct and want to customize it (adding additional controllers either to the admin or outside the admin) you'll need to get the latest substruct. Check out the issue here.

You can find download links there.

Working on open source projects is a lot of fun. It's interesting to see how other people do things. Contributing gives developers like me a boost of confidence. I plan on finding and fixing more bugs in the future for this project and others.

Labels: , , ,


Wednesday, August 20, 2008

Last.fm Cocoa Tagger v1.2

I added some new features including:



Most of these features allow you to keep the app open (as long as it doesn't crash). Check it out!



Download now

Labels: , , , , , , , , ,


Sunday, July 6, 2008

Last Update for a While... Promise!

That is, until I get column sorting going. That's my next task. In this release, I have integrated sparkle updates! I'm so happy about that. Thanks to Fool’s Ruby and Cocoa Workshop, adding sparkle updates was a breeze. Thank you K.M. Lawson!

As always, I will provide a screenshot and updated download link. Get it now!

Labels: , , , , , , ,


Please Update

Please download the latest version. There was a problem with using smart playlists. I had to remove them from the playlists dropdown temporarily (or maybe even permanently).

Download now

Labels: , , , , , , ,


Quick Update

Due to problems with the Universal build, I had to build strictly for 10.5. So, here's a new version with some cool new features. Let me know if you have any problems with it.

Download now (10.5.x build)

Labels: , , , , , , ,


Saturday, July 5, 2008

Icon Goodness!

Thanks to Matt, I now have an icon for the Last.fm Tagger.



Love it!

Also, I setup a github account for this project.

Labels: , , , , , , ,


Friday, July 4, 2008

An Unexpected Rewrite...

After asking a few friends to beta test the app, I found out that it wouldn't run on any machine but mine. Luckily, I have another mac around and was able to test. I ended up having to rewrite the whole thing! My problem was when I was trying to match the sender of the 'tableView_objectValueForTableColumn_row' delegate method to an NSTableView object. This wasn't working on any other machine for some reason. So, I ended up rewriting it all and refactoring things into separate controllers which is really nice.

The app will close out properly if no playlist with the name of 'lastfmtagger' exists. I am working on integrating a way to create the playlist on demand so that the app will continue running.



I would also like to be able to refresh the list of artists on demand, or on a timed observe of some sort so that when you add artists to the playlist, the app is updated accordingly. Tons of ideas, tons of time!

Here's an updated screenshot:



And the download link of course.

Labels: , , , , ,


New Last.fm Tagger Written in Cocoa

So, I'm trying my hand at Cocoa, namely RubyCocoa. Objective-C is a bit out of my grasp right now. If I start needing big performance boosts, I'll go that route. In the meantime, writing RubyCocoa apps is insanely fun. I wrote this basic tagger in 2 days (roughly 6 hours) and it works pretty well!

If you plan on trying out this app, create a small playlist with the name 'lastfmtagger' and put a few albums in there.

Problems I am aware of:


  1. Can't load huge iTunes library/playlist yet without taking forever to load

  2. App craps out if the file is missing in iTunes when trying to write

  3. Some more I can't think of right now...



Give it a shot, and let me know what you think. Here's a screenshot.

Labels: , , , , ,


Tuesday, March 11, 2008

We Made it to 1.0!

I made a nice improvement that I feel warrants a 1.0 release. That improvement is simply a hash that stores genres for the life of the process so that an additional query to last.fm is not needed. I fixed a bug where "q" wasn't cleanly aborting the process. Also, I did a little spring cleaning with the code... moved everything out of a main file and into separate files for better organization.

I'm happy to say that build.last.fm accepted my application! So you can now find the tagger on build.last.fm.

If anyone out there would like a feature added, just let me know and I'll see about implementing it.

Get 1.0 now!

Labels: , , ,


Thursday, February 28, 2008

Last.fm Tagger 0.9

I've made a few improvements to the Last.fm Tagger. Namely, the ability to cleanly abort the tagging process. Other features include displaying a count of tracks selected in iTunes, as well as the number of unique artists in that selection.

As always, you'll need the wonderfully awesome RubyOSA gem installed.

Download it now.

Labels: , , , ,


Tuesday, October 9, 2007

Last.fm Tagger for RubyOSA Update 3

I've once again improved my little Last.fm script for use with RubyOSA. It needed updating badly. There were quite a few things bugging me.

What's new:

• Nice console messages telling you what's happening.
• Support for skipping over identical genre's.
• Ability to continue or abort tagging process. So if you don't like that tag, type 'n'.
• Use -q to subdue tagging confirmations.
• Full UTF-8 support, as well as proper URL escaping.



Download it now! And have somewhat useful genre's to choose from.

I will tackle cleaning it up later. Add me to your RSS reader if you want script updates.

Remember, you can get RubyOSA with the Last.fm Tagger here.

Labels: , , , , , , , ,


Thursday, August 9, 2007

eMusic.fm Greasemonkey Script


I finished a v0.1 of my eMusic.fm Greasemonkey script. What it does is pretty simple. On an album page, it will pull the 10 most popular tags from last.fm and inject them into the page below the description.

I found myself looking at last.fm for a particular artist to see what the community has labeled it as. This provides a more insightful look into artists you may not be aware of.

Download my eMusic.fm greasemonkey script

Labels: , , , , , ,


Wednesday, June 27, 2007

Simple Rails Refactoring

I needed to attach articles to our default template when running migrations. This was currently executed like so:


type = Type.find(1)
Template.find(:all).each { |t| t.types << type }


Pretty simple. Find the first type, which is supposed to be Pages. Iterate through all templates and assign Pages to them.

I needed to do the same for Articles too, so to get it done quickly, I just repeated what I did previously. So the code looks like this now:


type = Type.find(1)
Template.find(:all).each { |t| t.types << type }

type = Type.find(3)
Template.find(:all).each { |t| t.types << type }


Now I'm finding Pages + Articles, then iterating twice over the Templates and inserting on two separate occasions. To foreign users, I thought it may be a little vague, so I decided to add comments:


# Set default template for pages
type = Type.find(1)
Template.find(:all).each { |t| t.types << type }

# Set default template for articles
type = Type.find(3)
Template.find(:all).each { |t| t.types << type }


Now! Whomever reads my code will know what I was thinking and could probably help clean it up a bit. But wait a sec, Rails adds some pretty nice syntactic sugar to make comments completely unnecessary. Take a look at the same code without comments:


type = Type.find_by_name('Page')
Template.find(:all).each { |t| t.types << type }

type = Type.find_by_name('Article')
Template.find(:all).each { |t| t.types << type }


I think the code is self-explanatory without comments now. But is there a way that I can reduce that still to one line? Sure. The `type` variables are not needed anyways. So let's get closer to one line:


Template.find(:all).each { |t| t.types << Type.find_by_name('Page') }
Template.find(:all).each { |t| t.types << Type.find_by_name('Article') }


This looks swell. I removed the variables and just put the finder methods add what's being inserted into the Template association.

This can be refactored one more time. I can append multiple calls to an array insertion, therefore reducing my code to one line.


Template.find(:all).each { |t| t.types << Type.find_by_name('Page') << Type.find_by_name('Article') }


Now it's lean.

I make it a point to do things like this as many times as possible in one day. Something that Dave Thomas at RailsConf stuck with me. It's not an accurate quote, but it goes something like t his:

Always check in code that is better than before. Even if it's a small improvement.

Labels: ,


Monday, March 19, 2007

New Last.fm Tagger for RubyOSA

Laurent Sansonetti was generous enough to ask me if I wanted to contribute my little Last.fm script to the RubyOSA samples directory. And of course, I was more than willing to do that. He actually updated the script to make it even better! Now, you simply select which track in iTunes you want to tag, and run the RubyOSA script. The script will then query Last.fm for the most popular tag, and set the genre to whatever the tag on Last.fm is.

Here's the updated code:

begin require 'rubygems'; rescue LoadError; end
require 'rbosa'
require 'net/http'
require 'cgi'
require 'rexml/document'
include REXML

itunes = OSA.app('iTunes')
selection = itunes.selection.get
if selection.empty?
$stderr.puts "Please select some tracks."
exit 1
end
selection.each do |track|
feed = "http://ws.audioscrobbler.com/1.0/album/#{CGI::escape(track.album)}/toptags.xml"
doc = Document.new(Net::HTTP.get(URI(feed)))
track.genre = doc.root[1][1].text
end


Remember, you can get RubyOSA with the Last.fm Tagger here.

Thanks to Laurent for making an awesome library. It makes playing with Ruby even more fun!

Labels: , , , , ,


Wednesday, November 15, 2006

Get Genre's from Last.fm and Tag with Last.fm Using RubyOSA

I’ve always hated genre’s. They always place a constriction on the acceptance of an artist. However, they do come in handy in certain situations. For instance, I like to tag all my ambient stuff so that I may quickly choose an artist to fall asleep to. Now, I’m on a tagging rampage, however there are some artists where I have no idea how to “genrealize” it. Ah, but the ever-growing community on Last.fm tags artists! Most of the time, the genre is agreeable upon a majority and therefore acceptable to me. I was growing sick of searching Last.fm for each artist, then choosing the most popular tag and tagging it in iTunes. If it weren’t for RubyOSA, I could not have accomplished what I wanted to do.

This is a perfect project for me that I hope to evolve into a more interactive process. Currently, the script is very basic in that you run it, it grabs the tag feed from Last.fm and sets the genre of the artist that you’re listening to. Check it out:




require 'rubygems'
require 'rbosa'
require 'net/http'
require 'cgi'
require 'rexml/document'

include REXML

itunes = OSA.app('iTunes')
track = itunes.current_track
feed = "http://ws.audioscrobbler.com/1.0/artist/#{CGI::escape(track.artist)}/toptags.xml"
doc = Document.new(Net::HTTP.get(URI(feed)))
track.genre = doc.root[1][1].text


In the future, I would like to provide a list of the top 10 tags in which you choose which to tag this song with and ultimately, I’d like to tag the whole artist with the genre. Maybe I could also provide album tagging, since some artists deviate in style across multiple albums.

Labels: , , ,


Monday, October 23, 2006

Ruby on Rails: My ACL Needs Testing! (RUM ACL)

Hello. I have built an ACL (user-management) system with Rails and I need you to test it. Please download the zip, extract it, create your database and set it up, then run rake db:migrate.

Some pluses of this system

Some minuses

Could/should it be developed as a plugin? Engine?

I’d really appreciate some feedback! Send me an email: email feedback

Download the RUM ACL: rum-1.0.zip

Labels: ,


Friday, October 20, 2006

Rails, meet the Yahoo! Geocoding API

One thing’s for sure: consuming web services in Rails is not very well documented. There are a few ways of going about it, but the easiest way uses REST.

Problem: provide a way to look up a zip code to find a city. With the returned city, search the local database for a match. Then output accordingly.

Solution: Ruby on Rails with the REXML library.

Forget SOAP and XML-RPC, get REST’ed! I will show you how I went about pulling a city by zip code, then using the returned city in a query on my local database.




# welcome_controller.rb
def city
appid = "myYahooDeveloperAppID"
url = "http://api.local.yahoo.com/MapsService/V1/geocode?appid=#{appid}&location=90210"
@results = REXML::Document.new(Net::HTTP.get(URI(url)))
end

# views/welcome/city.rhtml
@results.root.each_element { |city| "#{city[3].text}" }


There you go. Can you believe it’s that easy?

Labels: , ,


Tuesday, October 10, 2006

Code Highlighter Plugin!

Finally! Oh, how I’ve longed for your sweet caress…

Thanks to this how-to, I was able to get syntax highlighting working. It’s super-easy to setup, however I had to do some light digging to get the proper CSS. The one I found was similar to the RubyBlue TextMate theme that I adore.

Here’s a small excerpt (for testing) of the lib/codehighlighter plugin.rb file:

require 'behavior'
require 'syntax/convertors/html'

Behavior::Base.define_tags do
tag 'code' do |tag|
lang = tag.attr['lang'] || "ruby"
convertor = Syntax::Convertors::HTML.for_syntax(lang)
code = convertor.convert(tag.expand.to_s.strip, false)
%{
#{lang}-code">#{code}
}
end
end

And of course, the CSS:

pre.ruby-code {
background-color: #0D151E;
color: #fff;
padding: 10px 10px 10px 10px;
margin: 4px 0px;
font-size: 1.1em;
overflow: auto;
}

/* Syntax highlighting */
pre.ruby-code .normal {}
pre.ruby-code .comment { color: #428BDD; font-style: italic; }
pre.ruby-code .keyword { color: #F8BB00; }
pre.ruby-code .method { color: #077; }
pre.ruby-code .class { color: #fff; }
pre.ruby-code .module { color: #050; }
pre.ruby-code .punct { color: #FFF; }
pre.ruby-code .symbol { color: #B53B3C; }
pre.ruby-code .string { color: #1DC116; }
pre.ruby-code .char { color: #F07; }
pre.ruby-code .ident { color: #fff; }
pre.ruby-code .constant { color: #8AA6C1; }
pre.ruby-code .regex { color: #CA4344; }
pre.ruby-code .number { color: #EDDD3D; }
pre.ruby-code .attribute { color: #5bb; }
pre.ruby-code .global { color: #7FB; }
pre.ruby-code .expr { color: #227; }
pre.ruby-code .escape { color: #1C6A21; }


Labels:


Monday, September 4, 2006

Using Rails and a stand-alone script

A challenge was presented to me with my most recent project. Setup a script to run nightly (by a cronjob) to retrieve records for a past days orders. With these orders, generate an Excel spreadsheet followed by an email with this spreadsheet attached. I accomplished this with a lot of trial and error and I’m going to present the code for which you can do the same when/if you need to. Info was scarce on the web on how to do exactly what I nedeed to do, so hopefully this will enlighten some lucky folks out there. Ha! As always, this probably can be improved quite a bit, I’m just glad to have it done and working! (Client happy..)

Keep in mind that you need the spreadsheet-excel gem. Install it via:

gem install spreadsheet-excel

Here goes:




#!/usr/bin/env ruby

# ABSTRACT
# This file is to be executed by a cron script that is setup on a local server

# PROCESS
# 1. Load Rails environment and make connection to database
# 2. Get list of operators, get orders which have an agent that belongs to that partner to operator
# 3. With that list of orders, create the Excel spreadsheet
# 4. Mail spreadsheet to operator (CC to mycablehome)
# 5. Create record in Leads table with a reference to the spreadsheet

RAILS_ENV = 'production'

require File.dirname(__FILE__) + '/../config/environment'
require "spreadsheet/excel"

@operators = Operator.find(:all, :select => 'id, site, name, system_city, report_email_address_to, report_email_address_cc')
@operators.each do |operator|
@orders = Order.find(:all,
:conditions => [ "operator_id = ? AND created_at #{(1.day.ago.to_date..Date.today).to_s(:db)}", operator.id ])

file = "#{1.day.ago.to_date}_#{operator.site}_#{operator.system_city.underscore.downcase}_sales_leads.xls"
workbook = Spreadsheet::Excel.new("#{RAILS_ROOT}/public/reports/#{file}")

worksheet = workbook.add_worksheet("#{operator.site} Sales Leads for #{1.day.ago.to_date}")
worksheet.write(0, 0, "Customer name")
worksheet.write(0, 1, "Email")
worksheet.write(0, 2, "Service street address")
worksheet.write(0, 3, "City")
worksheet.write(0, 4, "State")
worksheet.write(0, 5, "Zip")
worksheet.write(0, 6, "Home phone")
worksheet.write(0, 7, "Daytime phone")
worksheet.write(0, 8, "Cable Package")
worksheet.write(0, 9, "Services")
worksheet.write(0, 10, "Num. of TV's")
worksheet.write(0, 11, "Num. dig A/O")
worksheet.write(0, 12, "Num. HD/DVR")
worksheet.write(0, 13, "HD only")
worksheet.write(0, 14, "Installation date")
worksheet.write(0, 15, "Installation time")
worksheet.write(0, 16, "Alternate installation date")
worksheet.write(0, 17, "Alternate installation time")
worksheet.write(0, 18, "Order num")
worksheet.write(0, 19, "Comments")

row = 1
@orders.each do |order|
worksheet.write(row, 0, "#{order.customer.first_name} #{order.customer.last_name}")
worksheet.write(row, 1, "#{order.customer.email}")
worksheet.write(row, 2, "#{order.customer.addresses.first.address}")
worksheet.write(row, 3, "#{order.customer.addresses.first.city}")
worksheet.write(row, 4, "#{order.customer.addresses.first.state}")
worksheet.write(row, 5, "#{order.customer.addresses.first.zip}")
worksheet.write(row, 6, "#{order.customer.home_phone}")
worksheet.write(row, 7, "#{order.customer.daytime_phone}")
unless order.package.nil?
worksheet.write(row, 8, "#{order.package.name}")
worksheet.write(row, 9, "#{order.package.services.collect { |s| "#{s.name} " } }")
worksheet.write(row, 10, "#{order.tv_sets}")
worksheet.write(row, 11, "#{order.digital_boxes}")
worksheet.write(row, 12, "#{order.dvr_boxes}")
worksheet.write(row, 13, "#{order.own_hdtv_set}")
worksheet.write(row, 14, "#{order.operator_order.installation_date}")
worksheet.write(row, 15, "#{order.operator_order.installation_time}")
worksheet.write(row, 16, "#{order.operator_order.alternate_installation_date}")
worksheet.write(row, 17, "#{order.operator_order.alternate_installation_time}")
end
worksheet.write(row, 18, "#{order.id}")
worksheet.write(row, 19, "#{order.operator_order.special_instructions}") unless order.package.nil?
row += 1
end

workbook.close

OrderMailer.deliver_sales_leads(operator.report_email_address_to, operator.report_email_address_cc, operator.site, operator.system_city)
end


Hopefully this will get you started.

Labels: ,


Thursday, June 29, 2006

Adventures with Ruby (part 1)

These are notes I’m taking while reading the Programming Ruby book. It’s excellent.

General Ruby

Classes

Cool and unforgettable features

I AM WHAT I DO NOT UNDERSTAND. I AM ARBITARY!

Labels: ,


Friday, March 3, 2006

About the Blog and its Guts

Wow.

What can I say? Other than, “I adore Ruby on Rails”. If you are a struggling Rails would-be developer, have no fear! I was in your shoes at one time, in fact, I’ve only taken off one shoe.

You may ask, what it is that got me so thrilled about Rails? This pretty much sums it up.

Here’s a break-down of my process over the past few days:

  1. Stumbled upon this wonderful, amazing, enlightening article published by Apple.
  2. Work was slow, so I decided to buckle down and get a development process in order.
  3. Eventually got a process in order using SVN for keeping up-to-date on progress at work and at home. This was a huge step.
  4. Progressed my blog into what it is today. I’m very happy with it.

I’m very obsessive about order. So much that for the longest time, I could never accomplish anything because I didn’t have a process. Rails provides this process almost transparently. It’s an amazing framework, and I applaud David for frontiering the future of web development.

Bless the Sterkens Dubbel Ale. Mmmf…

Labels: ,


Monday, November 14, 2005

A Reanimated CollectorNerd.com

I’ve always wanted to setup my own CVS repository. Sure, I could set one up at home, but it kind of defeats the purpose of it. I want a remote location for storing my files that is accessibly (safely and securely) from anywheres. Our previous host LiquidWeb did not support hosting CVS for individual accounts. Dreamhost does, though. My main reason to switch to Dreamhost was that they had Rails installed. They sealed the deal with their awesome prices. CVS is so nice, especially when coupled with RadRails a well-made IDE for developing with Ruby on Rails.

Now, onto the topic of this article. A Reanimated Collector Nerd. Some of you may be familiar with a project that I’ve been working on (on and off for a few years now). I haven’t worked on it in about a year until yesterday, where I quickly implemented item comments. So that when a user leaves a comment on someone’s collected item, they receive an email letting them know.

Thanks to Basecamp we now have the most resourceful collaboration software ever. This is going to help everyone compile their ideas into one big “basecamp” Too bad the free version allows on two writeboards (sigh). I’m going to try my hardest on making Collector Nerd (on Rails) a reality! I want this to happen. Bad.

Labels: ,


This page is powered by Blogger. Isn't yours?

Subscribe to Posts [Atom]