Aug 18

My day job has been keeping me busy-busy-busy, but I had some time this weekend to add some more to MyQuotable.com. Here is what is new:

  • You can now edit your user profile. (Name, etc)
  • The color scheme is slightly different.
  • You now collect positive karma every time someone votes for one of your quotes.
  • Pagination!
  • I refactored the statusbar. Breadcrumbs and navigation work better and more consistently.

Next up: Search and Categorization

Aug 01

MyQuotable is a fun project for me. Here is what is new:

  1. Tags:  Tag your quotes and filter by those tags.
  2. /popular: See which quotes have the most votes
  3. RSS Feeds for Recent, Popular, and User specific quotes.

What’s next? You tell me. The alpha testers I have so far have had some great feedback. Here is my punchlist:

  • Quote updates via email
  • Pagination
  • Wordpress plugin
  • Friending / Following
  • Tag other user’s quotes and View items you have tagged
  • View items you have voted on
  • Tag cloud view
  • Manage my account settings

If you haven’t given the site a try yet, please do. I welcome your feedback.

Jul 23

The lastest version of VoteFu has a few useful enhancements:

  1. Protect against mass assignment forgery. The params hash will only be consulted for :vote => true or false. The rest has to be assigned in the controller.
  2. The acts_as_voteable mixin now has support for tallying votes.

The documentation will be updated tonight, but briefly, here is what you can do with the new tally() method:

1
2
3
4
5
6
7
8
@items = Item.tally(
  {  :at_least => 1, 
      :at_most => 10000,  
      :start_at => 2.weeks.ago,
      :end_at => 1.day.ago,
      :limit => 10,
      :order => "items.name desc"
  })

This will select the Items with between 1 and 10,000 votes, the votes having been cast within the last two weeks (not including today), then display the 10 last items in an alphabetical list.

More to come. Enjoy!

Jul 21

Quotes. I love quoting people. I have a ton of quotes saved in text files, on my blog, in my various mood messages on IM platforms, and who-know-where-else.

I decided to build an application to manage, organize, and syndicate quotes. You can read more about it on my project page for MyQuotable.

If you read this blog, you are invited to participate in my open alpha of MyQuotable.com.

Early Alpha image of myquotable.com

Early Alpha image of myquotable.com

Jul 14

UPDATE:

I made the classic coder mistake, and didn’t dig into the plugin code before working around it. I am leaving this post up instead of being embarrassed and deleting it because it a good example of why you should read your plugin code. After reading the code, I found that TAGLIST IS JUST AN ARRAY: you can simply do this and avoid all of the filter magic that I put in place below.

1
2
  # in config/initializers/tag_list.rb
  TagList.delimiter = " "

Drat. That’s way better.

As it turns out, this answer is in the README for acts_as_taggable_on_steroids, but not in acts_as_taggable_on. I’m sending Michael a note to ask him to include that detail in the documentation.

Original Post

Michael Bleigh’s acts_as_taggable_on plugin is a useful extension of acts_as_taggable_on_steroids.

When using it in forms, however, many users have noticed that the @model.tag_list field deals with comma separated lists of tags, like so:

1
2
# From the README
@user.tag_list = "awesome, slick, hefty"      # this should be familiar

If, in your edit view template, you provide a text field for your users to type in freeform tags, then you will find that your database tables have multiword tags instead of multiple tags. This is less than ideal. Users do not like to type commas.

Build your form like this, and you will see the issue when a user types in “awesome slick hefty” in the input box.

<% form_for([@model]) do f %>
    <div id="model_tags">
	Tags: <%= f.text_field :tag_list %> 		
    </div>
...
<% end %>

You will see this in your DB:

+----+-----------------------------------+
| id | name                              |
+----+-----------------------------------+
|  1 | awesome slick hefty               | 
+----+-----------------------------------+

What you wanted was this:

+----+-----------------------------------+
| id | name                              |
+----+-----------------------------------+
|  1 | awesome                           | 
|  2 | slick                             | 
|  3 | hefty                             | 
+----+-----------------------------------+

The Answer

My workaround involves using a before_filter to unroll the content of the input box, and a compactor method in the edit action to change the internal storage format of acts_as_taggable_on to the format that the user expects. Here is an example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# ... In the taggable_controller
  before_filter :join_tag_list, :only => [:create, :update]
 
  # GET /users/:id/quotes/1/edit
  def edit 
    @model ||= Model.find(params[:id])
    unroll_tag_list
  end
 
  private
  def join_tag_list
    # Split the tags on spaces and join into comma separated format
    params[:model][:tag_list] = params[:model][:tag_list].split(" ").join(",")
  end
 
  def unroll_tag_list
    # Split the tags on commas and join into a single space separated string
    @model.tag_list = @model.tag_list.join(" ")    
  end

That’s it! Now my users see and work with space-separated tags, and acts_as_taggable_on continues to store comma separated lists internally.

Jul 13

VoteFu is a voting mixin that allows you to extend your models to vote on one another. Largely based on Cosmin Radoi’s acts_as_voteable plugin, VoteFu adds named_scope support, a set of generators to make using the plugin easier, a :polymorphic association to the voting class (so you can have more than one model type perform votes), and some enhancements for Rails 2.1.

I have simplified the code it requires to cast a vote down from three to one. After setting up your models with the proper mixin functionality, you can cast votes like this:

1
2
3
4
5
   voter.vote_for(voteable)
   # OR
   voter.vote_against(voteable)
  # OR
   voter.vote(voteable, [true | false] )

You can also use the old acts_as_voteable sytnax (which will continue to be supported):

1
2
3
4
  vote = Vote.new(:vote => true)
  m    = Model.find(params[:id])
  m.votes    << vote
  user.votes << vote

I hope you find the plugin useful. Comments and feedback welcome. If there is enough demand, I’ll open up a lighthouse project to track issues.

Grab the code from GitHub or visit the VoteFu Page.

Jun 08

The new Rails routing has nice support for nested reources. And, I think it handles just enough nesting. Any more support, and it’s possible to go overboard and write ugly code.

For example, here is what Rails now supports for nested routing syntax:

1
2
3
4
5
  # Simple parent child association
  map.resources :user, :has_many => :posts
 
  # A parent with many types of children
  map.resources :posts, :has_many => [:comments, :trackbacks, :pingbacks ]

Nice. That is prefectly terse and descriptive, without being too clever.

There has been some debate and consternation over why a complicated multi-nested resource can’t be set up this way. For example, if I am building a project management tool, why can’t I do this?

1
2
map.resources :projects, 
  :has_many => [ :releases,  :milestones => [:tasks, {:features => :tasks}] } ]

That is, a project has many releases AND milestones, and in turn milestones have many tasks and features. And, features can also have tasks.

That’s complicated, and too much cleveness to jam into one statement. I think Rails 2.0.2 / 2.1 has it right in making you write that routing rule like this:

1
2
3
4
5
6
7
8
9
  map.resources :projects do |project|
    project.resources :releases
    project.resources :milestones do |milestone| 
      milestone.resources :tasks
      milestone.resources :features do |feature|
        feature.resources :tasks
      end
    end
  end

I hear a crowd of people clamoring for the uber-terse syntax outlined in the second listing. I think that’s a bad idea. It encourages hard-to-follow, obfuscated code.

When you have to do something complicated, it’s worth taking a few extra lines to make it clear to the next programmer.

Jun 06

Sorry for the outage on this blog yesterday. My hosting provider upgraded to Rails 2.1, and I had cleverly forgotten to:

bash# rake freeze:rails:gems

If this happens to you, and you make the unfortunate mistake of trying to instead update Typo to Rails 2.1 using:

bash# rake rails:update

You’ll be in even worse trouble, unless you’re prepared to do a lot of fixing:

bash# Pete$ script/server
./script/../config/boot.rb:45:in `require': no such file to load -- ./script/../config/../vendor/rails/railties/lib/initializer (LoadError)
	from ./script/../config/boot.rb:45:in `load_initializer'
	from ./script/../config/boot.rb:38:in `run'
	from ./script/../config/boot.rb:11:in `boot!'
	from ./script/../config/boot.rb:109
	from script/server:2:in `require'
	from script/server:2

Your best bet? Revert from source control and freeze Typo in your dev environment. If you’re stuck without source control or a dev environment, let me know and I’ll talk walk you through the alternate fix.

May 26

In 21 Ruby Tricks You Should Be Using In Your Own Code, Peter Cooper outlines some really useful best practices that programmers could benefit from. However, I think that there’s a tendency to over-obfuscate code from time to time in the Ruby world.

I like terse code. I like beautiful code. But I dislike tricky code. For example, let’s look at two tricks Peter Cooper outlines in his post:

Trick #7: Cut down on local variable definitions.

1
2
3
4
5
6
7
 
# Cooper says do this: 
(z ||= []) << 'test'
 
# Instead of this: 
z ||= []
z << "test"

Counting whitespace characters, Cooper’s version of the code is 20 characters long. The second version of the code is 20 characters long. Personally I find the second version easier to read.

Trick #9: 9 - Use ‘and’ and ‘or’ to group operations for single liners.

1
2
3
4
5
6
7
8
9
10
11
# Cooper likes this:
queue = []
%w{hello x world}.each do |word|
  queue << word and puts "Added to queue" unless word.length <  2
end
puts queue.inspect
 
# Output:
#   Added to queue
#   Added to queue
#   [”hello”, “world”]

Hm. While I think this does terse up the code by grouping statements that do a single logical operation onto one line, I think it’s still easier to read this way:

1
2
3
4
5
6
7
8
9
# I like this:
queue = []
%w{hello x world}.each do |word|
  unless word.length < 2 do
    queue << word
    puts "Added to queue" 
  end
end
puts queue.inspect

I think this method makes it easier to modify the discrete elements of the queue operations. For example, I may want to comment out that call to “puts” someday, which would be tougher if I’d used the “and” operator to string it all together on one line. I might also want to add a debug log statement. Again, it gets tough when you’re trying to fit it all onto one line.

My opinion is that finding tricks in the language that allow you to string a bunch of steps onto one line aren’t always the best way to make good code. Sometimes, finding a way to make a set of statements look like a logical unit is a better choice.

May 19

I was paying a parking ticket online to Montgomery County, MD. When the site processed my payment, it submitted to the action shown below. Haha.

I am pretty sure I wouldn’t make that mistake with rails routes.