jump to navigation

These Chains of Selects October 29, 2008

Posted by reidmix in Code, Example, Javascript.
Tags: , , , , , ,
3 comments

I was looking for javascript library that would manage select (HTML) elements that are dependent on one another.  These are the drop-downs you often see on online car sites where you select a make and it populates the next drop-down with the models for that car make.

I was hoping that there would be a libary that was publically available, object oriented, small, backed by JSON, and would even like it to be based on the Prototype library.  All I could find was a library that may satisfy one of my requirements but not all.  Libraries I found were either procedural, terribly bulky and complicated, and never backed by JSON.

In my buy it or build it moment, I decided to build it: here is a chained_selects javascript library.  It’s fairly lightweight, it runs in about 85 lines (comments and whitespace included), you can initialize it with javascript objects or JSON, it’s object oriented and relies on just a little bit of Prototype.

You need the heirarchy of data to back a ChainedSelect object, the form, and selects that are assoicated with each “level” in the heirarchy.  It can have as many levels you want in the hierarchy from 2 up.  So, in my car example, imagine you have a two car makes (Acura and Volkswagen) and the Acura make has two models (the RL and the TL, sweet!), and the Volkswagen make has three models (Beetle, Jetta, and the Passat).  Each of the models have a database id.  My JSON for this heirarchy would look like:

var data = {"Acura":{"RL":1, "TL":2},"Volkswagen":{"Beetle":3,"Jetta":4,"Passat":5}}

You can see the first level is the outer-most hash with the makes, and the inner mosts hashes have the models and their db ids.  For rails programmers, this could be a rails hash that you run to_json with.

I set up my HTML form, with a select for each level in the heirarchy, like so:

  <form id="cars" method="post" action="/choose_car">
    <select name="make"></select>
    <select name="model"></select>
  </form>

Then I can setup the ChainedSelect object to manage these selects, using the data above:

new ChainedSelect(data, 'cars', ['make','model']);

This says, created a chained select with my data for the form with id ‘cars’, with the first level select with the name ‘make’ and second level select with the name ‘model’.

The first select will load with three elements:

  • Choose make…
  • Acura
  • Volkswagen

When Volkswagen is selected, the second select will load:

  • Choose model…
  • Beetle
  • Jetta
  • Passat

If Acura is then chose, the second select will refresh with the correct data. If the ‘model’ is chosen let’s say Jetta, then the id for that model is sent in the form (4).  For rails developers, the name of the select needs to be different from the label (“Choose model…”), you can specify a label and a select name separately, with an embedded array, like so:

new ChainedSelect(data, 'cars', ['make',['car[model]','model']]);

Oftentimes, we have an edit page with the values already prefilled on our forms, we can add another array parameter that specifies the path to the selection:

new ChainedSelect(data, 'cars', ['make','model'], ['Volkswagen','Jetta']);

Lastly, when the final select is chosen, sometimes we want to take an action or an AJAX call, we can supply an onComplete function to run which is yielded a the value chosen:

new ChainedSelect(data, 'cars', ['make','model'], null, function(choice) { alert("You chose:"+choice); } );

You can use all of these elements together to get the behavior you want, I also have a little rails form helper to create my javascript for me:

  def chained_selects(tree, form, selects, active, oncomplete=nil)
    %(new ChainedSelects(#{tree}, '#{form}', #{selects.inspect}, #{active.inspect}, #{oncomplete || 'null'});)
  end

I know that the script can be improved in many ways (Doug McInnes added the onComplete feature).  Please feel free to send patches or any bugs you may find.
Enjoy!

Include Health October 29, 2008

Posted by reidmix in Example, Plugins & Gems, Rails.
Tags: , , , , ,
1 comment so far

I created a little plugin to make it easier to offer health check URLs in all our applications called Health.  These URLs are a necessity for our SysAdmins to hook into Big Brother or Nagios.  These monitoring systems systematically poll an application at this URL to check the status of the application.

To get started, install the health plugin and include it in your Application controller:

class ApplicationController < ActionController::Base
  include Health
end
&#91;/sourcecode&#93;

That's it.  You should be able to navigate to <a href="http://localhost:3000/check_heath">http://localhost:3000/check_heath</a>. Health sets up the named route '<span class="s1">check_health' and</span> by default it will check the connection of the database by running a "<strong>select 1</strong>" SQL statement.  If all goes well you should see "SERVERUP".  If there's a problem with the query you will see "DBDOWN" or an 500 error depending on the level of problem.  You can turn off the DB checking (our SysAdmins wanted it):


ApplicationController < ActionController::Base
  include Health
  health_check :with_db => false
end

One more trick, it does is handle any additional / custom checks that you may require. Pass a block, Proc, or a symbol that represents the name of the method to the health_check directive:

ApplicationController < ActionController::Base include Health health_check do |controller| controller.has_donut? || "no donut" end end [/sourcecode] In this case, assuming the controller has a method called has_donut? which does not return false or nil, you will see the “SERVERUP” message, otherwise you will see what was last evaluated in a message like “PROCDOWN no donut”

And you can mix and match all three types of blocks, Procs, and symbols — more examples are in the README.

Hyphenated XML tags in Builder October 13, 2008

Posted by John Dewey in ActiveRecord, Example, Rails, Ruby.
3 comments

I typically use to_xml when building a shallow representation of my model in XML. It becomes hella difficult to maintain the format of my XML when nesting numerous levels deep. Sometimes you gotta use a Builder.

If you would like your tags to be hyphenated (like the to_xml default), here is a nice trick:

xml.__send__('hyphenated-tag-name'.to_sym) do
  xml.tag "data"
end

Coverage Nagging October 10, 2008

Posted by John Dewey in Code, Command Line, Example, Rails, RSpec.
add a comment

I like to keep an eye on my coverage every so often. I run the following in my applications root, for occasional coverage reminders.

while : ; do rake spec:rcov ; open coverage/index.html; sleep 3600 ; done

Reloading classes in RSpec August 11, 2008

Posted by John Dewey in Code, Example, RSpec, Ruby.
1 comment so far

I had a class where methods could change a class variable’s structure on load. However, RSpec doesn’t reload a class, so testing
these methods is difficult.

One can reload a class inside RSpec by adding a before task:

describe SimplyTabby, "remove_system_information class method" do
  before(:each) do
    Object.send(:remove_const, 'Clazz')
    load 'clazz.rb'
  end

  it "should ..."
	...
  end
end

IE6 Accept Header is Faulty and Makes format.any Suck May 14, 2008

Posted by reidmix in AbstractRequest, ActionController, acts_as_authenticated, Changeset, Code, Example, MimeResponds, Monkey Patch, Plugins & Gems, Rails.
Tags: , , , , , , , , , ,
11 comments

I’ve been using the awesome acts_as_authenticated plugin as the basis for user login and authorization. Its access_denied method takes advantage of the ActionController::MimeResponds.any method which will be invoked on a login_required before filter:

    def access_denied
      respond_to do |format|
        format.html do
          store_location
          redirect_to new_session_path
        end
        format.any do
          request_http_basic_authentication 'Web Password'
        end
      end
    end

This code essentially says, if it’s an html request, store the original request and redirect to the login page, any other requests get the 401 Unauthorized header which the user agent can then use to initiate an HTTP Auth. This bit of code only works correctly with Edge Rail’s 8987 Changeset that allows any to act as a catch-all for any request mime-types not already specified.

Awesome!
Except when Internet Explorer 6 comes into the picture and happily sends this
strange and incomplete ‘Accept:’ header:

image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*

As you can see, it doesn’t specify application/xhtml+xml or even text/html and what’s worse is the subsequent requests to the same page sends the catch-all */*.

What does this all mean?
When IE6 navigates to a page that requires login, instead of redirecting the user to the login page, the user is presented with an HTTP Auth Dialog. What’s worse is that in my application, cookies and other application state for an web-based end-user is set outside of the login_from_basic_auth method — you know — in my login action.

HTTP Auth Prompt by IE6

My guess is when the format.any fix goes out in the next rails release, IE6 users on acts_as_authenticated sites are going to be sad-faced. I’m not entirely sure the correct way to fix this problem but this is how I solved it in my application:

class ActionController::AbstractRequest
  def accepts_with_faulty_header
    @env['HTTP_ACCEPT']='*/*' if @env['HTTP_USER_AGENT'] =~ /msie/i and @env['HTTP_USER_AGENT'] !~ /opera|webtv/i
    accepts_without_faulty_header
  end
  alias_method_chain :accepts, :faulty_header
end

Originally, I thought to patch the Mime::Type.parse function, but decided that I wanted to make sure to look at the user agent for IE6. As you can see, I switch the HTTP_ACCEPT header to the catch-all */* string if the original HTTP_ACCEPT header is a match to the exact (bizarre) IE6 string and the HTTP_USER_AGENT is IE (matches MSIE but not Opera or WebTV). Then I call the original ActionController::AbstractRequest.accepts method.

I’d love to hear your ideas and suggestions — and if others have run into this problem as I didn’t find many users have this exact problem and how they may have solved it.

Update: Based on the conversation in the comments, I’ve re-written the patch to always change the HTTP_ACCEPT header to the catch-all */* if it is an IE Browser.

Run Rake Tasks with Delayed Job February 25, 2008

Posted by John Dewey in Code, Delayed::Job, Example, Rails, Rake.
1 comment so far

We needed to execute a Rake Task in the background, when a particular event occurred in Rails.  We decided to give Delayed::Job aka DJ a shot.  This “decorator” will execute a Rake task, while optionally passing ENV variables to the task.

Execute a Rake task from another task February 18, 2008

Posted by John Dewey in Example, Rake, Ruby.
3 comments

Executing a Rake task from inside another Rake task is a nice addition to your toolshed.  Passing parameters to the Rake task is even cooler.  ‘Rake::Task[“TaskName”].execute’ and ‘Rake::Task[“TaskName”].invoke‘ to the rescue.  A Rakefile that demonstrates the execution of a task with parameter passing.

Rake tasks :through Capistrano January 27, 2008

Posted by John Dewey in Capistrano, Example, Rake.
1 comment so far

Executing Rake tasks on host(s) through Capistrano is nothing new, and there are quite a few ways to do it. Some of the configurations out there, use a common Capistrano task to execute all Rake tasks. This works quite well, but you run into problems when scoping tasks to specific roles. The common Rake exec task is not scoped to a specific role (because it could run any task on any given role), so if you call it from a Cap task scoped to :db, it will still execute across all roles.

This was corrected by creating a Rake exec method instead of a Cap task. The method can pass any arbitrary variables and/or Rakefile to Rake.An example Capistrano task, displays it’s usage. There are a few Cap variables already set, such as rake and rails_env. Using the rakefile variable allows the execution of a specific Rakefile, which comes in handy when wanting to ignore the Rails Rakefile.

Why ignore the Rails Rakefile? In our configuration the Rakefile loads environment.rb, which sets the load_paths to our custom rubygems path – that means loading Rails! What happens when Rails is not installed, and you need a bootstrap task. The ability to pass in a Rakefile all of the sudden becomes useful.

Use ActiveResource to Consume Simple Objects Over REST January 16, 2008

Posted by reidmix in ActiveResource, Code, Example, Rails.
Tags:
add a comment

I’m sure that many organizations have services available via REST that are not produced by Rails and do not follow Rails conventions. That doesn’t mean you have to write your own client. For simple objects, use ActiveResource.

To take advantage of ActiveResource in this way use the ActiveResource::Base#find method. You need to find(:all), find(:first), or find(:one) but you need to pass a string of the path to the :from option along with any :params you need. If you pass a symbol as the :from option, this will not work and will employ all of the the ActiveResource::Base mechanics. A string will bypass this, for example:

class RestClient < ActiveResource::Base
  self.site = "http://java-based-service.company.com"

  def self.invoke(params = {})
    find(:one, :from => "/nonconventional/path/to/rest/service/", :params => params)
  end
end

object = RestClient.invoke(:irregular_id => 10) #=> service returns <data><name>reid</name><role>blogger</role></data>
object.name #=> reid
object.role #=> blogger