├── .yardopts ├── init.rb ├── lib ├── table_builder │ ├── version.rb │ ├── table_builder.rb │ └── calendar_helper.rb └── table_builder.rb ├── .gitignore ├── Gemfile ├── .travis.yml ├── Rakefile ├── .autotest ├── test ├── test_helper.rb ├── table_builder_test.rb └── calendar_helper_test.rb ├── LICENSE ├── table_builder.gemspec └── README.md /.yardopts: -------------------------------------------------------------------------------- 1 | --markup markdown lib/**/*.rb -------------------------------------------------------------------------------- /init.rb: -------------------------------------------------------------------------------- 1 | require 'table_builder' 2 | -------------------------------------------------------------------------------- /lib/table_builder/version.rb: -------------------------------------------------------------------------------- 1 | module TableBuilder 2 | VERSION = "0.3.0" 3 | end 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | pkg 3 | .idea 4 | *.gem 5 | .bundle 6 | Gemfile.lock 7 | pkg/* 8 | .yardoc 9 | doc 10 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "http://rubygems.org" 2 | 3 | # Specify your gem's dependencies in table_builder.gemspec 4 | gemspec 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | rvm: 3 | - 1.9.3 4 | # uncomment this line if your project needs to run something other than `rake`: 5 | # script: bundle exec rspec spec -------------------------------------------------------------------------------- /lib/table_builder.rb: -------------------------------------------------------------------------------- 1 | require "table_builder/table_builder.rb" 2 | require "table_builder/calendar_helper.rb" 3 | require "table_builder/version.rb" 4 | 5 | ActionView::Base.send :include, TableHelper 6 | ActionView::Base.send :include, CalendarHelper -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "rake" 2 | require "rake/testtask" 3 | require "bundler/gem_tasks" 4 | 5 | desc "Default: run unit tests." 6 | task :default => :test 7 | 8 | desc "Test the table_builder plugin." 9 | Rake::TestTask.new(:test) do |t| 10 | t.libs << "lib" 11 | t.pattern = "test/**/*_test.rb" 12 | t.verbose = true 13 | end 14 | -------------------------------------------------------------------------------- /.autotest: -------------------------------------------------------------------------------- 1 | Autotest.add_hook :initialize do |at| 2 | at.clear_mappings 3 | at.add_mapping(/^lib\/.*\.rb$/) do |filename, _| 4 | possible = File.basename(filename).gsub '_', '_?' 5 | at.files_matching %r%^test/.*#{possible}$% 6 | end 7 | 8 | at.add_mapping(/^test\/.*\_test.rb/) do |f, _| 9 | at.files_matching(/^test\/.*_test.rb$/) 10 | end 11 | 12 | end -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | require 'test/unit' 2 | 3 | require "rubygems" 4 | require 'active_support' 5 | require 'action_pack' 6 | require 'action_controller' 7 | require 'action_view' 8 | require 'action_controller' 9 | require 'action_view' 10 | require 'action_view/base' 11 | require 'action_view/template' 12 | require 'action_view/test_case' 13 | 14 | require(File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'table_builder'))) 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2008 Petrik de Heus 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /table_builder.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | 3 | $:.push File.expand_path("../lib", __FILE__) 4 | require "table_builder/version" 5 | 6 | Gem::Specification.new do |s| 7 | s.name = "watu_table_builder" 8 | s.version = TableBuilder::VERSION 9 | s.authors = ["Petrik de Heus", "Andrew C. Greenberg", "Jason Cheong-Kee-You", "J. Pablo Fernández"] 10 | s.email = ["pupeno@watuhq.com"] 11 | s.homepage = "https://github.com/watu/table_builder" 12 | s.summary = %q{Rails builder for creating tables and calendars inspired by ActionView's FormBuilder.} 13 | s.description = %q{Rails builder for creating tables and calendars inspired by ActionView's FormBuilder.} 14 | s.license = "MIT" 15 | 16 | s.rubyforge_project = "table_builder" 17 | 18 | s.files = Dir["{lib,test}/**/*", "[A-Z]*", "init.rb"] - ["Gemfile.lock"] 19 | s.test_files = Dir["test/**/*"] 20 | s.require_paths = ["lib"] 21 | 22 | # specify any dependencies here; for example: 23 | # s.add_development_dependency "rspec" 24 | # s.add_runtime_dependency "rest-client" 25 | s.add_development_dependency "rake" 26 | s.add_development_dependency "test-unit" 27 | s.add_development_dependency "activesupport" 28 | s.add_development_dependency "actionpack" 29 | end 30 | -------------------------------------------------------------------------------- /lib/table_builder/table_builder.rb: -------------------------------------------------------------------------------- 1 | module TableHelper 2 | 3 | def table_for(objects, *args) 4 | raise ArgumentError, "Missing block" unless block_given? 5 | options = args.last.is_a?(Hash) ? args.pop : {} 6 | html_options = options[:html] 7 | builder = options[:builder] || TableBuilder 8 | 9 | content_tag(:table, html_options) do 10 | yield builder.new(objects || [], self, options) 11 | end 12 | end 13 | 14 | class TableBuilder 15 | include ::ActionView::Helpers::TagHelper 16 | 17 | def initialize(objects, template, options) 18 | raise ArgumentError, "TableBuilder expects an Enumerable object but found #{objects.inspect}" unless objects.respond_to? :each 19 | @objects, @template, @options = objects, template, options 20 | end 21 | 22 | def head(*args) 23 | if block_given? 24 | concat(tag(:thead, options_from_hash(args), true)) 25 | yield 26 | concat('') 27 | else 28 | @num_of_columns = args.size 29 | content_tag(:thead, 30 | content_tag(:tr, 31 | args.collect { |c| content_tag(:th, c.html_safe)}.join('').html_safe 32 | ) 33 | ) 34 | end 35 | end 36 | 37 | def head_r(*args) 38 | raise ArgumentError, "Missing block" unless block_given? 39 | options = options_from_hash(args) 40 | head do 41 | concat(tag(:tr, options, true)) 42 | yield 43 | concat('') 44 | end 45 | end 46 | 47 | def body(*args) 48 | raise ArgumentError, "Missing block" unless block_given? 49 | options = options_from_hash(args) 50 | tbody do 51 | @objects.each { |c| yield(c) } 52 | end 53 | end 54 | 55 | def body_r(*args) 56 | raise ArgumentError, "Missing block" unless block_given? 57 | options = options_from_hash(args) 58 | tbody do 59 | @objects.each { |c| 60 | concat(tag(:tr, options, true)) 61 | yield(c) 62 | concat(''.html_safe) 63 | } 64 | end 65 | end 66 | 67 | def r(*args) 68 | raise ArgumentError, "Missing block" unless block_given? 69 | options = options_from_hash(args) 70 | tr(options) do 71 | yield 72 | end 73 | end 74 | 75 | def h(*args) 76 | if block_given? 77 | concat(tag(:th, options_from_hash(args), true)) 78 | yield 79 | concat('') 80 | else 81 | content = args.shift 82 | content_tag(:th, content, options_from_hash(args)) 83 | end 84 | end 85 | 86 | def d(*args) 87 | if block_given? 88 | concat(tag(:td, options_from_hash(args), true)) 89 | yield 90 | concat('') 91 | else 92 | content = args.shift 93 | content_tag(:td, content, options_from_hash(args)) 94 | end 95 | end 96 | 97 | 98 | private 99 | 100 | def options_from_hash(args) 101 | args.last.is_a?(Hash) ? args.pop : {} 102 | end 103 | 104 | def concat(tag) 105 | @template.safe_concat(tag) 106 | "" 107 | end 108 | 109 | def content_tag(tag, content, *args) 110 | options = options_from_hash(args) 111 | @template.content_tag(tag, content, options) 112 | end 113 | 114 | def tbody 115 | concat('') 116 | yield 117 | concat('') 118 | end 119 | 120 | def tr options 121 | concat(tag(:tr, options, true)) 122 | yield 123 | concat('') 124 | end 125 | end 126 | end 127 | -------------------------------------------------------------------------------- /test/table_builder_test.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path(File.dirname(__FILE__) + '/test_helper.rb') 2 | 3 | class TableBuilderTest < ActionView::TestCase 4 | include ActionView::Helpers::TextHelper 5 | include ActionView::Helpers::TagHelper 6 | include TableHelper 7 | attr_accessor :output_buffer 8 | 9 | def setup 10 | @drummer1 = Drummer.new(1, 'John "Stumpy" Pepys') 11 | @drummer2 = Drummer.new(2, 'Eric "Stumpy Joe" Childs') 12 | @drummer3 = Drummer.new(3, 'Peter "James" Bond') 13 | @drummer4 = Drummer.new(4, 'Mick Shrimpton (R. J. "Ric" Parnell)') 14 | end 15 | 16 | def test_table_for 17 | output = table_for([], :html => { :id => 'id', :style => 'style', :class => 'class'}) do |t| 18 | end 19 | expected = %() << 20 | %(
) 21 | assert_dom_equal expected, output 22 | end 23 | 24 | def test_table_for_without_an_array_raises_error 25 | assert_raises(ArgumentError) do 26 | table_for('a') {|t| } 27 | end 28 | end 29 | 30 | def test_head 31 | output = table_for([]) do |t| 32 | t.head do 33 | t.r do 34 | output_buffer.concat t.h('Id') 35 | output_buffer.concat t.h('Name') 36 | end 37 | end 38 | end 39 | expected = %() << 40 | %() << 41 | %() << 42 | %() << 43 | %() << 44 | %() << 45 | %() << 46 | %(
IdName
) 47 | assert_dom_equal expected, output 48 | end 49 | 50 | def test_head_r 51 | output = table_for([]) do |t| 52 | t.head_r do 53 | output_buffer.concat t.h('Id') 54 | output_buffer.concat t.h('Name') 55 | end 56 | end 57 | expected = %() << 58 | %() << 59 | %() << 60 | %() << 61 | %() << 62 | %() << 63 | %() << 64 | %(
IdName
) 65 | assert_dom_equal expected, output 66 | end 67 | 68 | def test_head_with_array 69 | output = table_for([@drummer1, @drummer2]) do |t| 70 | concat t.head('Id', 'Name') 71 | end 72 | expected = %() << 73 | %() << 74 | %() << 75 | %() << 76 | %() << 77 | %() << 78 | %() << 79 | %(
IdName
) 80 | assert_dom_equal expected, output 81 | end 82 | 83 | def test_body 84 | output = table_for([@drummer3, @drummer4]) do |t| 85 | t.body do |e| 86 | t.r do 87 | concat t.d(e.id) 88 | concat t.d(e.name) 89 | end 90 | end 91 | end 92 | expected = %() << 93 | %() << 94 | %() << 95 | %() << 96 | %() << 97 | %() << 98 | %() << 99 | %() << 100 | %() << 101 | %() << 102 | %() << 103 | %(
3Peter "James" Bond
4Mick Shrimpton (R. J. "Ric" Parnell)
) 104 | assert_dom_equal expected, output 105 | end 106 | 107 | def test_body_r 108 | output = table_for([@drummer3, @drummer4]) do |t| 109 | t.body_r do |e| 110 | concat t.d(e.id) 111 | concat t.d(e.name) 112 | end 113 | end 114 | expected = %() << 115 | %() << 116 | %() << 117 | %() << 118 | %() << 119 | %() << 120 | %() << 121 | %() << 122 | %() << 123 | %() << 124 | %() << 125 | %(
3Peter "James" Bond
4Mick Shrimpton (R. J. "Ric" Parnell)
) 126 | assert_dom_equal expected, output 127 | end 128 | 129 | def test_td_with_options 130 | output = table_for([@drummer1]) do |t| 131 | t.body_r do |e| 132 | output_buffer.concat t.d(e.name, :class => 'class') 133 | end 134 | end 135 | expected = %() << 136 | %() << 137 | %() << 138 | %() << 139 | %() << 140 | %() << 141 | %(
John "Stumpy" Pepys
) 142 | assert_dom_equal expected, output 143 | end 144 | 145 | def test_td_with_block 146 | output = table_for([@drummer1]) do |t| 147 | t.body_r do |e| 148 | t.d do 149 | concat 'content' 150 | end 151 | end 152 | end 153 | expected = %() << 154 | %() << 155 | %() << 156 | %() << 157 | %() << 158 | %() << 159 | %(
content
) 160 | assert_dom_equal expected, output 161 | end 162 | 163 | def test_td_with_block_and_options 164 | output = table_for([@drummer1]) do |t| 165 | t.body_r do |e| 166 | t.d(:class => 'class') do 167 | concat 'content' 168 | end 169 | end 170 | end 171 | expected = %() << 172 | %() << 173 | %() << 174 | %() << 175 | %() << 176 | %() << 177 | %(
content
) 178 | assert_dom_equal expected, output 179 | end 180 | 181 | end 182 | 183 | class Drummer < Struct.new(:id, :name); end 184 | -------------------------------------------------------------------------------- /lib/table_builder/calendar_helper.rb: -------------------------------------------------------------------------------- 1 | module CalendarHelper 2 | # Generates a calendar (as a table) for an array of objects placing each of them on the corresponding date. 3 | # 4 | # **TODO: fully document this method, the current documentation is far from done.** 5 | # 6 | # @param [Hash] options extra options 7 | # 8 | # :row_header if true, each row will have an extra cell at the beginning, as a row header. A typical usage would be 9 | # to output week numbers. When the block is called, it will get the date that would normally be passed to the 10 | # first day of the week (to give you some context) and a nil list of objects (and that's how you recognize it as 11 | # a header, because empty days get an empty array, not nil). 12 | def calendar_for(objects, *args) 13 | raise ArgumentError, "Missing block" unless block_given? 14 | options = args.last.is_a?(Hash) ? args.pop : {} 15 | html_options = options[:html] 16 | builder = options[:builder] || CalendarBuilder 17 | calendar = options[:calendar] || Calendar 18 | content_tag(:table, nil, html_options) do 19 | yield builder.new(objects || [], self, calendar, options) 20 | end 21 | end 22 | 23 | class CalendarBuilder < TableHelper::TableBuilder 24 | def initialize(objects, template, calendar, options) 25 | super(objects, template, options) 26 | @calendar = calendar.new(options) 27 | @today = options[:today] || Time.now 28 | @row_header = options[:row_header] || false 29 | end 30 | 31 | def day(*args) 32 | raise ArgumentError, "Missing block" unless block_given? 33 | options = options_from_hash(args) 34 | day_method = options.delete(:day_method) || :date 35 | id_pattern = options.delete(:id) 36 | tbody do 37 | @calendar.objects_for_days(@objects, day_method).to_a.sort{|a1, a2| a1.first <=> a2.first }.each do |o| 38 | key, array = o 39 | day, objects = array 40 | concat(tag(:tr, options, true)) if(day.wday == @calendar.first_weekday) 41 | if @row_header && day.wday == @calendar.first_weekday 42 | row_header_options = td_options(day, id_pattern) 43 | row_header_options[:class] ||= "" 44 | row_header_options[:class] << " row_header" 45 | concat(tag(:td, row_header_options, true)) 46 | yield(day, nil) 47 | concat("") 48 | end 49 | concat(tag(:td, td_options(day, id_pattern), true)) 50 | yield(day, objects) 51 | concat('') 52 | concat('') if(day.wday == @calendar.last_weekday) 53 | end 54 | end 55 | end 56 | 57 | private 58 | 59 | def objects_for_days 60 | @calendar.objects_for_days(@objects) 61 | end 62 | 63 | def td_options(day, id_pattern) 64 | options = {} 65 | css_classes = [] 66 | css_classes << 'today' if day.strftime("%Y-%m-%d") == @today.strftime("%Y-%m-%d") 67 | css_classes << 'notmonth' if day.month != @calendar.month 68 | css_classes << 'weekend' if day.wday == 0 or day.wday == 6 69 | css_classes << 'future' if day > @today.to_date 70 | options[:class] = css_classes.join(' ') unless css_classes.empty? 71 | options[:id] = day.strftime(id_pattern) if id_pattern 72 | options 73 | end 74 | 75 | end 76 | 77 | class Calendar 78 | attr_accessor :first_weekday, :last_weekday, :month 79 | 80 | # :first lets you set the first day to start the calendar on (default is the first day of the given :month and :year). 81 | # :first => :today will use Date.today 82 | # :last lets you set the last day of the calendar (default is the last day of the given :month and :year). 83 | # :last => :thirty will show 30 days from :first 84 | # :last => :week will show one week 85 | def initialize(options={}) 86 | @year = options[:year] || Time.now.year 87 | @month = options[:month] || Time.now.month 88 | @first_day_of_week = options[:first_day_of_week] || 0 89 | @first_weekday = first_day_of_week(@first_day_of_week) 90 | @last_weekday = last_day_of_week(@first_day_of_week) 91 | 92 | @first = options[:first]==:today ? Date.today : options[:first] || Date.civil(@year, @month, 1) 93 | 94 | if options[:last] == :thirty_days || options[:last] == :thirty 95 | @last = @first + 30 96 | elsif options[:last] == :one_week || options[:last] == :week 97 | @last = @first 98 | else 99 | @last = options[:last] || Date.civil(@year, @month, -1) 100 | end 101 | 102 | end 103 | 104 | def each_day 105 | first_day.upto(last_day) do |day| 106 | yield(day) 107 | end 108 | end 109 | 110 | def last_day 111 | last = @last 112 | while(last.wday % 7 != @last_weekday % 7) 113 | last = last.next 114 | end 115 | last 116 | end 117 | 118 | def first_day 119 | first = @first - 6 120 | while(first.wday % 7 != (@first_weekday) % 7) 121 | first = first.next 122 | end 123 | first 124 | end 125 | 126 | def objects_for_days(objects, day_method) 127 | unless @objects_for_days 128 | @objects_for_days = {} 129 | days.each{|day| @objects_for_days[day.strftime("%Y-%m-%d")] = [day, []]} 130 | objects.each do |o| 131 | date = o.send(day_method.to_sym).strftime("%Y-%m-%d") 132 | if @objects_for_days[date] 133 | @objects_for_days[date][1] << o 134 | end 135 | end 136 | end 137 | @objects_for_days 138 | end 139 | 140 | def days 141 | unless @days 142 | @days = [] 143 | each_day{|day| @days << day} 144 | end 145 | @days 146 | end 147 | 148 | def mjdays 149 | unless @mjdays 150 | @mdays = [] 151 | each_day{|day| @days << day} 152 | end 153 | @days 154 | end 155 | 156 | def first_day_of_week(day) 157 | day 158 | end 159 | 160 | def last_day_of_week(day) 161 | if day > 0 162 | day - 1 163 | else 164 | 6 165 | end 166 | end 167 | end 168 | 169 | end 170 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/watu/table_builder.png?branch=master)](https://travis-ci.org/watu/table_builder) 2 | [![Coverage Status](https://coveralls.io/repos/watu/table_builder/badge.png)](https://coveralls.io/r/watu/table_builder) 3 | [![Code Climate](https://codeclimate.com/github/watu/table_builder.png)](https://codeclimate.com/github/watu/table_builder) 4 | [![Gem Version](https://badge.fury.io/rb/watu_table_builder.png)](http://badge.fury.io/rb/watu_table_builder) 5 | 6 | About watu_table_builder 7 | ======================== 8 | 9 | watu_table_builder is a fork of [table_builder](https://github.com/jchunky/table_builder) in an effort to bring it up to 10 | speed with the current Ruby and Rails practices (bundler, gem, ci) as well as maybe re-vive it, start merging useful 11 | branches, implementing new features, fixing bugs, etc. 12 | 13 | Install 14 | ======= 15 | 16 | Add this to your Gemfile: 17 | 18 | gem "watu_table_builder", :require => "table_builder" 19 | 20 | or if you prefer to use it straight from GitHub: 21 | 22 | gem "watu_table_builder", :require => "table_builder", :git => "git://github.com/watu/table_builder.git" 23 | 24 | TableBuilder 25 | ============ 26 | 27 | Rails builder for creating tables and calendars inspired by ActionView's FormBuilder, updated for Rails 3.0beta 28 | This is a fork of Petrik de Heus plugin for earlier versions of Rails. Note the new idiomatic use of "<%=" for the 29 | table_for and calendar_for functions. 30 | 31 | Examples 32 | ======== 33 | 34 | table_for has methods for each tag used in a table (table, thead, tr, td, etc.) 35 | 36 | A basic example would look like this: 37 | 38 | @front_men = [FrontMan.new(1, 'David St. Hubbins'), FrontMan.new(2, 'David Lee Roth')] 39 | 40 | and 41 | 42 | <%= table_for(@front_men) do |t| %> 43 | <%= t.head do %> 44 | <%= t.r do %> 45 | <%= t.h('Id') %> 46 | <%= t.h('Name') %> 47 | <% end %> 48 | <% end %> 49 | <%= t.body do |front_man| %> 50 | <%= t.r do %> 51 | <%= t.d(h(front_man.id)) %> 52 | <%= t.d(h(front_man.name)) %> 53 | <% end %> 54 | <% end %> 55 | <% end %> 56 | 57 | You can pass an array to the head method: 58 | 59 | <%= t.head('Id', 'Name') %> 60 | 61 | The body and r method can be combined for easier usage: 62 | 63 | <%= t.body_r do |front_man| %> 64 | <%= t.d(h(front_man.id)) %> 65 | <%= t.d(h(front_man.name)) %> 66 | <% end %> 67 | 68 | You can also pass blocks to the d and h methods for more flexibility: 69 | 70 | 71 | <%= t.d(:class => 'name') do %> 72 | <%= link_to(h(front_man.name), front_man_url(front_man)) %> 73 | <% end %> 74 | 75 | All tag methods are rails tag methods, so they can have extra html options. 76 | 77 | @drummers = [Drummer.new(1, 'John "Stumpy" Pepys'), Drummer.new(2, 'Eric "Stumpy Joe" Childs')] 78 | 79 | and 80 | 81 | <%= table_for(@drummers, :html => { :id => 'spinal_tap', :class => 'drummers'}) do |t| %> 82 | <%= t.body_r(:class => 'row') do |e| %> 83 | <%= t.d(h(e.id), :title => 'id') %> 84 | <%= t.d(h(e.name)) %> 85 | <% end %> 86 | <% end %> 87 | 88 | which produces the following html: 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 |
1John "Stumpy" Pepys
2Eric "Stumpy Joe" Childs
102 | 103 | 104 | You can customize the table by creating your own TableBuilder: 105 | 106 | <%= table_for(@drummers, :builder => PagedTableBuilder) do |t| %> 107 | 108 | Calendar Table 109 | ============== 110 | 111 | calendar_for creates a table like table_for. 112 | All objects get sorted per day of the month 113 | 114 | A basic example would look like this: 115 | 116 | @tasks = Task.this_month 117 | 118 | and 119 | 120 | <%= calendar_for(@tasks) do |t| %> 121 | <%= t.head('mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun') %> 122 | <%= t.day do |day, tasks| %> 123 | <%= day.day %> 124 | <% tasks.each do |task| %> 125 | <%= h(task.name) %> 126 | <% end %> 127 | <% end %> 128 | <% end %> 129 | 130 | To show a different month you can pass the :year and :month options: 131 | 132 | <%= calendar_for(@tasks, :year => 2009, :month => 1) do |t| %> 133 | 134 | To highlight a different day you can pass the :today option: 135 | 136 | <%= calendar_for(@tasks, :today => Date.civil(2008, 12, 26)) do |t| %> 137 | 138 | By default the :date method is called on the objects for sorting. 139 | To use another method you can pass the :day_method option: 140 | 141 | <%= t.day(:day_method => :calendar_date) do |day, tasks| %> 142 | 143 | If you want to add id's to your td tag you can pass a pattern: 144 | 145 | <%= t.day(:id => 'day_%d') do |day, tasks| %> 146 | 147 | To have a header at the begining of each row: 148 | 149 | <%= calendar_for(@tasks, :row_header => true) do |t| %> 150 | 151 | and then in your block you get nil as the list of objects and the first day of thet upcoming week. For example: 152 | 153 | <%= calendar_for(@tasks) do |t| %> 154 | <%= t.day do |day, tasks| %> 155 | <% if tasks.nil? %> 156 | <%= day.cweek %> 157 | <% else %> 158 | <%= day.day %> 159 | <% tasks.each do |task| %> 160 | <%= h(task.name) %> 161 | <% end %> 162 | <% end %> 163 | <% end %> 164 | <% end %> 165 | 166 | Contributing 167 | ============ 168 | 169 | Document any new options and verify the documentation looks correct by running: 170 | 171 | yard server --reload 172 | 173 | and going to http://localhost:8808 174 | 175 | Contributors 176 | ============ 177 | 178 | Petrik de Heus, Sean Dague, F. Kocherga, John Duff, Andrew C. Greenberg, Jason Cheong-Kee-You, [J. Pablo Fernández](http://pupeno.com). 179 | 180 | Original Work Copyright (c) 2008 Petrik de Heus, released under the MIT license. 181 | 182 | Fork revisions Copyright (c) 2010 Andrew C. Greenberg, released under the MIT license. 183 | 184 | Fork revisions Copyright (c) 2012 [Carrousel Apps Ltd (Watu)](http://watuhq.com), released under the MIT license. 185 | -------------------------------------------------------------------------------- /test/calendar_helper_test.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path(File.dirname(__FILE__) + '/test_helper.rb') 2 | 3 | class CalendarHelperTest < ActionView::TestCase 4 | include ActionView::Helpers::TextHelper 5 | include ActionView::Helpers::TagHelper 6 | include CalendarHelper 7 | attr_accessor :output_buffer 8 | 9 | def setup 10 | @events = [Event.new(3, 'Jimmy Page', Date.civil(2008, 12, 26)), 11 | Event.new(4, 'Robert Plant', Date.civil(2008, 12, 26))] 12 | end 13 | 14 | def test_calendar_for 15 | output = calendar_for(@events, :html => { :id => 'id', :style => 'style', :class => 'class'}) do |t| 16 | end 17 | expected = %() << 18 | %(
) 19 | assert_dom_equal expected, output 20 | end 21 | 22 | def test_calendar_for_without_an_array 23 | self.output_buffer = '' 24 | assert_raises(ArgumentError) do 25 | calendar_for('a') {|t| } 26 | end 27 | end 28 | 29 | def test_calendar_for_with_empty_array 30 | output = calendar_for([], :year=> 2008, :month => 12) do |c| 31 | c.day do |day, events| 32 | concat(events.collect{|e| e.id}.join) 33 | end 34 | end 35 | expected = %() << 36 | %() << 37 | %() << 38 | %() << 39 | %() << 40 | %() << 41 | %() << 42 | %() << 43 | %(
) 44 | assert_dom_equal expected, output 45 | end 46 | 47 | def test_calendar_for_with_events 48 | output = calendar_for(@events, :year=> 2008, :month => 12) do |c| 49 | c.day do |day, events| 50 | content = events.collect{|e| e.id}.join 51 | concat("(#{day.day})#{content}") 52 | end 53 | end 54 | expected = %() << 55 | %() << 56 | %() << 57 | %() << 58 | %() << 59 | %() << 60 | %() << 61 | %() << 62 | %(
(30)(1)(2)(3)(4)(5)(6)
(7)(8)(9)(10)(11)(12)(13)
(14)(15)(16)(17)(18)(19)(20)
(21)(22)(23)(24)(25)(26)34(27)
(28)(29)(30)(31)(1)(2)(3)
) 63 | assert_dom_equal expected, output 64 | end 65 | 66 | def test_calendar_for_sets_css_classes 67 | output = calendar_for([], :year=> 2008, :month => 12, :today => Date.civil(2008, 12, 15)) do |c| 68 | c.day do |day, events| 69 | concat(events.collect{|e| e.id}.join) 70 | end 71 | end 72 | expected = %() << 73 | %() << 74 | %() << 75 | %() << 76 | %() << 77 | %() << 78 | %() << 79 | %() << 80 | %(
) 81 | assert_dom_equal expected, output 82 | end 83 | 84 | def test_calendar_for_thirty_days 85 | today = Date.civil(2008, 12, 15) 86 | output = calendar_for([], :today => today, :year=>2008, :month=>12, :first=>today, :last=>:thirty_days) do |c| 87 | c.day do |day, events| 88 | concat(events.collect{|e| e.id}.join) 89 | end 90 | end 91 | expected = %() << 92 | %() << 93 | %() << 94 | %() << 95 | %() << 96 | %() << 97 | %() << 98 | %() << 99 | %(
) 100 | assert_dom_equal expected, output 101 | end 102 | 103 | def test_calendar_for_week 104 | today = Date.civil(2008, 12, 15) 105 | output = calendar_for([], :today => today, :year=>2008, :month=>12, :first=>today, :last=>:week) do |c| 106 | c.day do |day, events| 107 | concat(events.collect{|e| e.id}.join) 108 | end 109 | end 110 | expected = %() << 111 | %() << 112 | %() << 113 | %() << 114 | %(
) 115 | assert_dom_equal expected, output 116 | end 117 | 118 | def test_calendar_for_sets_css_ids 119 | output = calendar_for([], :year=> 2008, :month => 12, :today => Date.civil(2008, 12, 15)) do |c| 120 | c.day(:id => 'day_%d') do |day, events| 121 | concat(events.collect{|e| e.id}.join) 122 | end 123 | end 124 | expected = %() << 125 | %() << 126 | %() << 127 | %() << 128 | %() << 129 | %() << 130 | %() << 131 | %() << 132 | %(
) 133 | assert_dom_equal expected, output 134 | end 135 | 136 | def test_calendar_for_with_row_headers 137 | output = calendar_for([], :year=> 2008, :month => 12, :row_header => true) do |c| 138 | c.day do |day, events| 139 | if events.nil? 140 | concat(day.cweek) 141 | else 142 | concat(events.collect{|e| e.id}.join) 143 | end 144 | end 145 | end 146 | expected = %() << 147 | %() << 148 | %() << 149 | %() << 150 | %() << 151 | %() << 152 | %() << 153 | %() << 154 | %(
48
49
50
51
52
) 155 | assert_dom_equal expected, output 156 | end 157 | 158 | def test_calendar_for_with_enumerable_object 159 | output = calendar_for(Wrapped.new(@events), :year=> 2008, :month => 12) do |c| 160 | c.day do |day, events| 161 | content = events.collect{|e| e.id}.join 162 | concat("(#{day.day})#{content}") 163 | end 164 | end 165 | expected = %() << 166 | %() << 167 | %() << 168 | %() << 169 | %() << 170 | %() << 171 | %() << 172 | %() << 173 | %(
(30)(1)(2)(3)(4)(5)(6)
(7)(8)(9)(10)(11)(12)(13)
(14)(15)(16)(17)(18)(19)(20)
(21)(22)(23)(24)(25)(26)34(27)
(28)(29)(30)(31)(1)(2)(3)
) 174 | assert_dom_equal expected, output 175 | end 176 | 177 | end 178 | 179 | class CalendarHelperTest < ActionView::TestCase 180 | 181 | def setup 182 | @events = [Event.new(3, 'Jimmy Page', Date.civil(2008, 12, 26)), 183 | Event.new(4, 'Robert Plant', Date.civil(2008, 12, 26))] 184 | end 185 | 186 | def test_objects_for_days_with_events 187 | calendar = CalendarHelper::Calendar.new(:year=> 2008, :month => 12) 188 | objects_for_days = {} 189 | Date.civil(2008, 11, 30).upto(Date.civil(2009, 1, 3)){|day| objects_for_days[day.strftime("%Y-%m-%d")] = [day, []]} 190 | objects_for_days['2008-12-26'][1] = @events 191 | assert_equal objects_for_days, calendar.objects_for_days(@events, :date) 192 | end 193 | 194 | def test_objects_for_days 195 | calendar = CalendarHelper::Calendar.new(:year=> 2008, :month => 12) 196 | objects_for_days = {} 197 | Date.civil(2008, 11, 30).upto(Date.civil(2009, 1, 3)){|day| objects_for_days[day.strftime("%Y-%m-%d")] = [day, []]} 198 | assert_equal objects_for_days, calendar.objects_for_days([], :date) 199 | end 200 | 201 | def test_days 202 | calendar = CalendarHelper::Calendar.new(:year=> 2008, :month => 12) 203 | days = [] 204 | Date.civil(2008, 11, 30).upto(Date.civil(2009, 1, 3)){|day| days << day} 205 | assert_equal days, calendar.days 206 | end 207 | 208 | def test_days_with_first_day_of_week_set 209 | calendar = CalendarHelper::Calendar.new(:year=> 2008, :month => 12, :first_day_of_week => 1) 210 | days = [] 211 | Date.civil(2008, 12, 1).upto(Date.civil(2009, 1, 4)){|day| days << day} 212 | assert_equal days, calendar.days 213 | end 214 | 215 | def test_first_day 216 | calendar = CalendarHelper::Calendar.new(:year=> 2008, :month => 12) 217 | assert_equal Date.civil(2008, 11, 30), calendar.first_day 218 | end 219 | 220 | def test_last_day 221 | calendar = CalendarHelper::Calendar.new(:year=> 2008, :month => 12) 222 | assert_equal Date.civil(2009, 1, 3), calendar.last_day 223 | end 224 | 225 | def test_last_day_with_first_day_of_week_set 226 | calendar = CalendarHelper::Calendar.new(:year=> 2008, :month => 12, :first_day_of_week => 1) 227 | assert_equal Date.civil(2009, 1, 4), calendar.last_day 228 | end 229 | end 230 | 231 | class Event < Struct.new(:id, :name, :date); end 232 | 233 | class Wrapped 234 | include Enumerable 235 | attr_accessor :objects 236 | 237 | def initialize(objects) 238 | @objects = objects 239 | end 240 | 241 | def each 242 | @objects.each { |item| yield item } 243 | end 244 | 245 | def <=>(other) 246 | @objects <=> other 247 | end 248 | end 249 | --------------------------------------------------------------------------------