Thursday, June 7, 2012

Multiple Select Combo Form for Rails

I was trying to find the way to make a dropdown combo box with multiple selection for a Rails project. There are many tips in the documentation and online, but I could not find the right way to make sure the options were initialized to their proper values when the form is created. For example, if a book already belongs to categories "fiction" and "mystery," how to make sure that those are already selected and highlighted on the form. This is what I finally arrived at. A key point is that the column name needs to be “{collection}_ids” and not simply “{collection},” in order for the right choices to be automatically selected when the form is built. This example would be used in selecting multiple categories to apply to some model.
= fields_for :record do |form|
  = form.label :category_ids, “Categories”
  %br
  = form.select :category_ids, 
       Category.collect {|x| [x.name, x.id]}, {}, :multiple => true
I think the standard update action in the controller will handle this without any special action. To do the update manually, you could just say
@record.update_attributes(:category_ids=>params[:record][:category_ids])

See the Rails doc at http://bit.ly/LDDSma.

Wednesday, April 25, 2012

Add Check-in, Check-out to Dropbox

Dropbox is a great solution for online backup and, to some extent, for simple collaboration. One limitation, however, is that there is no way to know when a collaborator has opened a file for editing. If two people edit a file at the same time, Dropbox will save both edited files which will not have conflicting changes.

Notifybox is a third-party solution to this problem. It only works with Microsoft Office documents and on Windows, but it does seem workable and is free for use on a single folder.

When you open a file within a monitored folder, a dialog pops up:

image

If you choose “Check-out,” you can go ahead and edit the file. When you save and close it, Notifybox informs you that it is now checked-in.

If you choose “Cancel” instead, a dialog box tells you that you should close the file without saving it. This isn’t enforced, so it’s up to the user to pay attention to the prompts.

If you try to open a file that is already checked out by someone else, a different message tells you that you should close the file without saving since it is already in use. It appears that all the potential users must be using Notifybox … I don’t think that it can tell if someone has checked out a file unless that person is also using Notifybox.

It’s a simple solution that bridges a gap between Dropbox and more complex version control systems that are likely too intimidating for most casual users.

A full subscription costs $6 monthly and allows you to monitor multiple folders and subfolders, and to encrypt and compress files in your Dropbox folder. The free version lets you monitor a single folder.

Wednesday, March 28, 2012

Dead Paypal Security Key

imagesJust on the off chance someone else has the same problem … My PayPal security key is several years old and last night it finally quit. It flashed these messages when turned on:

88888888
(some gobbledygook strange characters)
batt  75
--E994--
Soft3328

I presume it means the battery was low, which wouldn’t be surprising. Well, should I just give it to my son to play with, or try to change the battery? I paid $5 for it but now they don’t even sell the key-chain model, and the credit-card model costs $30. Plus, I’m in Nigeria and it would take a good while to get a replacement.

I opened it up, which is fairly easy, and found a standard CR2032 lithium battery. I slid it out and slid in a new one. Uh oh, same error message! Well, let it sit for a while without the battery, I thought. So I took it out again, waited 5 minutes, then put the new one in. Now it just shows 88888888 no matter what! The button does nothing, the display does not turn off after a minute. Shorting some of the pads inside doesn’t do anything either. So now it’s no use even as a dumb toy.

It all makes sense, though. The key works by generating a 6 digit number every 30 seconds, and the number is verified by PayPal as belonging to your key. That means it’s tied to real clock time. If the battery is low and the unit thinks it may be unable to assure the accurate time, then it has to fail. Once that state has occurred, there should be no way to restore the correct state. Changing the battery doesn’t change the fact that the time has become suspect. Waiting 5 minutes before changing the battery probably put the key into a factory new state where it is waiting for programming.

Bottom line: If your security key fails, don’t bother trying to fix it. My own feeling is that it’s not worth $30 for the current credit-card-size key unless you have a weak password or are really careless with it, but then I might feel differently if I see a $3000 fraudulent charge on my account some day!

Saturday, November 13, 2010

Creating an Autocompleting Association Input in Rails3 + ActiveScaffold + JQuery UI

Summary: This is a way to add an auto-completing input on a form in Rails 3 + ActiveScaffold using JQuery UI. No method or template overrides are needed. Only one short Javascript function is used. Note that ActiveScaffold uses Prototype rather than JQuery by default, so existing projects based on Prototype will have to be adjusted to use JQuery instead (as well?) for this to work.

The Rails/ActiveScaffold project I’m working on has models for Member and Country. Each member belongs to a country—i.e. has a nationality. This is all done in the usual way using a key country_id in the member model to refer to the country:

class Member < ActiveRecord::Base 
belongs_to :country

and

class Country < ActiveRecord::Base 
has_many :members

The usual way of making an editing control in the member create and update forms is simply to add the line

config.columns[:country].form_ui = :select

to the members controller. This creates a dropdown select box populated with the labels and ids (values) of the countries table. If your list of options (countries in this case) is very large, however, it can be impractical to send the entire list for the user to select from. Hence the need for something like an Ajax-based autocompletion input. As the user enters characters, the newly forming string is sent back to the server, which returns a list of possible options that match the string to the current point. Type “Z” for the country and “Zimbabwe” and “Zambia” pop up.

I've spent several days trying to convert a select-box to an autocompleting-text-box in my Rails project. One thought was that perhaps I should have left well-enough alone rather than make the change. Even selecting a list of several hundred options for a select list does not add too much overhead: say 300 items of 20 characters each = 6 KB, hardly worth worrying about. Still, I’ve done it so will document what worked for me.

My second thought is actually a question: why isn’t it dirt-easy to do this in Rails and/or ActiveScaffold? Is it so rarely used? Or perhaps I simply missed the easy way even though I did quite a bit of searching. There is an auto_complete plugin but I couldn't get it to work with Rails 3 and ActiveScaffold--perhaps I could now that I understand more. I was greatly helped by Anup Narkhede's helpful example. In the end, though, I found a method that seems to me even easier than using the plugin!

How to Do It


Anyway, this is how I did it – I’m sure there are better ways. To start with an overview, the way I did this is:

  1. Adjust the member model so that we can set the country_id indirectly just by saying this_member.country_name='France'. To do this define accessor method's to read and write the member's country name. The read method looks up the country_id and returns the name, while the write method country_name= sets the country_id based on the name.
  2. Replace the form's input for country_id with one for country_name.
  3. Create a lookup function to be called by JQuery. For example, if JQuery sends 'Z' the function will return {'Zambia','Zimbabwe'}.
  4. Add the JQuery autocomplete function to the country_name input.

The Details


Start with these models:

class Member < ActiveRecord::Base 
  belongs_to :country
end

class Country < ActiveRecord::Base
  validates_uniqueness_of :name
  has_many :members
end

Step 1: add the accessor functions to the member model:


class Member < ActiveRecord::Base 
  belongs_to :country

   def country_name
     Country.find(country_id).name
   end

   def country_name= (name)
     country = Country.find_by_name(name)
     self.country_id = country.id if country
   end
end

Step 2: Replace the form's input for country_id with one for country_name:


class MembersController < ApplicationController
  active_scaffold :member do |config|
    config.columns = [:name, :country_name]
  end
 end

Totally Optional Sidetrack: Before I hit on the technique of using the accessor methods in the model, I was overriding the MemberController update and create methods, as Anup Narkhede had shown. If for some reason you use a variation of that technique, there is one gotcha to be aware of. You cannot simply look up the id and insert it as params[:record][:country] unless the :country column was included in the form, because it will be ignored. This is part of the security of Rails 3: the user can't pass back arbitrary fields, because only those present in the form are processed. So, when I was overriding the controller update and create methods, I had to include the original :country column as a hidden field in the form. I'm guessing that this is also the reason that Anup first saved the country id in an instance variable @country, then used it in a before_create_save method rather than just adding the new element to the parameter hash.

Sidetrack: Note that you could compose any kind of string to use as the label rather than using "name." You would simply write the accessors for whatever you wanted. For example, you could use nationality rather than country name. Whatever is used, however, must be present and unique for each country so that the label-to-id lookup will return a single, valid country.

Step 3: Create a lookup function to be called by JQuery.


In app/controllers/autocomplete_controller.rb

class AutocompleteController < ApplicationController

  def country
    @countries = Country.where("name LIKE ?", "#{params[:term]}%").select("id, name")
    @json_resp = []
    @countries.each do |c|
      @json_resp << c.name
    end

    respond_to do |format|
      format.js { render :json => @json_resp }
    end
  end

I would have preferred to put this into the existing CountriesController, but for some reason when I did that the response was very slow (2+ seconds). In any case, it's important to get the routing right. I used

  match 'autocomplete/:action'

Step 4. Set up JQuery


We need to add JQuery and JQuery UI to our project if they're not already present. Add to your default template (or elsewhere as long as it will be available)

<%= stylesheet_link_tag 
  'http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.4/themes/ui-lightness/jquery-ui.css' %>

<%= javascript_include_tag 'http://ajax.googleapis.com/ajax/libs/jquery/1.4.1/jquery.js' %>
<%= javascript_include_tag 'http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.4/jquery-ui.js' %>

If you prefer, you can load the files to your own Rails public/stylesheets and public/javascript folders and link to them there.

Finally, add this short script to public/javascripts/application.js:

$(function() {
  $( ".country_name-input" ).live("click", function(){
    $(this).autocomplete({
      source: "autocomplete/country.js"
      });
  });
});

The country_name-input (be careful of the underscore and hyphen) is the class of the input. You could use an ID or other means of specifying it depending on what ActiveScaffold or other framework is generating.

The .live("click" piece is used to attach the JQuery UI autocomplete widget to country_name-input as soon as the input is clicked. We do this because, in ActiveScaffold or other Ajax-based views, the input may not exist in the DOM when the page loads, so some other event must be used to attach it. The demo and documentation for JQuery UI autocomplete are at http://jqueryui.com/demos/autocomplete/.

Everything is in place and should work now, once you put a few countries into the countries table.

Please comment if you have any questions, corrections, or suggestions!

Tuesday, October 12, 2010

Change or Remove the Horizontal Line Above Footnotes in MS Word 2007

This should be a simple thing, right? If you don’t want a separating line above the footnotes, just remove it. In my case, the end notes flow to several pages, and a line is still being put at the top of every page, conflicting with the page header.
I look around and tried to find how to do this, but (a) the help documentation doesn’t seem to say, at least not in a place I thought to look and (b) most of the answers on the Web refer to the same set of instructions from years ago for Word 2003, even when they claim to be for 2007. The instructions are actually almost the same, except that Word 2007 no longer has a “normal” view, which is what you are supposed to start with in the instructions.
Anyway, here is how to do it in Word 2007
  • Select Draft from the View tab on the ribbon
image
  • Select “Show Notes” from the References tab. If you don't see “Show Notes,” be sure you're looking at “References” and not “Review.”
image
  • Now you should see your notes in the bottom pane, below this menu:
image
  • Since “All Endnotes” is the first item in the drop-down box, you would think that the other options would be “All Footnotes” or similar. However, this is where you select the separator formatting & other options. Select “Endnote Separator.” I suppose if you have footnotes, you will see “Footnote Separator.”
image
  • Now you can delete the separator or define a new one. I don’t know if a graphic can be substituted; I couldn’t find a way. Be sure to re-define the “Endnote Continuation Separator” also, if you need to; it’s what appears on subsequent pages.
image
Thanks to Rob van der Heijden whose post was the one that finally got me on the right track!

Saturday, April 3, 2010

Energy-efficient LCD Monitors

Many of us live in areas where electrical power is at a premium, so one of the threads I follow in this blog is low-power computing. It’s amazing how far you can stretch your amps and watts these days without loosing much computing power.

I’ve recently bought a Fit-PC and have an order in for the Fit-PC2 model. These are very small “desktop” computers that run on only a few (6-8) watts, but do all the usual things you need a computer to do. In contrast, typical laptops run at 30-60 watts.
I haven’t had a chance to try out the Fit-PC yet, because I need a monitor. Since I’ve been living out of a suitcase for the last 7 months, I’ve just been using a laptop. Now that I’m getting ready to return to Nigeria in two months, though, I’m looking for an energy-efficient monitor.

One review of energy-efficient monitors covers four models, but only one is in the size range I want for carrying overseas: the Lenovo Thinkvision L1940P. It runs at 18 watts, is rated 5 (of 5) stars by one reviewer on Amazon, and costs $222. However, Amazon also sells the 19-inch ViewSonic VX1932wm-LED, which runs at only 15 watts, costs $167, and is rated 4.5 stars by 7 users. That's the one I plan to buy. With that monitor and the fit-PC, I'll be using barely 23 watts when using the computer at maximum capacity. That means I could run it for 24 hours straight on half the capacity of a 100 amp-hour 12V storage battery. Of course, the performance is nowhere near that of a “normal” desktop or even laptop, but it should be fine for most tasks (browsing, email, word-processing). I’ll let you know!

Wednesday, March 31, 2010

Send and Receive Faxes Online – Free (or almost free)!

Faxes are becoming less essential in our lives as we can usually substitute scanned documents sent by email. Now and then, however, it’s useful or even necessary to send or receive a fax. There are a couple of low-cost (or free) solutions for this.

If your phone line is connected to your computer, then you can simply use the built-in fax services of you computer. However, that can be hard to set up. In any case, many of us no longer have working phone lines.

To send faxes, the service I like is FaxItNice. You can fax a document up to 10 pages long for $5. However, the better option is to sign up for the plan where you pay $20 up front, then pay only $0.18 per page whenever you want. Credits don’t expire. I’ve been using the service for 7 years and so far it has been quite stable and reliable. You just fill in name and number of the recipient, select a document you want to send (Word, text, PDF, images, and many other formats), and press the button to upload the document. You can preview the fax, add a cover sheet, then click to send. The service then sends the fax for you, redialing as needed until it gets through. You can try the system for free (one fax) here.

To receive faxes, which I need to do even less often than sending them, I use K7. It’s completely free. You just sign up to get a phone number which will then receive your faxes. When someone sends a fax, K7 will convert it to a fairly small image file and email it to you. You can also log in to view your faxes.

There are other services for sending and receiving faxes, but I haven’t found any better deals than these. Let me know if you have more suggestions.