Never been to DZone Snippets before?

Snippets is a public source code repository. Easily build up your personal collection of code snippets, categorize them with tags / keywords, and share them with the world

« Newer Snippets
Older Snippets »
Showing 1-10 of 18 total  RSS 

week_view_helper

// Week view helper.

require 'date'

# Author: Josh Adams
# This helper is based on CalendarHelper.
# WeekViewHelper allows you to draw a databound week view calendar with fine-grained CSS formatting
module WeekViewHelper
  VERSION = '0.0.1'

  # Returns an HTML week-view calendar. In its simplest form, this method generates a plain
  # calendar (which can then be customized using CSS) for a given span of days.
  # However, this may be customized in a variety of ways -- changing the default CSS
  # classes, generating the individual day entries yourself, and so on.
  # 
  # The following options are required:
  #  :start_date
  #  :end_date
  # 
  # The following are optional, available for customizing the default behaviour:
  #   :table_class       => "week-view"        # The class for the <table> tag.
  #   :day_name_class    => "dayName"         # The class is for the names of the days, at the top.
  #   :day_class         => "day"             # The class for the individual day number cells.
  #                                             This may or may not be used if you specify a block (see below).
  #   :show_today        => true              # Highlights today on the calendar using the CSS class 'today'. 
  #                                           # Defaults to true.
  #   :previous_span_text   => nil            # Displayed left if set
  #   :next_span_text   => nil                # Displayed right if set
  #
  # For more customization, you can pass a code block to this method, that will get two argument, both DateTime objects,
  # and return a values for the individual table cells. The block can return an array, [cell_text, cell_attrs],
  # cell_text being the text that is displayed and cell_attrs a hash containing the attributes for the <td> tag
  # (this can be used to change the <td>'s class for customization with CSS).
  # This block can also return the cell_text only, in which case the <td>'s class defaults to the value given in
  # +:day_class+. If the block returns nil, the default options are used.
  # 
  # Example usage:
  #   week_view(:start_date => (Date.today - 5), :end_date => Date.today) # This generates the simplest possible week-view.
  #   week_view(:start_date => (Date.today - 5), :end_date => Date.today, :table_class => "calendar_helper"}) # This generates a week-view, as
  #                                                                             # before, but the <table>'s class
  #                                                                             # is set to "calendar_helper".
  #   week_view(:start_date => (Date.today - 5), :end_date => Date.today) do |s| # This generates a simple week-view, but gives special spans
  #     if listOfSpecialSpans.include?(s)          # (spans that are in the array listOfSpecialSpans) one CSS class,
  #       ["", {:class => "specialSpan"}]      # "specialSpan", and gives the rest of the spans another CSS class,
  #     else                                      # "normalSpan". You can also use this to highlight the current time differently
  #       ["", {:class => "normalSpan"}]       # from the rest of the days, etc.
  #     end
  #   end
  #
  # For consistency with the themes provided in the calendar_styles generator, use "specialSpan" as the CSS class for marked days.
  # 
  def week_view(options = {}, &block)
    raise(ArgumentError, "No start date given")  unless options.has_key?(:start_date)
    raise(ArgumentError, "No end date given") unless options.has_key?(:end_date)
    span = (options[:end_date] - options[:start_date]).to_i # Get the number of days represented by the span given
    dates = (options[:start_date]..options[:end_date])
    start_time = 8
    end_time   = 17
    time_range = (start_time..end_time).to_a
    duration = 15

    block                        ||= Proc.new {|d| nil}
    defaults = {
      :table_class => 'week-view',
      :day_name_class => 'dayName',
      :day_class => 'day',
      :show_today => true,
      :previous_span_text => nil,
      :next_span_text => nil
    }
    options = defaults.merge options

		if options[:url]
      next_start_date = options[:end_date] + 1
      next_end_date   = next_start_date + 5
      next_link = link_to('>>', url_for(options[:url].merge(:start_date => next_start_date, :end_date => next_end_date)) + options[:url_append])
      prev_start_date = options[:start_date] - span
      prev_end_date = options[:start_date] - 1
      prev_link = link_to('<<', url_for(options[:url].merge(:start_date => prev_start_date, :end_date => prev_end_date)) + options[:url_append])
		end

    # TODO Use some kind of builder instead of straight HTML
    cal = %(<table class="#{options[:table_class]}">\n)
    cal << %(\t<thead>\n\t\t<tr>\n)
    cal << %(\t\t\t<th>#{dates.first.strftime("%Y")}</th>\n)
    dates.each do |d|
      cal << "\t\t\t<th#{Date.today == d ? " class='today'" : ""}>#{d.strftime("%A")}<br />#{d.strftime("%m/%d")}</th>\n"
    end
    cal << "\t\t</tr>\n\t</thead>\n\t<tbody>\n"
    time_range.each do |hour|
      minutes = 0
      print_hour = hour.to_s.rjust(2, '0')
      4.times do |i|
        print_minutes = minutes.to_s.rjust(2, '0')
        cal << %(\t\t<tr class='m#{print_minutes} d#{duration}'>\n)
        cal << %(\t\t\t<th rowspan="4"><h3>#{hour}</h3>AM</th>\n) if i==0
        options[:start_date].upto(options[:end_date]) do |d|
          the_minutes = minutes
          print_start_minutes = the_minutes.to_s.ljust(2, '0')
          start_datetime_string = %(#{d.to_s(:db)}T#{print_hour}:#{print_start_minutes}:00-06:00)
          start_datetime = DateTime.parse(start_datetime_string).to_datetime
          end_datetime = (start_datetime + duration.minutes).to_datetime
          range = (start_datetime...end_datetime)

          # cell_attrs should return a hash.
          cell_text, cell_attrs = block.call(range)
          cell_text ||= ""
          cell_attrs ||= {}
          cell_attrs[:class] = cell_attrs[:class].to_s + " today" if Date.today == d
          cell_attrs = cell_attrs.map {|k, v| %(#{k}="#{v}") }.join(" ")

          cal << "\t\t\t<td #{cell_attrs}>\n#{cell_text}&nbsp;\t\t\t</td>\n"
        end
        minutes += duration
        cal << %(\t\t</tr>)
      end
    end
    cal << "\n\t</tbody>\n</table>"
  end
  
  private
end



// Usage

<%= week_view :start_date => (Date.today - 2), :end_date => (Date.today + 2) do |range|
      cell_text = ""
      cell_attrs = {}
      @events.each do |e|
        #if range.first.to_date == e.starts_at.to_date#range.include?(e.starts_at.to_datetime)
        if range.include?(e.starts_at.to_datetime)
          cell_text << "<div class='event-container'>\n"
          if e.work_order
            work_order_text = ""
            work_order_text << "<h3>" << e.work_order.ticket.summary + "</h3>\n"
            work_order_text << e.work_order.description

            klass = ["event"]
            klass << "closed" if e.work_order.closed?
            klass << "mm" + (e.work_order.hours * 60).to_i.to_s

            work_order_text = "<div class='#{klass.join(" ")}'>\n#{work_order_text}\n</div>\n"
            work_order_link = link_to(work_order_text, company_asset_ticket_work_order_path(e.work_order.company, e.work_order.asset, e.work_order.ticket, e.work_order))
            cell_text << work_order_link
          end
          cell_text << "</div>\n"
        end
      end
      [cell_text, cell_attrs]
    end 
-%>


// Styles

table.week-view{
  width: 100%;
  padding: 0;
  border-collapse: collapse;
}
table.week-view tbody tr th{
  border-right: 1px solid black;
  text-align: left;
  padding-right: .5em;
}
table.week-view tbody tr th h3{
  display: inline;
  font-size: 3em;
  margin: 0;
  padding: 0;
  background: transparent;
  border: none;
}
table.week-view th{
  background-color: #eee;
  color: black;
}
table.week-view tr td{
  border-style: solid;
  border-width: 1px 1px 0 0;
  width: 19%;
  padding: 0;
  background-color: #ffffd5;
}
table.week-view tr.d15 td,
table.week-view tr.d15 th{
  font-size: 8px;
  height: 1em;
  border-color: #f3e4b1 #000;
}
table.week-view div.event-container{
  position: relative;
}
table.week-view tr td div.event{
  padding: 2px;
  background-color: #fff;
  height: 2em;
  width: 90%;
  position: absolute;
  top: 0;
  left: 0;
  border-style: solid;
  border-color: black black black #00f;
  border-width: .0625em .0625em .0625em .5em;
  margin-right: .5em;
  color: #333;
  overflow: hidden;
  float: left;
}
table.week-view tr td div.event.mm60{
  height: 8em;
}
table.week-view tr td div.event.mm120{
  height: 16em;
}
table.week-view tr td div.event h3{
  margin: 0;
  font-size: 9px;
  color: black;
  padding: 0;
  border: 0;
  background: transparent;
}
table.week-view tr.m00 td{
  border-top-color: #c2b68d;
}
table.week-view tr.m00 th{
  vertical-align: top;
}

Initial caps with ruby on rails

class String
  # unicode_str.initial_caps => new_str
  # returns a copy of a string with initial capitals
  # "Jules-Édouard".initial_caps => "J.É."
  def initial_caps
    self.tr('-', ' ').split(' ').map { |word| word.chars.first.upcase.to_s + "." }.join
  end
end

Rails helper: display flash hash

Rails Helper

<code rails>
def print_flash
msg = ''
flash.each_pair do |type, message|
text = content_tag(:strong, type.to_s.capitalize) << ": " << message
msg << content_tag(:p, text, :class => "flash #{type}")
end
msg
end

Array to HTL list helper

Makes a HTML list out of any array. The objects in the array is yielded to the block. Usage:


<%= list(@posts) { |post| link_to post.title, posts_url(post) } %>


And the actual helper (put it in application_helper.rb):

def list(stuff, &block)
  content_tag(:ul, stuff.map { |thingie| content_tag(:li, yield(thingie)) })
end

open_body_tag

  #creates a body tag uniquely indentifying this page
  #takes an options Hash with two keys:
  #
  #<tt>id</tt>::        string that will be used as the body's ID. defaults to <tt>controller.controller_name.singularize</tt>
  #<tt>classes</tt>::   an Array of class names. defaults to <tt>[params[:action]]</tt>
  #
  #Examples:
  #
  # in HomeController#index:
  #
  # <%= open_body_tag %>
  # => <body id='home' class='index'>
  #
  # <%= open_body_tag(:id => 'foo') %>
  # => <body id='foo' class='index'>
  #
  # <%= open_body_tag(:id => 'foo', :classes => %w(one two)) %>
  # => <body id='foo' class='one two'>
  def open_body_tag(options = { :id => controller.controller_name.singularize, :classes => [params[:action]] })
    "<body id='#{options[:id]}' class='#{options[:classes].join(' ')}'>"
  end

Helper for testing default routes generated by a resource in Ruby on Rails

These methods test that the routes for resources defined in routes.rb are working as expected. Call them from your functional (controller) tests.

Add the following 3 methods to test/test_helper.rb (updated for Rails 1.2.5 which no longer uses semicolons as a separator for the edit action):
# Test for routes generated by map.resource (singular).
def assert_routing_for_resource(controller, skip=[], nesting=[])
  routes = [
    ["new",'/new',{},:get], ["create",'',{},:post],
    ["show",'',{},:get], ["edit",'/edit',{},:get],
    ["update",'',{},:put], ["destroy",'',{},:delete]
    ]
  check_resource_routing(controller, routes, skip, nesting)
end
# Test for routes generated by map.resources (plural).
def assert_routing_for_resources(controller, skip=[], nesting=[])
  routes = [
    ["index",'',{},:get], ["new",'/new',{},:get], ["create",'',{},:post],
    ["show",'/1',{:id=>'1'},:get], ["edit",'/1/edit',{:id=>'1'},:get],
    ["update",'/1',{:id=>'1'},:put], ["destroy",'/1',{:id=>'1'},:delete]
    ]
  check_resource_routing(controller, routes, skip, nesting)
end

# Check that the expected paths will be generated by a resource, and that
# the expected params will be generated by paths defined by a resource.
# routes is array of [action, url string after controller, extra params].
def check_resource_routing(controller, routes, skip=[], nesting=[])
  # set a prefix for nested resources
  prefix = nesting.join('s/1/')
  unless prefix.blank?
    prefix += "s/1/"
  end
  # Add params for nested resources.
  # For each 'nest', include a ":nest_id=>'1'" param.
  params = {}
  nesting.each do |param|
    params["#{param}_id".to_sym] = '1'
  end
  # Test each of the standard resource routes.
  routes.each do |pair|
    unless skip.include? pair[0]
      assert_generates("/#{prefix}#{controller}#{pair[1]}",
        {:controller=>controller,
        :action=>pair[0]}.merge(pair[2]).merge(params), {}, {},
        "Failed generation of resource route for action #{pair[0]} /#{prefix}#{controller}#{pair[1]}")
      assert_recognizes(
        {:controller=>controller,
          :action=>pair[0]}.merge(pair[2]).merge(params),
        {:path=>"/#{prefix}#{controller}#{pair[1]}", :method=>pair[3]},
        {}, "Failed to recognize resource route for path #{pair[3]}:/#{prefix}#{controller}#{pair[1]}")
    end
  end
end

EXAMPLES

You can specify actions to 'skip' (if you have a special route for that action).
If using nested resources, set the nesting array (use singular strings).

So, if you have the following in routes.rb:
map.make_thing '/make', :controller=>'things', :action=>'new'
map.resources :nests do |nest|
  nest.resources :things
end
map.resource :foo

then you can use the following in things_controller_test.rb:
def test_resource_routing
  assert_routing_for_resources 'things', ['new'], ['nest']
  assert_routing_for_resource 'foo'
end

remote image submit tag

// do a remote_submit_tag but instade of simple input it makes a input type image

def remote_image_submit_tag(source,options)
    options[:with] ||= 'Form.serialize(this.form)'
    
    options[:html] ||= {}
    options[:html][:type] = 'image'
    options[:html][:onclick] = "#{remote_function(options)}; return false;"
    options[:html][:src] = image_path(source)
    
    tag("input", options[:html], false)
  end

Helper for quicly creating standard tables

I often want to display an array of objects as a table on a page, and I end up doing the the same things over and over again. I wrote this helper method to speed things up:

def table(collection, headers, options = {}, &proc)
  options.reverse_merge!({
    :placeholder  => 'Nothing to display',
    :caption      => nil,
    :summary      => nil,
    :footer       => ''
  })
  placeholder_unless collection.any?, options[:placeholder] do
    summary = options[:summary] || "A list of #{collection.first.class.to_s.pluralize}"
    output = "<table summary=\"#{summary}\">\n"
    output << content_tag('caption', options[:caption]) if options[:caption]
    output << "\t<caption>#{options[:caption]}</caption>\n" if options[:caption]
    output << content_tag('thead', content_tag('tr', headers.collect { |h| "\n\t" + content_tag('th', h) }))
    output << "<tfoot><tr>" + content_tag('th', options[:footer], :colspan => headers.size) + "</tr></tfoot>\n" if options[:footer]
    output << "<tbody>\n"
    concat(output, proc.binding)
    collection.each do |row|
      proc.call(row, cycle('odd', 'even'))
    end
    concat("</tbody>\n</table>\n", proc.binding)
  end
end


Writing...

<% table(@posts, %w{ID title}) do |post, klass| -%>
    <tr class="<%= klass %>">
      <td><%= post.id</td>
      <td><%= post.title </td>
    </tr>
<% end -%>


results in...

<table summary="A list of posts">
  <thead>
    <tr>
      <th>ID</th>
      <th>Title</th>
    </tr>
  </thead>
  <tfoot><tr><td colspan="2"></td></tr></tfoot>
  <tbody>
    <tr>
      <td>1</td>
      <td>My first post</td>
    </tr>
  </tbody>
</table>


Or, when the collection is an empty array (collection.any? returns false), a placeholder message is displayed:

<p class="placeholder">Nothing to display</p>


So you pass in your collection and an array of strings as your table headers as the first two arguments, and the third is a hash of options you can use to set the contents of the table's summary-attribute, the caption and footer-elements and the placeholder.

The summary attribute defaults to "A list of [objects]", where 'objects' is derived from the class name of the collection.

The function finally takes a block for every element in the collection, yeilding it the element and either 'odd' or 'even' so you can use CSS-classes to apply a zebra stripes effect.

Formatting a date with or without the time

Ruby on Rails

This helper returns the date (and possibly the time) formatted the way you want

def format_date(date, use_time = false)
    if use_time == true
        ampm = date.strftime("%p").downcase
        new_date = date.strftime("%B %d, %Y at %I:%M" + ampm)
    else
        new_date = date.strftime("%B %d, %Y")
    end
end

Helpers for placeholder messages

When there are no results to display for a user you often want to tell so using a placeholder message, such as 'Sorry, no posts matched your criteria' or 'You have no recent purchases'.

I created these two helpers to make using placeholder a little bit easier.

def placeholder(message = 'Nothing to display', options = {}, &proc)
  # set default options
  o = { :class => 'placeholder', :tag => 'p' }.merge(options)

  # wrap the results of the supplied block, or
  # just print out the message
  if proc
    t = o.delete(:tag)
    concat tag(t, o, true), proc.binding
    yield
    concat "</#{t}>", proc.binding
  else
    content_tag o.delete(:tag), message, o
  end
end
  
def placeholder_unless(condition, *args, &proc)
  condition ? proc.call : concat(placeholder(args), proc.binding)
end


Now you can use it as follows:

<%= placeholder :message => 'Nothing found' %>

<% placeholder do %>
Nothing found.
<% end %>


Results in:

<p class="placeholder">Nothing found</p>


The second function allows the following usage:

<% placeholder_unless @posts.any?, 'No posts found' do %>
  <%= render :partial => @post %>
<% end %>


The point is it all results in a nice, painless message with consistent markup and styling and I find it makes my code a bit more readable.

I'm sure the code could be optimized but for now it works.
« Newer Snippets
Older Snippets »
Showing 1-10 of 18 total  RSS