11/18/08

fun with link_to_remote

Permalink 04:58:10 pm, Categories: Ruby, 406 words

Code:

link_to_remote(name, options = {}, html_options = {})

Returns a link to a remote action defined by options[:url] (using the url_for format) that’s called in the background using XMLHttpRequest. The result of that request can then be inserted into a DOM object whose id can be specified with options[:update]. Usually, the result would be a partial prepared by the controller with either render_partial or render_partial_collection.

Examples:

Code:

link_to_remote "Delete this post", :update => "posts", :url => { :action => "destroy", :id => post.id }

Code:

link_to_remote(image_tag("refresh"), :update => "emails", :url => { :action => "list_emails" })

You can also specify a hash for options[:update] to allow for easy redirection of output to an other DOM element if a server-side error occurs:

Example:

Code:

link_to_remote "Delete this post",
     :url => { :action => "destroy", :id => post.id },
     :update => { :success => "posts", :failure => "error" }

Optionally, you can use the options[:position] parameter to influence how the target DOM element is updated. It must be one of :before, :top, :bottom, or :after.

By default, these remote requests are processed asynchronous during which various JavaScript callbacks can be triggered (for progress indicators and the likes). All callbacks get access to the request object, which holds the underlying XMLHttpRequest.

To access the server response, use request.responseText, to find out the HTTP status, use request.status.

Example:

Code:

link_to_remote word,
      :url => { :action => "undo", :n => word_counter },
      :complete => "undoRequestCompleted(request)"

The callbacks that may be specified are (in order):
:loading: Called when the remote document is being loaded with data by the browser.
:loaded: Called when the browser has finished loading the remote document.
:interactive: Called when the user can interact with the remote document, even though it has not finished loading.
:success: Called when the XMLHttpRequest is completed, and the HTTP status code is in the 2XX range.
:failure: Called when the XMLHttpRequest is completed, and the HTTP status code is not in the 2XX range.
:complete: Called when the XMLHttpRequest is complete (fires after success/failure if they are present).

You can further refine :success and :failure by adding additional callbacks for specific status codes:

Example:

Code:

link_to_remote word,
      :url => { :action => "action" },
      404 => "alert('Not found...? Wrong URL...?')",
      :failure => "alert('HTTP Error ' + request.status + '!')"

A status code callback overrides the success/failure handlers if present.

11/15/08

Lighthouse API integration in 10 minutes or less

Permalink 01:47:59 pm, Categories: Ruby, 535 words

As a second part in this series, I am going to talk about Lighthouse for a second. They rock. I built up a calendar app based on their API and the Ruby calendar_helper, and it barely took me a day. True story. I ended up using an API token, and I learned a lot about a lib/ based connection object, very interesting.

Here are a few caveats:
Don’t mess with the root class too much, it’s not really necessary for most of what I had to do. This is actually one of the cases where it makes sense to load the logic into the controller (a little bit).
All Lighthouse database objects are not created equal - Tickets are king, and everything else is nothing. I will explain more later - just don’t try to get picky with your call for Milestones. All I have ever been able to do is return a simple dump.

Installation: all I had to do was click through to the GIT repository. From there click lib/, and then lighthouse.rb. Cut and paste the code you see into a lighthouse.rb file in your lib/ folder. Everything you need is right there.

code snippet from my controller action:

Code:

if @project
      #calculate i differential
      month_now = Time.now.month
      year_now = Time.now.year
      i = ((@year - year_now)*12)+ (@month - month_now).abs
      if i == 1
        @tickets = @project.tickets(:q => "created:'before #{Time.now.day} day#{Time.now.day>1 ? 's' : ''} ago'")
      elsif i > 1
        @tickets = @project.tickets(:q => "created:'before #{i-1} months ago'")
      else
        @tickets = @project.tickets(:q => "created:'since 1 month ago'")
      end
      @milestones = @project.milestones(:q => "due_on:'#{@month}/#{@year}'")
    else
      false
    end

Thankfully, you can get picky with your call for Tickets. That is where the meat of the API is. The text based date searching is pretty cool, because you get a hard limit of 30 rows returned from your query, so you need to be granular enough to catch only what you want.

This is a remote controller action that I use to get info about a particular ticket:

Code:

def ticket_body
    ticket_id = params[:id]
    @project = Lighthouse::Project.find(:first)
    ticket = Lighthouse::Ticket.find ticket_id, :params => {:project_id => @project.id}
    out_string = ""
    #counter = 0
    ticket.versions.each do |version|
      this_body = version.body_html
      out_string << "<i>on #{(version.created_at - (60 * 60 * 8)).strftime("%m/%d at %I:%M%p")} #{get_owner_name(version.user_id)} said:</i> #{this_body ? this_body : "status change"}<br><br>"
    end
    render :text => out_string
  end

Notice the 8 hour time differential from UTC code, and that you have to iterate through the ticket.versions to pull out the notes (thanks to Lighthouse help staff for that one).

And then, of course, I have this at the top of my controller:

Code:

def initialize
    Lighthouse.account = 'xxx'
    Lighthouse.token = '1111919192384584XXXXXXXXXXX'
    @project = Lighthouse::Project.find(:first)
  end

like I said before, somehow the @project.milestones method won’t take a :q argument, so you’re stuck with just a simple ’select *’ query for your @milestones recordset. Maybe they will change this soon.

Ruby calendar_helper for dummies

Permalink 01:16:36 pm, Categories: Ruby, 811 words

So there I was, trying to piece together how to use this calendar_helper that I found for Ruby.
It actually works great, is more or less CSS-powered which is good for cross-browser issues, and is pretty easy to use BUT you sort of have to decipher how to use it. Everything you need is there, but there is not a spec of hand-holding. Ah, the good old days, when all you had to do was RTFM. Now you have to DTFM, the d standing of course for decipher, or possibly discern, assuming you actually have something akin to a manual. Anyway, here is what I got.

Good to know #1: You should actually read the part about installation - you have to add Readme.txt to the rake file in the vendors folder that is created on the calendar_helper install. Other than that, the install was a piece of cake. Remember to add all the pertinent files to your project - all the classes and style sheets, etc.

Good to know #2: Don’t bother with the ‘next’ and ‘previous’ month options, they didn’t turn out to be too useful (to my mind).

#3: Don’t use custom CSS classes until you have the UI where you want it. The default classes look A LOT better at first, so just leave it there in the beginning.

Now for the codes samples. This is from a Lighthouse integration that I did recently, which was awesome and I’ll cover the Lighthouse part in a separate post.

the controller - notice how I pull the year/month out of the parameters, this is how I use the same page for everything.

Code:

def index
    @year = params[:year] ? params[:year].to_i : Time.now.year
    @month = params[:month] ? params[:month].to_i : Time.now.month
    @page_title = "Lighthouse Calendar"
    if @project
      #calculate i differential
      month_now = Time.now.month
      year_now = Time.now.year
      i = ((@year - year_now)*12)+ (@month - month_now).abs
      if i == 1
        @tickets = @project.tickets(:q => "created:'before #{Time.now.day} day#{Time.now.day>1 ? 's' : ''} ago'")
      elsif i > 1
        @tickets = @project.tickets(:q => "created:'before #{i-1} months ago'")
      else
        @tickets = @project.tickets(:q => "created:'since 1 month ago'")
      end
      @milestones = @project.milestones(:q => "due_on:'#{@month}/#{@year}'")
    else
      false
    end
  end

the view:

Code:

<h3>Dev Calendar <%=@year%></h3>
 
<table cellpadding=4>
  <tr>
  <td>
  <% if @month == 1 %>
    <%= link_to "previous", :controller => "pcalendar", :month => 12, :year => @year.to_i-1 %>
  
  <% else %>
    <%= link_to "previous", :controller => "pcalendar", :month => @month.to_i-1, :year => @year %>
    <% end %>
  </td>
  <td>
<%=calendar(:year => @year, :month => @month) do |d|
    x = match_day(d, @tickets)
    milestone_match = match_milestone_day(d, @milestones)
    if x.size > 1 && (milestone_match>0)
      ["#{d.mday} <small>#{link_to_remote(image_tag('icon/star.png', :border=>0),
         { :url => {
          :controller => 'pcalendar',
          :action => 'day_info',
          :day => d.mday,
          :month => @month,
          :year => @year
             },
          :complete => visual_effect(:appear, "pcalendar_info", :duration => 1.4),
          :html => { :title => x.to_s,
          :name => x.to_s },
          :update => "pcalendar_info"
          })}</small>"]    
    elsif x.size > 1 && !(milestone_match>0)
      ["#{d.mday} <small>#{  link_to_remote("x",
         { :url => {
          :controller => 'pcalendar',
          :action => 'day_info',
          :day => d.mday,
          :month => @month,
          :year => @year
             },
          :complete => visual_effect(:appear, "pcalendar_info", :duration => 1.4),
          :html => { :title => x.to_s,
          :name => x.to_s },
          :update => "pcalendar_info"
          })}</small>"]
  elsif (milestone_match>0)
      ["#{d.mday} <small>#{  link_to_remote(image_tag('icon/star.png', :border=>0),
         { :url => {
          :controller => 'pcalendar',
          :action => 'day_info',
          :day => d.mday,
          :month => @month,
          :year => @year
             },
          :complete => visual_effect(:appear, "pcalendar_info", :duration => 1.4),
          :html => { :title => "milestone",
          :name => "milestone" },
          :update => "pcalendar_info"
          })}</small>"]
    else
      [d.mday]
    end
end %></td>
  <td>
  <% if @month == 12 %>
    <%= link_to "next", :controller => "pcalendar", :month => 1, :year => @year.to_i+1 %>
  <% else %>
    <%= link_to "next", :controller => "pcalendar", :month => @month.to_i+1, :year => @year %>
  <% end %>
  </td>
</tr></table>
 
<div id="pcalendar_info" style="display:none">
  
</div>

There it is. I especially love that you can pass the calendar method a block, and that each day iterates as a date object. Sweet. Remember that for some reason, the day (number) of a date object, as far as I can tell, is [date].mday, as opposed to .day, which is how it works off of a Time object. Go figure.

10/29/08

Ruby export to excel

Permalink 01:16:00 pm, Categories: Ruby, 330 words

Just write the headers, pull up the xml by report_id, and slurp it out. Piece of cake.

Code:

def export
      headers['Content-Type'] = "application/vnd.ms-excel"
      headers['Content-Disposition'] = "attachment; filename='report.xls'"
      headers['Cache-Control'] = ''
      s = UserReport.find(params[:report_id])
      col2 = s.col2
      @records = XmlSimple.xml_in(s.xml_val)
      movedir = "public/delivery/"
      x_axis = "Timeline"
      y_axis = @records["yAxisName"] ? @records["yAxisName"] : "$$$"
      f_name = s.file_name
      File.open(f_name, 'w') do |f|
        if current_user.is_test?
          f.puts "<table><tr><td colspan=2><b>Test Data</b></td></tr>"
        else  
          f.puts "<table><tr><td colspan=2><b>#{s.caption}</b></td></tr>"
        end
        f.puts "<table><tr><td colspan=2><b>#{s.date_range}</b></td></tr>"
        if s.sub_caption then f.puts "<table><tr><td colspan=2><b>#{s.sub_caption}</b></td></tr>" end
        f.puts "<tr><td align='right'><b>#{s.col1}</b></td><td align='right'><b>#{s.col2}</b></td></tr>"
        @records["set"].each do |x|
          f.puts "<tr><td>#{clean_col(x["label"])}</td><td>#{parse_report_value(x["value"], col2)}</td></tr>"
        end
        f.puts "<tr><td colspan=2>#{s.filter_info}</td></tr>"
        f.puts "<tr><td colspan=2></td></tr>"
        f.puts "</table>"
      end
      small_export ="delivery/#{f_name}"
      export_link = "#{movedir}/#{f_name}"
      FileUtils.mkpath(movedir)
      FileUtils.mv f_name, export_link
      small_export
    end

cross browser hr tag

Permalink 01:13:04 pm, Categories: Ruby, 14 words

Code:

<hr style="color:#999;background-color:#999;height:3px;border:none;width:761px;" />

works everywhere!

:: Next Page >>

codeboxer.com

Krister Axel is a Ruby on Rails programmer working and living in Santa Monica. Codeboxer.com has answers to questions about coding for Ruby on Rails, SQL and web application development.

:: Next Page >>

Search

Categories

Linkblog

contributors

games

other

python

ruby

javascript

Who's Online?

  • Guest Users: 1

Account

Syndicate this blog

powered by
B2/Evolution

XHTML || RSS || Atom :: ©2007-08 krister axel