later_dude 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,13 @@
1
+ 2009/10/30
2
+ ----------
3
+ - Introduced new :yield_surrounding_days option to yield surrounding days of other months to passed block.
4
+
5
+ 2009/2/2
6
+ ---------
7
+ - Introduced options to show and format next and previous month.
8
+ - Replaced option :header_month_format with :current_month to align it with :next_month and :previous_month.
9
+ - Removed some unused methods for month names.
10
+
11
+ 2009/1/14
12
+ ---------
13
+ First release
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Clemens Kofler
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.
data/README ADDED
@@ -0,0 +1,142 @@
1
+ LaterDude
2
+ =========
3
+
4
+ LaterDude is a small calendar helper plugin for Rails with i18n support.
5
+
6
+ It was heavily inspired by "Jeremy Voorhis'/Geoffrey Grosenbach's calendar_helper plugin":http://topfunky.net/svn/plugins/calendar_helper/. Here's what's different:
7
+
8
+ * LaterDude leverages the power of the "Rails i18n plugin":http://rails-i18n.org. This means that if you have Rails 2.2 or higher (or if you've installed the i18n gem and the plugin separately), you can just drop in locales and you'll get localized month and day names.
9
+ * Where the calendar_helper had one helper method that internally called multiple other private methods, LaterDude keeps the namespace clean by tucking away all of its functionality in a presenter (of sorts).
10
+ * Most CSS class names are given implictly, e.g. "day", "weekendDay" (both are given to the displayed month as well as to the previous and following month), "otherMonth" (all days that don't belong to the displayed month) and "today". The only CSS class that can be configured is the table's class (which defaults to "calendar"). Further customization can be taken care of by passing a block (see examples).
11
+
12
+ Installation
13
+ ============
14
+
15
+ You can use LaterDude either as a gem (preferred) or as a Rails plugin.
16
+
17
+ To use the gem version, put the following gem requirement in your environment.rb:
18
+
19
+ config.gem "later_dude", :source => 'http://gemcutter.org'
20
+
21
+ To install it as a plugin, fire up your terminal, go to your Rails app and type:
22
+
23
+ $ ruby script/plugin install git://github.com/clemens/later_dude.git
24
+
25
+ Examples
26
+ ========
27
+
28
+ There are two basic ways of outputting a calendar: via the presenter itself or by using the helper method:
29
+
30
+ <%= calendar_for(2009, 1) %> and
31
+ <%= LaterDude::Calendar.new(2009, 1).to_html %>
32
+
33
+ both yield the same results. This is similar to what Rails does with RedCloth/BlueCloth filters.
34
+
35
+ In addition to year and month which are required parameters you can optionally supply an options hash:
36
+
37
+ <%= calendar_for(2009, 1, :calendar_class => "my_calendar", :first_day_of_week => 1)
38
+
39
+ Possible options are:
40
+
41
+ * :calendar_class: The CSS class for the surrounding table. Defaults to "calendar".
42
+ * :first_day_of_week: The first day of the week (0 = Sunday, 1 = Monday etc.). Defaults to the :'date.first_day_of_week' translation in the respective locale or 0 if no translation can be found.
43
+ * :hide_day_names: Hide the table header showing day names. Defaults to false.
44
+ * :hide_month_name: Hide the table header showing the month name. Defaults to false.
45
+ * :use_full_day_names: Use full instead of abbreviated day names. Defaults to false.
46
+ * :use_full_month_names: Use full instead of abbreviated month names. Defaults to true.
47
+ * :yield_surrounding_days: Defines whether or not days or the previous and next month are yielded to the passed block. Defaults to false.
48
+
49
+ * :next_month: Defines if and how the next month should be displayed. See section "Formatting the header section" for further information.
50
+ * :previous_month: Defines if and how the previous month should be displayed. See section "Formatting the header section" for further information.
51
+ * :next_and_previous_month: Defines if and how the next and previous month should be displayed. See section "Formatting the header section" for further information.
52
+ * :current_month: Defines how the current month should be displayed. See section "Formatting the header section" for further information.
53
+
54
+ You can also pass in a block to mark days according to your own set of rules. The block gets passed each day of the current month (i.e. days of the previous and following month are *not* yielded):
55
+
56
+ <%= calendar_for(2009, 1) do |day|
57
+ if Calendar.has_events_on?(day)
58
+ [link_to(day.day, events_path(day.year, day.month, day.day)), { :class => "dayWithEvents" }]
59
+ else
60
+ day.day
61
+ end
62
+ end %>
63
+
64
+ The block can either return an array containing two elements or a single value. If an array is returned, the first element will be the content of the table cell and the second will be used as HTML options for the table cell tag. If a single value is returned, it will be used as the content of the table cell.
65
+
66
+ Hint: You can avoid cluttering up your views and move the block to a helper:
67
+
68
+ # app/helpers/calendar_helper.rb
69
+ def calendar_events_proc
70
+ lambda do |day|
71
+ if Calendar.has_events_on?(day)
72
+ [link_to(day.day, events_path(day.year, day.month, day.day)), { :class => "dayWithEvents" }]
73
+ else
74
+ day.day
75
+ end
76
+ end
77
+ end
78
+
79
+ # app/views/calendar/show.html
80
+ <%= calendar_for(2009, 1, &calendar_events_proc)
81
+
82
+ Formatting the header section
83
+ =============================
84
+
85
+ You can customize the calendar's header section (i.e. the first table row) by using one or more of the following options:
86
+ * :next_month
87
+ * :previous_month
88
+ * :next_and_previous_month
89
+ * :current_month
90
+
91
+ All of these can either take a single value or an array of two values:
92
+
93
+ # simple string
94
+ :next_month => "&raquo;"
95
+
96
+ # strftime string
97
+ :next_month => "%b"
98
+
99
+ # proc - will evaluate the proc
100
+ :next_month => lambda { |date| link_to "&raquo;", calendar_path(date.year, month.year) }
101
+
102
+ # simple string and proc - will evaluate the proc and use it for link_to, e.g. link_to("&raquo;", calendar_path(date.year, month.year))
103
+ :next_month => ["&raquo;", lambda { |date| calendar_path(date.year, month.year) }]
104
+
105
+ # strftime string and proc - will evaluate the proc and use it for link_to, e.g. link_to("Jan", calendar_path(date.year, month.year))
106
+ :next_month => ["%b", lambda { |date| calendar_path(date.year, month.year) }]
107
+
108
+ If you use a proc it gets passed a Date object carrying the respective month, i.e. the proc for :previous_month gets passed a Date object representing the previous month etc.
109
+
110
+ You can do all kinds of mighty stuff with a proc. Imagine a situation where you want to show the abbreviated month name and then the number of events for the given month:
111
+
112
+ # depending on your routes, this outputs something like <a href="/calendar/2009/1">Jan (3 events)</a>
113
+ :next_month => lambda { |date| link_to I18n.localize(date, :format => "%b (#{pluralize(Event.in_month(date))})", calendar_path(date.year, date.month) }
114
+
115
+ The option :next_and_previous_month can be used as a shortcut if you want to use the same formatting for the next and previous month. Both, :next_month and :previous_month override the :next_and_previous_month if given.
116
+
117
+ i18n
118
+ ====
119
+
120
+ You can define the following keys in your locale files to customize output:
121
+
122
+ * date.formats.calendar_header: Defines the date format to be used for the table header.
123
+ * date.first_day_of_week: Defines the first day of a week (0 = Sunday, 1 = Monday etc.).
124
+
125
+ Of course, you can also put other configuration options in the locale and then pass them in manually:
126
+
127
+ # somewhere in a locale
128
+ date:
129
+ calendar:
130
+ calendar_class: my_cool_calendar
131
+
132
+ # in the view
133
+ <%= calendar_for(2009, 1, :calendar_class => I18n.translate(:'date.calendar.calendar_class', :default => "my_calendar"))
134
+
135
+ Note that you should always pass in a default in case a translation can't be found since your options override the default options which might result in weird exceptions if the translation is missing.
136
+
137
+ Bugs & Feedback
138
+ ===============
139
+
140
+ You can send me feedback, bug reports and patches via "GitHub":http://github.com/clemens.
141
+
142
+ Copyright (c) 2009 Clemens Kofler <clemens@railway.at>, released under the MIT license.
@@ -0,0 +1,10 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+
5
+ require 'tasks/distribution'
6
+ require 'tasks/documentation'
7
+ require 'tasks/testing'
8
+
9
+ desc 'Default: run unit tests.'
10
+ task :default => :test
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.2.1
data/init.rb ADDED
@@ -0,0 +1,2 @@
1
+ require 'later_dude'
2
+ ActionView::Base.send(:include, LaterDude::CalendarHelper)
@@ -0,0 +1,212 @@
1
+ require 'i18n'
2
+ require 'action_view'
3
+
4
+ module LaterDude
5
+ module CalendarHelper
6
+ def calendar_for(year, month, options={}, &block)
7
+ Calendar.new(year, month, options, &block).to_html
8
+ end
9
+ end
10
+
11
+ # TODO: Maybe make output prettier?
12
+ class Calendar
13
+ include ActionView::Helpers::TagHelper
14
+ include ActionView::Helpers::UrlHelper
15
+
16
+ def initialize(year, month, options={}, &block)
17
+ @year, @month = year, month
18
+ @options = options.symbolize_keys.reverse_merge(Calendar.default_calendar_options)
19
+
20
+ # next_month and previous_month take precedence over next_and_previous_month
21
+ @options[:next_month] ||= @options[:next_and_previous_month]
22
+ @options[:previous_month] ||= @options[:next_and_previous_month]
23
+
24
+ @days = Date.civil(@year, @month, 1)..Date.civil(@year, @month, -1)
25
+ @block = block
26
+ end
27
+
28
+ def to_html
29
+ <<-EOF
30
+ <table class="#{@options[:calendar_class]}">
31
+ <thead>
32
+ #{show_month_names}
33
+ #{show_day_names}
34
+ </thead>
35
+ <tbody>
36
+ #{show_days}
37
+ </tbody>
38
+ </table>
39
+ EOF
40
+ end
41
+
42
+ private
43
+ def show_days
44
+ "<tr>#{show_previous_month}#{show_current_month}#{show_following_month}</tr>"
45
+ end
46
+
47
+ def show_previous_month
48
+ return if @days.first.wday == first_day_of_week # don't display anything if the first day is the first day of a week
49
+
50
+ returning "" do |output|
51
+ beginning_of_week(@days.first).upto(@days.first - 1) { |d| output << show_day(d) }
52
+ end
53
+ end
54
+
55
+ def show_current_month
56
+ returning "" do |output|
57
+ @days.first.upto(@days.last) { |d| output << show_day(d) }
58
+ end
59
+ end
60
+
61
+ def show_following_month
62
+ return if @days.last.wday == last_day_of_week # don't display anything if the last day is the last day of a week
63
+
64
+ returning "" do |output|
65
+ (@days.last + 1).upto(beginning_of_week(@days.last + 1.week) - 1) { |d| output << show_day(d) }
66
+ end
67
+ end
68
+
69
+ def show_day(day)
70
+ options = { :class => "day" }
71
+ options[:class] << " otherMonth" if day.month != @days.first.month
72
+ options[:class] << " weekend" if Calendar.weekend?(day)
73
+ options[:class] << " today" if day.today?
74
+
75
+ # block is only called for current month or if :yield_surrounding_days is set to true
76
+ if @block && (@options[:yield_surrounding_days] || day.month == @days.first.month)
77
+ content, options_from_block = Array(@block.call(day))
78
+
79
+ # passing options is optional
80
+ if options_from_block.is_a?(Hash)
81
+ options[:class] << " #{options_from_block.delete(:class)}" if options_from_block[:class]
82
+ options.merge!(options_from_block)
83
+ end
84
+ else
85
+ content = day.day
86
+ end
87
+
88
+ returning content_tag(:td, content, options) do |output|
89
+ if day < @days.last && day.wday == last_day_of_week # opening and closing tag for the first and last week are included in #show_days
90
+ output << "</tr><tr>" # close table row at the end of a week and start a new one
91
+ end
92
+ end
93
+ end
94
+
95
+ def beginning_of_week(day)
96
+ diff = day.wday - first_day_of_week
97
+ diff += 7 if first_day_of_week > day.wday # hackish ;-)
98
+ day - diff
99
+ end
100
+
101
+ def show_month_names
102
+ return if @options[:hide_month_name]
103
+
104
+ %(<tr class="month_names">
105
+ #{previous_month}#{current_month}#{next_month}
106
+ </tr>)
107
+ end
108
+
109
+ # @options[:previous_month] can either be a single value or an array containing two values. For a single value, the
110
+ # value can either be a strftime compatible string or a proc.
111
+ # For an array, the first value is considered to be a strftime compatible string and the second is considered to be
112
+ # a proc. If the second value is not a proc then it will be ignored.
113
+ def previous_month
114
+ return unless @options[:previous_month]
115
+
116
+ show_month(@days.first - 1.month, @options[:previous_month], :class => "previous")
117
+ end
118
+
119
+ # see previous_month
120
+ def next_month
121
+ return unless @options[:next_month]
122
+
123
+ show_month(@days.first + 1.month, @options[:next_month], :class => "next")
124
+ end
125
+
126
+ # see previous_month and next_month
127
+ def current_month
128
+ colspan = @options[:previous_month] || @options[:next_month] ? 3 : 7 # span across all 7 days if previous and next month aren't shown
129
+
130
+ show_month(@days.first, @options[:current_month], :colspan => colspan, :class => "current")
131
+ end
132
+
133
+ def show_month(month, format, options={})
134
+ options[:colspan] ||= 2
135
+
136
+ returning %(<th colspan="#{options[:colspan]}" class="#{options[:class]} #{Date::MONTHNAMES[month.month].downcase}">) do |output|
137
+ output << if format.kind_of?(Array) && format.size == 2
138
+ text = I18n.localize(month, :format => format.first.to_s)
139
+ format.last.respond_to?(:call) ? link_to(text, format.last.call(month)) : text
140
+ else
141
+ format.respond_to?(:call) ? format.call(month) : I18n.localize(month, :format => format.to_s)
142
+ end
143
+ output << '</th>'
144
+ end
145
+ end
146
+
147
+ def day_names
148
+ @day_names ||= @options[:use_full_day_names] ? full_day_names : abbreviated_day_names
149
+ end
150
+
151
+ def full_day_names
152
+ @full_day_names ||= I18n.translate(:'date.day_names')
153
+ end
154
+
155
+ def abbreviated_day_names
156
+ @abbreviated_day_names ||= I18n.translate(:'date.abbr_day_names')
157
+ end
158
+
159
+ def show_day_names
160
+ return if @options[:hide_day_names]
161
+
162
+ returning '<tr class="day_names">' do |output|
163
+ apply_first_day_of_week(day_names).each do |day|
164
+ output << %(<th scope="col" class="#{Date::DAYNAMES[day_names.index(day)].downcase}">#{include_day_abbreviation(day)}</th>)
165
+ end
166
+ output << "</tr>"
167
+ end
168
+ end
169
+
170
+ # => <abbr title="Sunday">Sun</abbr>
171
+ def include_day_abbreviation(day)
172
+ return day if @options[:use_full_day_names]
173
+
174
+ %(<abbr title="#{full_day_names[abbreviated_day_names.index(day)]}">#{day}</abbr>)
175
+ end
176
+
177
+ def apply_first_day_of_week(day_names)
178
+ names = day_names.dup
179
+ first_day_of_week.times { names.push(names.shift) }
180
+ names
181
+ end
182
+
183
+ def first_day_of_week
184
+ @options[:first_day_of_week]
185
+ end
186
+
187
+ def last_day_of_week
188
+ @options[:first_day_of_week] > 0 ? @options[:first_day_of_week] - 1 : 6
189
+ end
190
+
191
+ class << self
192
+ def weekend?(day)
193
+ [0,6].include?(day.wday) # 0 = Sunday, 6 = Saturday
194
+ end
195
+
196
+ def default_calendar_options
197
+ {
198
+ :calendar_class => "calendar",
199
+ :first_day_of_week => I18n.translate(:'date.first_day_of_week', :default => "0").to_i,
200
+ :hide_day_names => false,
201
+ :hide_month_name => false,
202
+ :use_full_day_names => false,
203
+ :current_month => I18n.translate(:'date.formats.calendar_header', :default => "%B"),
204
+ :next_month => false,
205
+ :previous_month => false,
206
+ :next_and_previous_month => false,
207
+ :yield_surrounding_days => false
208
+ }
209
+ end
210
+ end
211
+ end
212
+ end
@@ -0,0 +1,22 @@
1
+ begin
2
+ require 'jeweler'
3
+ Jeweler::Tasks.new do |s|
4
+ s.name = "later_dude"
5
+ s.summary = "Small calendar helper plugin for Rails with i18n support"
6
+ s.email = "clemens@railway.at"
7
+ s.homepage = "http://github.com/clemens/later_dude"
8
+ s.description = "LaterDude is a small calendar helper plugin for Rails with i18n support."
9
+ s.authors = ["Clemens Kofler"]
10
+ s.files = FileList["CHANGELOG",
11
+ "init.rb",
12
+ "lib/later_dude.rb",
13
+ "MIT-LICENSE",
14
+ "Rakefile",
15
+ "README",
16
+ "tasks/**/*.rb",
17
+ "VERSION"]
18
+ s.test_files = FileList["test/**/*.rb"]
19
+ end
20
+ rescue LoadError
21
+ puts "Jeweler, or one of its dependencies, is not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
22
+ end
@@ -0,0 +1,8 @@
1
+ desc 'Generate documentation for the later_dude plugin.'
2
+ Rake::RDocTask.new(:rdoc) do |rdoc|
3
+ rdoc.rdoc_dir = 'rdoc'
4
+ rdoc.title = 'LaterDude'
5
+ rdoc.options << '--line-numbers' << '--inline-source'
6
+ rdoc.rdoc_files.include('README')
7
+ rdoc.rdoc_files.include('lib/**/*.rb')
8
+ end
@@ -0,0 +1,7 @@
1
+ desc 'Test the later_dude plugin.'
2
+ Rake::TestTask.new(:test) do |t|
3
+ t.libs << 'lib'
4
+ t.libs << 'test'
5
+ t.pattern = 'test/**/*_test.rb'
6
+ t.verbose = true
7
+ end
@@ -0,0 +1,24 @@
1
+ require 'test_helper'
2
+
3
+ class CalendarHelperTest < ActiveSupport::TestCase
4
+ include LaterDude::CalendarHelper
5
+
6
+ test "requires year and month" do
7
+ assert_raises(ArgumentError) { calendar_for }
8
+ assert_raises(ArgumentError) { calendar_for(2009) }
9
+ assert_nothing_raised { calendar_for(2009, 1) }
10
+ end
11
+
12
+ test "accepts optional options hash" do
13
+ options = { :calendar_class => "my_calendar", :first_day_of_week => 1 }
14
+ assert_nothing_raised { calendar_for(2009, 1, options) }
15
+ end
16
+
17
+ test "accepts optional block" do
18
+ options = { :calendar_class => "my_calendar", :first_day_of_week => 1 }
19
+ some_block = lambda { nil }
20
+
21
+ assert_nothing_raised { calendar_for(2009, 1, &some_block) }
22
+ assert_nothing_raised { calendar_for(2009, 1, options, &some_block) }
23
+ end
24
+ end
@@ -0,0 +1,255 @@
1
+ require 'test_helper'
2
+
3
+ # TODO: figure out why I have to reference Calendar via its module ...
4
+ class CalendarTest < ActiveSupport::TestCase
5
+ # some constants for increased readability
6
+ FULL_DAY_NAMES = I18n.translate(:'date.day_names')
7
+ ABBR_DAY_NAMES = I18n.translate(:'date.abbr_day_names')
8
+ FULL_MONTH_NAMES = I18n.translate(:'date.month_names')
9
+ ABBR_MONTH_NAMES = I18n.translate(:'date.abbr_month_names')
10
+
11
+ test "has default options" do
12
+ # TODO improve this so that every call gets a fresh copy of the default options
13
+ default_calendar_options = LaterDude::Calendar.send(:default_calendar_options)
14
+
15
+ assert_kind_of Hash, default_calendar_options
16
+
17
+ assert_equal "calendar", default_calendar_options[:calendar_class]
18
+
19
+ [:hide_day_names, :hide_month_name, :use_full_day_names].each do |key|
20
+ assert !default_calendar_options[key]
21
+ end
22
+
23
+ # default date format for header is to show the full month name if no translation is set in locale
24
+ assert_equal "%B", default_calendar_options[:current_month]
25
+
26
+ # next and previous month aren't shown by default
27
+ assert !default_calendar_options[:next_month]
28
+ assert !default_calendar_options[:previous_month]
29
+ assert !default_calendar_options[:next_and_previous_month]
30
+
31
+ # some options use i18n ...
32
+ I18n.stubs(:translate).with(:'date.first_day_of_week', :default => "0").then.returns("1")
33
+ I18n.stubs(:translate).with(:'date.formats.calendar_header', :default => "%B").then.returns("%B %Y")
34
+
35
+ # default first day of week is Sunday (= 0) if no translation is set in locale
36
+ assert_equal 0, default_calendar_options[:first_day_of_week]
37
+ # with default first day of week set in locale
38
+ assert_equal 1, (LaterDude::Calendar.send(:default_calendar_options)[:first_day_of_week]) # have to do this so that we don't use the cached version
39
+
40
+ # with default date format set in locale
41
+ assert_equal "%B %Y", (LaterDude::Calendar.send(:default_calendar_options)[:current_month])
42
+ end
43
+
44
+ # options
45
+ test "uses full day names" do
46
+ assert_equal FULL_DAY_NAMES, LaterDude::Calendar.new(2009, 1, :use_full_day_names => true).send(:day_names)
47
+ end
48
+
49
+ test "uses abbreviated day names" do
50
+ assert_equal ABBR_DAY_NAMES, LaterDude::Calendar.new(2009, 1).send(:day_names)
51
+ assert_equal ABBR_DAY_NAMES, LaterDude::Calendar.new(2009, 1, :use_full_day_names => false).send(:day_names)
52
+ end
53
+
54
+ test "doesn't show day names" do
55
+ assert_nil LaterDude::Calendar.new(2009, 1, :hide_day_names => true).send(:show_day_names)
56
+ end
57
+
58
+ # TODO I think this isn't needed anymore due to the new :header_date_format option
59
+ test "doesn't show month names" do
60
+ assert_nil LaterDude::Calendar.new(2009, 1, :hide_month_name => true).send(:show_month_names)
61
+ end
62
+
63
+ # links/texts for next, previous and current month
64
+ test "uses next month for a string" do
65
+ assert_match %r(&raquo;), LaterDude::Calendar.new(2009, 1, :next_month => "&raquo;").send(:next_month)
66
+ end
67
+
68
+ test "uses next month for a strftime string" do
69
+ assert_match %r(Feb), LaterDude::Calendar.new(2009, 1, :next_month => "%b").send(:next_month)
70
+ end
71
+
72
+ test "uses next month for proc" do
73
+ assert_match %r(<a href="/events/2009/2">&raquo;</a>), LaterDude::Calendar.new(2009, 1, :next_month => lambda { |date| link_to "&raquo;", "/events/#{date.year}/#{date.month}" }).send(:next_month)
74
+ end
75
+
76
+ test "uses next month for array if first value is a string and second is a proc" do
77
+ assert_match %r(<a href="/events/2009/2">&raquo;</a>), LaterDude::Calendar.new(2009, 1, :next_month => ["&raquo;", lambda { |date| "/events/#{date.year}/#{date.month}" }]).send(:next_month)
78
+ end
79
+
80
+ test "uses next month for array if first value is a strftime string and second is a proc" do
81
+ assert_match %r(<a href="/events/2009/2">Feb</a>), LaterDude::Calendar.new(2009, 1, :next_month => ["%b", lambda { |date| "/events/#{date.year}/#{date.month}" }]).send(:next_month)
82
+ end
83
+
84
+ test "uses previous month for a string" do
85
+ assert_match %r(&laquo;), LaterDude::Calendar.new(2009, 1, :previous_month => "&laquo;").send(:previous_month)
86
+ end
87
+
88
+ test "uses previous month for a strftime string" do
89
+ assert_match %r(Dec), LaterDude::Calendar.new(2009, 1, :previous_month => "%b").send(:previous_month)
90
+ end
91
+
92
+ test "uses previous month for proc" do
93
+ assert_match %r(<a href="/events/2008/12">&laquo;</a>), LaterDude::Calendar.new(2009, 1, :previous_month => lambda { |date| link_to "&laquo;", "/events/#{date.year}/#{date.month}" }).send(:previous_month)
94
+ end
95
+
96
+ test "uses previous month for array if first value is a string and second is a proc" do
97
+ assert_match %r(<a href="/events/2008/12">&laquo;</a>), LaterDude::Calendar.new(2009, 1, :previous_month => ["&laquo;", lambda { |date| "/events/#{date.year}/#{date.month}" }]).send(:previous_month)
98
+ end
99
+
100
+ test "uses previous month for array if first value is a strftime string and second is a proc" do
101
+ assert_match %r(<a href="/events/2008/12">Dec</a>), LaterDude::Calendar.new(2009, 1, :previous_month => ["%b", lambda { |date| "/events/#{date.year}/#{date.month}" }]).send(:previous_month)
102
+ end
103
+
104
+ test "uses next and previous month for array if first value is a strftime string and second is a proc" do
105
+ calendar = LaterDude::Calendar.new(2009, 1, :next_and_previous_month => ["%b", lambda { |date| "/events/#{date.year}/#{date.month}" }])
106
+ assert_match %r(<a href="/events/2008/12">Dec</a>), calendar.send(:previous_month)
107
+ assert_match %r(<a href="/events/2009/2">Feb</a>), calendar.send(:next_month)
108
+ end
109
+
110
+ test "uses next/previous month options rather than combined option if both are given" do
111
+ calendar = LaterDude::Calendar.new(2009, 1, :next_month => "&raquo;", :previous_month => "&laquo;", :next_and_previous_month => ["%b", lambda { |date| "/events/#{date.year}/#{date.month}" }])
112
+ assert_match %r(&laquo;), calendar.send(:previous_month)
113
+ assert_match %r(&raquo;), calendar.send(:next_month)
114
+ end
115
+
116
+ test "uses current month for a string" do
117
+ assert_match %r(Current), LaterDude::Calendar.new(2009, 1, :current_month => "Current").send(:current_month)
118
+ end
119
+
120
+ test "uses current month for a strftime string" do
121
+ assert_match %r(Jan), LaterDude::Calendar.new(2009, 1, :current_month => "%b").send(:current_month)
122
+ end
123
+
124
+ test "uses current month for proc" do
125
+ assert_match %r(<a href="/events/2009/1">Current</a>), LaterDude::Calendar.new(2009, 1, :current_month => lambda { |date| link_to "Current", "/events/#{date.year}/#{date.month}" }).send(:current_month)
126
+ end
127
+
128
+ test "uses current month for array if first value is a string and second is a proc" do
129
+ assert_match %r(<a href="/events/2009/1">Current</a>), LaterDude::Calendar.new(2009, 1, :current_month => ["Current", lambda { |date| "/events/#{date.year}/#{date.month}" }]).send(:current_month)
130
+ end
131
+
132
+ test "uses current month for array if first value is a strftime string and second is a proc" do
133
+ assert_match %r(<a href="/events/2009/1">Jan</a>), LaterDude::Calendar.new(2009, 1, :current_month => ["%b", lambda { |date| "/events/#{date.year}/#{date.month}" }]).send(:current_month)
134
+ end
135
+
136
+ # helper methods
137
+ test "shows whether a given day is on a weekend or not" do
138
+ [0, 6].each { |day| assert LaterDude::Calendar.weekend?(mock(:wday => day)) }
139
+ (1..5).each { |day| assert !LaterDude::Calendar.weekend?(mock(:wday => day)) }
140
+ end
141
+
142
+ test "includes day name abbreviation" do
143
+ assert_equal %q(<abbr title="Sunday">Sun</abbr>), LaterDude::Calendar.new(2009, 1, :use_full_day_names => false).send(:include_day_abbreviation, "Sun")
144
+ end
145
+
146
+ test "doesn't include day name abbreviation" do
147
+ assert_equal "Sunday", LaterDude::Calendar.new(2009, 1, :use_full_day_names => true).send(:include_day_abbreviation, "Sunday")
148
+ end
149
+
150
+ test "shows index of first and last day of week" do
151
+ assert_equal 0, LaterDude::Calendar.new(2009, 1).send(:first_day_of_week)
152
+ assert_equal 6, LaterDude::Calendar.new(2009, 1).send(:last_day_of_week)
153
+
154
+ assert_equal 1, LaterDude::Calendar.new(2009, 1, :first_day_of_week => 1).send(:first_day_of_week)
155
+ assert_equal 0, LaterDude::Calendar.new(2009, 1, :first_day_of_week => 1).send(:last_day_of_week)
156
+ end
157
+
158
+ test "applies first day of week accordingly" do
159
+ assert_equal %w(Mon Tue Wed Thu Fri Sat Sun), LaterDude::Calendar.new(2009, 1, :first_day_of_week => 1).send(:apply_first_day_of_week, ABBR_DAY_NAMES)
160
+ end
161
+
162
+ test "returns the first day of the week for a given date" do
163
+ day = Date.civil(2009, 1, 4) # first Sunday in 2009
164
+
165
+ assert_equal Date.civil(2009, 1, 4), LaterDude::Calendar.new(2009, 1, :first_day_of_week => 0).send(:beginning_of_week, day)
166
+ assert_equal Date.civil(2008, 12, 29), LaterDude::Calendar.new(2009, 1, :first_day_of_week => 1).send(:beginning_of_week, day)
167
+ assert_equal Date.civil(2008, 12, 30), LaterDude::Calendar.new(2009, 1, :first_day_of_week => 2).send(:beginning_of_week, day)
168
+ assert_equal Date.civil(2008, 12, 31), LaterDude::Calendar.new(2009, 1, :first_day_of_week => 3).send(:beginning_of_week, day)
169
+ assert_equal Date.civil(2009, 1, 1), LaterDude::Calendar.new(2009, 1, :first_day_of_week => 4).send(:beginning_of_week, day)
170
+ assert_equal Date.civil(2009, 1, 2), LaterDude::Calendar.new(2009, 1, :first_day_of_week => 5).send(:beginning_of_week, day)
171
+ assert_equal Date.civil(2009, 1, 3), LaterDude::Calendar.new(2009, 1, :first_day_of_week => 6).send(:beginning_of_week, day)
172
+ end
173
+
174
+ # TODO: may consider to mock some of the behavior so I don't have to use concrete months that fit for a test
175
+ test "shows previous month if first day of month isn't the first day of a week" do
176
+ assert_not_nil LaterDude::Calendar.new(2009, 4).send(:show_previous_month) # April 1st is a Wednesday
177
+ end
178
+
179
+ test "shows following month if last day of month isn't the last day of a week" do
180
+ assert_not_nil LaterDude::Calendar.new(2009, 4).send(:show_following_month) # April 30th is a Thursday
181
+ end
182
+
183
+ test "shouldn't show previous month if first day of month is the first day of a week" do
184
+ assert_nil LaterDude::Calendar.new(2009, 2).send(:show_previous_month) # February 1st is a Sunday
185
+ end
186
+
187
+ test "shouldn't show following month if last day of month is the last day of a week" do
188
+ assert_nil LaterDude::Calendar.new(2009, 2).send(:show_following_month) # February 28th is a Saturday
189
+ end
190
+
191
+ test "highlights current day (= today)" do
192
+ Date.stubs(:current).then.returns(Date.civil(2009, 1, 14))
193
+ assert_match %r(<td class="([^\"]*)today([^\"]*)">14</td>), LaterDude::Calendar.new(2009, 1).to_html
194
+ end
195
+
196
+ test "shows special days as designated by a block" do
197
+ CalendarTest.send(:include, ActionView::Helpers)
198
+
199
+ # all even days should be linked
200
+ special_days_proc = lambda do |day|
201
+ if day.day.even?
202
+ [link_to(day.day, "/calendar/#{day.year}/#{day.month}/#{day.day}"), { :class => "specialDay" } ]
203
+ else
204
+ day.day
205
+ end
206
+ end
207
+
208
+ calendar_html = LaterDude::Calendar.new(2009, 1, &special_days_proc).to_html
209
+
210
+ (Date.civil(2009, 1, 1)..Date.civil(2009, 1, -1)).each do |day|
211
+ if day.day.even?
212
+ assert_match %r(<td class="day([^\"]*)specialDay"><a href="/calendar/#{day.year}/#{day.month}/#{day.day}">#{day.day}</a></td>), calendar_html
213
+ else
214
+ assert_match %r(<td class="day([^\"]*)">#{day.day}</td>), calendar_html
215
+ assert_no_match %r(<td class="day([^\"]*)specialDay"><a href="/calendar/#{day.year}/#{day.month}/#{day.day}">#{day.day}</a></td>), calendar_html
216
+ end
217
+ end
218
+ end
219
+
220
+ test "yields days of surrounding months :yield_surrounding_days is set to true" do
221
+ CalendarTest.send(:include, ActionView::Helpers)
222
+
223
+ # make it bold
224
+ special_days_proc = lambda { |day| "<b>#{day.day}</b>" }
225
+
226
+ calendar_html = LaterDude::Calendar.new(2009, 4, { :yield_surrounding_days => true }, &special_days_proc).to_html
227
+
228
+ # start of first week: 29 March 2009
229
+ # end of last week: 2 May 2009
230
+ surrounding_days = [Date.civil(2009, 3, 29), Date.civil(2009, 3, 30), Date.civil(2009, 3, 31), Date.civil(2009, 5, 1), Date.civil(2009, 5, 2)]
231
+ surrounding_days.each do |day|
232
+ assert_match %r(<td class="day([^\"]*)otherMonth([^\"]*)"><b>#{day.day}</b></td>), calendar_html
233
+ end
234
+ end
235
+
236
+ test "yields days of surrounding months :yield_surrounding_days isn't set to true" do
237
+ CalendarTest.send(:include, ActionView::Helpers)
238
+
239
+ # make it bold
240
+ special_days_proc = lambda { |day| "<b>#{day.day}</b>" }
241
+
242
+ calendar_html = LaterDude::Calendar.new(2009, 4, &special_days_proc).to_html
243
+ calendar_html2 = LaterDude::Calendar.new(2009, 4, { :yield_surrounding_days => false }, &special_days_proc).to_html
244
+
245
+ # start of first week: 29 March 2009
246
+ # end of last week: 2 May 2009
247
+ surrounding_days = [Date.civil(2009, 3, 29), Date.civil(2009, 3, 30), Date.civil(2009, 3, 31), Date.civil(2009, 5, 1), Date.civil(2009, 5, 2)]
248
+ surrounding_days.each do |day|
249
+ assert_no_match %r(<td class="day([^\"]*)otherMonth([^\"]*)"><b>#{day.day}</b></td>), calendar_html
250
+ assert_no_match %r(<td class="day([^\"]*)otherMonth([^\"]*)"><b>#{day.day}</b></td>), calendar_html2
251
+ end
252
+ end
253
+
254
+ # TODO: Should I do "real" output testing despite the good coverage of output-related methods? Testing HTML is tedious ...
255
+ end
@@ -0,0 +1,5 @@
1
+ require 'rubygems'
2
+ require 'active_support'
3
+ require 'active_support/test_case'
4
+
5
+ require 'later_dude'
metadata ADDED
@@ -0,0 +1,66 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: later_dude
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.1
5
+ platform: ruby
6
+ authors:
7
+ - Clemens Kofler
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-10-31 00:00:00 +01:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: LaterDude is a small calendar helper plugin for Rails with i18n support.
17
+ email: clemens@railway.at
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README
24
+ files:
25
+ - CHANGELOG
26
+ - MIT-LICENSE
27
+ - README
28
+ - Rakefile
29
+ - VERSION
30
+ - init.rb
31
+ - lib/later_dude.rb
32
+ - tasks/distribution.rb
33
+ - tasks/documentation.rb
34
+ - tasks/testing.rb
35
+ has_rdoc: true
36
+ homepage: http://github.com/clemens/later_dude
37
+ licenses: []
38
+
39
+ post_install_message:
40
+ rdoc_options:
41
+ - --charset=UTF-8
42
+ require_paths:
43
+ - lib
44
+ required_ruby_version: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: "0"
49
+ version:
50
+ required_rubygems_version: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: "0"
55
+ version:
56
+ requirements: []
57
+
58
+ rubyforge_project:
59
+ rubygems_version: 1.3.5
60
+ signing_key:
61
+ specification_version: 3
62
+ summary: Small calendar helper plugin for Rails with i18n support
63
+ test_files:
64
+ - test/calendar_helper_test.rb
65
+ - test/calendar_test.rb
66
+ - test/test_helper.rb