Wolfmans Howlings

A programmers Blog about Ruby, Rails and a few other issues

Having multiple text_field_with_auto_complete in the same view

Posted by Jim Morris Wed, 18 Oct 2006 06:45:47 GMT

I ran into a problem using text_field_with_auto_complete in a view where I wanted to have many of them created by an iteration. You can use the :index option for the text_field, but it doesn't carry over to the various divs used in the AJAX calls.

<% 0.upto(10) do |legi|  %>
<% @leg= @mission.legs.find(:first, :conditions => ['legnum = ?', legi]) %>
text_field_with_auto_complete( :leg, :name, {:index => legi, :size => 20})
<% end %>
<% end %>

is what you really want to do, just as for a regular text_field.

I googled around and found this bug report for a fix to text_field_with_auto_complete that allows :index, as that fix does not appear to be in the current stable release of Rails or on Edge rails, I just created a my_text_field_with_auto_complete and it worked like a charm!!

  def my_text_field_with_auto_complete(object, method, tag_options = {}, completion_options = {})
    if(tag_options[:index])
      tag_name = "#{object}_#{tag_options[:index]}_#{method}"
    else
      tag_name = "#{object}_#{method}"
    end

    (completion_options[:skip_style] ? "" : auto_complete_stylesheet) +
        text_field(object, method, tag_options) +
        content_tag("div", "", :id => tag_name + "_auto_complete", :class => "auto_complete") +
        auto_complete_field(tag_name, { :url => { :action => "auto_complete_for_#{object}_#{method}" } }.update(completion_options))
  end

So I put this in app/helpers/application_helper.rb and simply use my_text_field_with_auto_complete in my views.

Here is what the controller side looks like...

  def auto_complete_for_leg_name
    leg= params[:leg].keys[0] # get index as its always only one at a time
    auto_complete_responder_for_name params[:leg][leg][:name]
  end
...
private
  def auto_complete_responder_for_name(value)
    param= value.downcase + '%'
    find_options= {
      :conditions => [ 'LOWER(lname) LIKE ?', param ],
      :order => 'lname ASC',
      :limit => 6
    }
    @names = Person.find(:all, find_options)
    render :partial => 'names'
  end

Posted in  | Tags ,  | 18 comments | no trackbacks

Epsilon Programmers Editor

Posted by Jim Morris Sun, 01 Oct 2006 00:20:00 GMT

I use Lugarus excellent Epsilon Editor for most of my programming editing needs, on Win32 and Linux.

(An exception is for Java programming where I use Eclipse).

I have spent some time writing extensions to Epsilon to handle Ruby and Rails programming, inspired mostly by Textmate on Mac/OSX, and Eclipse for Java. I tried using the Eclipse for Ruby, but I was very disappointed, also the developers have made some design decisions I can't live with (like no auto indent after opening braces etc).

I have tried Komodo Pro as described here.

OK the Epsilon editor is not free, it costs about $250 ($99 for an upgrade) which includes all platforms, and the license lets you use it on your Laptop and Desktop, in fact you can use any version of Epsilon on up to four computers you own. The result is a professional, solid, stable Editor, that is what you get when you pay for something. (This editor has been around at least 20 years, which is when I started using it on DOS!). In addition to running as an X Windows program and a console program on windows and Linux it also runs on Mac OS X, FreeBSD, OS/2 and DOS.

The Editor is an Emacs clone out of the box, it also has CUA and Brief emulations (well key bindings). The best feature IMHO is the fact you get most of the source code for the editor which is written in its own c-like language called eel. This makes it much easier to write extensions and customizations for the editor if you are familiar with C. (I never could wrap my brain around Lisp which is why I don't use GNU Emacs). It also runs in console mode as well as windows mode, which is useful if you have to login via ssh etc to edit files.

Seeing how every programmer has their own ideas of what an editor should do and its look and feel, easy customization is crucial.

The extensions I have written for Epsilon are all freely available, as are extensions written by other users. (Various language modes, template extensions, SCM extensions etc).

In the past I wrote a java help extension that tried to do context sensitive help, this worked OK but not as well as Eclipse.

Recently I wrote a bunch of extensions for Ruby and Rails, thanks to the ruby mode extension written by Timothy Byrd (available on Lugaru's download page) I was able to get syntax highlighting and formatting already done. I added some simple help extensions, a snippet facility (ala Textmate), and some convenience actions for Rails development. I also extended Timothy's ruby mode extension with something that completes the #{} when # is typed in a string. (I first saw this in Textmate and hated it, but it grew on me, until I had to have it on Linux). I have avoided the temptation to also do the automatic closing of { ( " etc that you find in Textmate because I still hate those, but they are easy to do using the same technique I used for #{}.

I've also added the ability to run the current buffer through the ruby interpreter and show the results in a pop up window, also to run a specific unit test if the file is a Ruby test case.

The key strokes any command uses is easily modified, as well as the colors used for syntax highlighting.

The version of ruby_mode.e on Lugarus site does not currently have the latest changes I have made, so it can be downloaded from the link below...

The other extensions can also be downloaded from my site...

These can be installed by copying them to your ~/.epsilon folder, and adding a load line to your einit.ecm file, see the comments in the source file. The snippets should be un-tarred into the ~/.epsilon folder. The README explains how to load them.

eg add this to your einit.ecm file:

(load-eel-from-path "ruby_mode.e" 2)
(load-eel-from-path "rubyhelp.e" 2)
(load-eel-from-path "snippets.e" 2)
(load-eel-from-path "rename_in_place.e" 2)

Also here are the color codes I use for ruby_mode, these are designed by Timothy: (Change the window-black to whatever color set you are using)

&window-black color class for ruby-brace: [0x725CEB on 0x0]
&window-black color class for ruby-class: [0xD9D240 on 0x0]
&window-black color class for ruby-comment: [0xC0C0C0 on 0x0]
&window-black color class for ruby-global: [0xFFB737 on 0x0]
&window-black color class for ruby-keyword: [0xFF8000 on 0x0]
&window-black color class for ruby-number: [0xFF9090 on 0x0]
&window-black color class for ruby-punctuation: yellow on black
&window-black color class for ruby-regexp: [0x007FFF on 0x0]
&window-black color class for ruby-shell-cmd: [0x8FFF2F on 0x0]
&window-black color class for ruby-shell-subst: [0x8FFF8F on 0x0]
&window-black color class for ruby-str-subst: [0xFFC0C8 on 0x0]
&window-black color class for ruby-string: cyan on black
&window-black color class for ruby-perl-var: red on black

Although you can browse for files that epsilon has currently open and switch between these buffers, the method is fairly crude by todays standards of tabbed windows etc, so I wrote a little graphical helper called project_browser.rb that uses the fox window toolkit. It just shows a tree of the directory it was given on the command line, and if you click on any of the files they open in the epsilon window. This is a lot like the project browser windows you find in Textmate, Eclipse and others. You need to install fox version 1.4 and the fox14 gem too to use this. It also allows you to exclude files and directories from display in the tree, by putting a YAML file called .proj_exclude.yaml in the project directory, I'll document this further if there is any interest in it (Leave a comment if you are interested). It allows multiple project directories to be open and shows them in tabs at the top. I'm also working on integrating subversion into it. It could also be adapted to work with virtually any editor that allows files to be sent to the editor by a separate process.

project browser

Posted in , ,  | Tags , , , ,  | no comments | no trackbacks

Using Ruby SVN bindings to get file status

Posted by Jim Morris Mon, 04 Sep 2006 18:51:35 GMT

If you Google around for information or even some documentation on the ruby SVN bindings you will find plenty of comments that it simply is not documented, so when I wanted to add an SVN status check to a UI I was working on (a project browser window for Rails), I had to "Use the source Luke". However given the bindings are actually mostly automatically generated by SWIG, and the actual details are hidden in a goo of swig generated c code, even that was a challenge.

Eventually I realized that the API was almost identical to the c level subversion API, not surprisingly, and for the most part it works the way you would expect. However I could not find any examples of the client status call, so here it is for anyone else struggling with this issue.

require "svn/core"
require "svn/client"
require "svn/wc"
require "svn/repos"

# define Consts for all the numeric status values
NONE = 1
UNVERSIONED = 2
NORMAL = 3
ADDED = 4
MISSING = 5
DELETED = 6
REPLACED = 7
MODIFIED = 8
MERGED = 9
CONFLICTED = 10
IGNORED = 11
OBSTRUCTED = 12
EXTERNAL = 13
INCOMPLETE = 14
# I added these so I can amalgamate repo and local status
UPDATED = 15
MODIFIED_NEWER = 16
NEWFILE = 17

$modes= { # Map status to English
    NONE => "None",
    UNVERSIONED => "unversioned",
    NORMAL => "normal",
    ADDED => "file added",
    MISSING => "missing",
    DELETED => "file removed",
    REPLACED => "deleted and then re-added",
    MODIFIED => "modified",
    MERGED => "received repos mods",
    CONFLICTED => "file modified and in conflict",
    IGNORED => "ignored",
    OBSTRUCTED =>  "unversioned resource is in the way of the versioned resource",
    EXTERNAL => "unversioned path populated by an svn:external property",
    INCOMPLETE => "directory doesn't contain a complete entries list",
    UPDATED => "newer version in repository",
    MODIFIED_NEWER => "modified and newer version in repository",
    NEWFILE => "new file in repository",
    nil => "Unknown status"
}

# this amalgamates the most common status between local and repository
def compute_status(status)
  # if modified in repo and localy
  return MODIFIED_NEWER if status.repos_text_status == MODIFIED && status.text_status != NORMAL
  # if added in repo and does not exist locally
  return NEWFILE if status.repos_text_status == ADDED && status.text_status == NONE
  # if repo is none then use local status
  return status.text_status if status.text_status != NORMAL
  # if normal locally but changed in repo
  return UPDATED if status.repos_text_status == MODIFIED
  # otherwise it must be normal
  NORMAL
end

ctx = Svn::Client::Context.new()

rev = ctx.status("/home/user/project/myproject", "HEAD", true, true) do |path, status|
  astat= compute_status(status)
  puts "#{path}: #{status.text_status},#{status.repos_text_status} = #{$modes[astat]}"

  unless status.entry.nil?
    puts(".....name: #{status.entry.name}")
    puts(".....url: #{status.entry.url}")
    puts(".....repos: #{status.entry.repos }")
    puts(".....revision: #{status.entry.revision}")
    puts(".....kind: #{status.entry.kind}")
    puts(".....schedule: #{status.entry.schedule}")
    puts(".....deleted: #{status.entry.deleted}")
    puts(".....absent: #{status.entry.absent}")
    puts(".....incomplete: #{status.entry.incomplete}")
    puts(".....cmt_date: #{status.entry.cmt_date}")
    puts(".....cmt_rev: #{status.entry.cmt_rev}")
    puts(".....cmt_author: #{status.entry.cmt_author}")
    puts(".....prop_time: #{status.entry.prop_time}")
    puts(".....text_time: #{status.entry.text_time}")
  end
end

This is a sample, it shows how to setup the client context, call the status call, and parse the results. I also added some sugar by defining the numeric status codes and adding a hash to translate them to english. I also compute an amalgamated status code from the local status and status in the repository, to get the extra status codes I added above.

The status parameter returned by the status() call turns out to be a standard svn status structure, containing various fields, the most interesting one as far as status is concerned are text_status and repos_text_status fields which are numeric fields which specifies the SVN status of the file locally and in the repository, I have created in the example above a bunch of constants to define each of the states, and a hash to turn them into english.

Note the compute_status() method which looks at both the local status and repository status to see what the relative state of any file is wrt the repository.

The other interesting field is the entry field, which contains bunch of data as shown above, and in the structure definition below.

There are more parameters that can be passed to the status call, but the last two I show should be set to true, as that allows recursion into sub directories and shows all files, by default recursion is true but all_files is false which only show files not under svn control.

The developers have made some attempt to make this relatively easy to use, you can forget about the apr pools and stuff, and the second parameter is usually a structure that tells it the version or revision to search for, this has been simplified so you can pass in a string describing the revision ("HEAD", "TAIL", etc) or a number denoting the revision number.

The status and entry structures have also had the types converted into convenient ruby types, but for reference I show the c struct definitions from the various svn header files...

Here is the full status structure from svn_wc.h

typedef struct svn_wc_status2_t
{
  /** Can be @c NULL if not under version control. */
  svn_wc_entry_t *entry;

  /** The status of the entries text. */
  enum svn_wc_status_kind text_status;

  /** The status of the entries properties. */
  enum svn_wc_status_kind prop_status;

  /** a directory can be 'locked' if a working copy update was interrupted. */
  svn_boolean_t locked;

  /** a file or directory can be 'copied' if it's scheduled for
   * addition-with-history (or part of a subtree that is scheduled as such.).
   */
  svn_boolean_t copied;

  /** a file or directory can be 'switched' if the switch command has been
   * used.
   */
  svn_boolean_t switched;

  /** The entry's text status in the repository. */
  enum svn_wc_status_kind repos_text_status;

  /** The entry's property status in the repository. */
  enum svn_wc_status_kind repos_prop_status;

  /** The entry's lock in the repository, if any. */
  svn_lock_t *repos_lock;

}

and the entry structure

typedef struct svn_wc_entry_t
{
  /* IMPORTANT: If you extend this structure, check svn_wc_entry_dup() to see
     if you need to extend that as well. */

  /* General Attributes */

  /** entry's name */
  const char *name;

  /** base revision */
  svn_revnum_t revision;

  /** url in repository */
  const char *url;

  /** canonical repository URL or NULL if not known */
  const char *repos;

  /** repository uuid */
  const char *uuid;

  /** node kind (file, dir, ...) */
  svn_node_kind_t kind;

  /* State information */

  /** scheduling (add, delete, replace ...) */
  svn_wc_schedule_t schedule;

  /** in a copied state */
  svn_boolean_t copied;
  /** deleted, but parent rev lags behind */
  svn_boolean_t deleted;

  /** absent -- we know an entry of this name exists, but that's all
      (usually this happens because of authz restrictions)  */
  svn_boolean_t absent;

  /** for THIS_DIR entry, implies whole entries file is incomplete */
  svn_boolean_t incomplete;

  /** copyfrom location */
  const char *copyfrom_url;

  /** copyfrom revision */
  svn_revnum_t copyfrom_rev;

  /** old version of conflicted file */
  const char *conflict_old;

  /** new version of conflicted file */
  const char *conflict_new;

  /** working version of conflicted file */
  const char *conflict_wrk;

  /** property reject file */
  const char *prejfile;

  /** last up-to-date time for text contents (0 means no information available)
   */
  apr_time_t text_time;

  /** last up-to-date time for properties (0 means no information available) */
  apr_time_t prop_time;

  /** base64-encoded checksum for the untranslated text base file,
   * can be @c NULL for backwards compatibility.
   */
  const char *checksum;

  /* "Entry props" */

  /** last revision this was changed */
  svn_revnum_t cmt_rev;

  /** last date this was changed */
  apr_time_t cmt_date;

  /** last commit author of this item */
  const char *cmt_author;

  /** lock token or NULL if path not locked in this WC
   * @since New in 1.2.
   */
  const char *lock_token;
  /** lock owner, or NULL if not locked in this WC
   * @since New in 1.2.
   */
  const char *lock_owner;
  /** lock comment or NULL if not locked in this WC or no comment
   * @since New in 1.2.
   */
  const char *lock_comment;
  /** Lock creation date or 0 if not locked in this WC
   * @since New in 1.2.
   */
  apr_time_t lock_creation_date;

  /* IMPORTANT: If you extend this structure, check svn_wc_entry_dup() to see
     if you need to extend that as well. */
} svn_wc_entry_t;

For the most part you can simply reference the fields in the structure and get a relatively understandable result.

The status call is defined in ruby as this

def status(path, rev=nil, recurse=true, get_all=false,
           update=true, no_ignore=false,
           ignore_externals=false, &status_func)

The first parameter is the path on the local file system to get the status of, the second is the revision to get, something like "HEAD", or 2345 can be passed in, the next parameter is whether to recurse into subdirectories, the next tells it to return the status of all files if set to true, default of false and only returns "interesting files" ie local mods and/or out-of-date or not versioned. Update is set to true it will contact the repository to get more information wrt to the version specified in the first parameter (which is ignored otherwise). I couldn't find any documentation in the c stuff about the no_ignore parameter, but the ignore_externals tells it to return status on externals or not. See the header file svn_client.h and the call svn_client_status2 for more documentation.

Posted in ,  | Tags , , ,  | 3 comments | no trackbacks

Getting f-spot to upload to flickr on KUbuntu

Posted by Jim Morris Mon, 28 Aug 2006 03:29:00 GMT

f-spot is a good photo importer and organizer, but it is a gnome application. When I installed it on KUbuntu, which uses KDE, using aptitude install f-spot it worked ok, except I got the following error when I tried to send my photos to flickr.

GLib.GException: There is no default action associated with this location

After some Googling I found this solution...

> gconftool-2 --set --type=string /desktop/gnome/url-handlers/http/command '/opt/firefox/firefox %s'

my Firefox is in /opt/firefox, so you may have to change that path, or even point to a different browser.

Make sure gconftool-2 is installed (it was on mine after installing a bunch of gnome apps).

It sets the default browser, and after this I was able to upload to flickr.

Posted in  | Tags , ,  | no comments | no trackbacks

Older posts: 1 ... 6 7 8 9 10