event_calendar 0.0.0 → 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -24,7 +24,7 @@
24
24
  :-webkit-border-top-right-radius= 0px
25
25
  :border-top-right-radius= 0px
26
26
 
27
- .calendar
27
+ .event_calendar
28
28
 
29
29
  table
30
30
  border-collapse: collapse
@@ -92,7 +92,7 @@
92
92
  !label_background_color = #EBEDE2
93
93
  !label_color = #000
94
94
 
95
- .calendar
95
+ .event_calendar
96
96
  border-bottom= !border_width "solid" !border_color
97
97
 
98
98
  table td
@@ -1,71 +1,71 @@
1
- div.calendar.send(calendar.date.strftime('%B').downcase).send("#{calendar.id}!") do
1
+ div.event_calendar.send(event_calendar.date.strftime('%B').downcase).send("#{event_calendar.id}!") do
2
2
  div.header do
3
3
  table do
4
4
  tbody do
5
5
  tr.navigation do
6
6
  td.previous_month(:colspan => 2) do
7
- a(:rel => 'previous', :href => calendar.navigation_url.call(calendar.date.last_month)) do
8
- calendar.evaluate_date_format_option(:navigation_label, calendar.date.last_month, calendar.date)
7
+ a(:rel => 'previous', :href => event_calendar.navigation_url.call(event_calendar.date.last_month)) do
8
+ event_calendar.evaluate_date_format_option(:navigation_label, event_calendar.date.last_month, event_calendar.date)
9
9
  end
10
10
  end
11
- td.current_month calendar.evaluate_date_format_option(:header_label, calendar.date), :colspan => 3
11
+ td.current_month event_calendar.evaluate_date_format_option(:header_label, event_calendar.date), :colspan => 3
12
12
  td.next_month(:colspan => 2) do
13
- a(:rel => 'next', :href => calendar.navigation_url.call(calendar.date.next_month)) do
14
- calendar.evaluate_date_format_option(:navigation_label, calendar.date.next_month, calendar.date)
13
+ a(:rel => 'next', :href => event_calendar.navigation_url.call(event_calendar.date.next_month)) do
14
+ event_calendar.evaluate_date_format_option(:navigation_label, event_calendar.date.next_month, event_calendar.date)
15
15
  end
16
16
  end
17
17
  end
18
18
  tr.labels do
19
- calendar.weeks.first.each_with_index do |day, index|
19
+ event_calendar.weeks.first.each_with_index do |day, index|
20
20
  day_label = td.day
21
21
  day_label = day_label.send(day.strftime('%A').downcase)
22
22
  day_label = day_label.today if day.cwday == Time.now.to_date.cwday
23
- day_label.label calendar.evaluate_date_format_option(:header_day_label, day)
23
+ day_label.label event_calendar.evaluate_date_format_option(:header_day_label, day)
24
24
  end
25
25
  end
26
26
  end
27
27
  end
28
28
  end
29
29
  div.body do
30
- calendar.weeks.each do |week|
31
- div.week.send("#{calendar.id}_week_#{week.first}_#{week.last}!") do
32
- table.send("#{calendar.id}_labels_#{week.first}_#{week.last}!") do
30
+ event_calendar.weeks.each do |week|
31
+ div.week.send("#{event_calendar.id}_week_#{week.first}_#{week.last}!") do
32
+ table.send("#{event_calendar.id}_labels_#{week.first}_#{week.last}!") do
33
33
  tbody do
34
34
  tr.labels do
35
35
  week.each do |day|
36
36
  day_label = td.day
37
- day_label = day_label.send(day.month == calendar.date.month ? 'current_month' : day < calendar.date ? 'previous_month' : 'next_month')
37
+ day_label = day_label.send(day.month == event_calendar.date.month ? 'current_month' : day < event_calendar.date ? 'previous_month' : 'next_month')
38
38
  day_label = day_label.send([6, 7].include?(day.cwday) ? 'weekend' : 'weekday')
39
39
  day_label = day_label.send(day.strftime('%A').downcase)
40
40
  day_label = day_label.send(day.strftime('%B').downcase)
41
41
  day_label = day_label.send("day_#{day.strftime('%d').gsub(/^0/, '')}")
42
42
  day_label = day_label.today if day == Time.now.to_date
43
- day_label = day_label.scheduled if calendar.events.any? { |event| event.start_date <= day && event.end_date >= day }
44
- day_label.label calendar.evaluate_date_format_option(:day_label, day)
43
+ day_label = day_label.scheduled if event_calendar.events.any? { |event| event.start_date <= day && event.end_date >= day }
44
+ day_label.label event_calendar.evaluate_date_format_option(:day_label, day)
45
45
  end
46
46
  end
47
47
  end
48
48
  end
49
- div.days.send("#{calendar.id}_days_#{week.first}_#{week.last}!") do
50
- table.grid.send("#{calendar.id}_grid_#{week.first}_#{week.last}!") do
49
+ div.days.send("#{event_calendar.id}_days_#{week.first}_#{week.last}!") do
50
+ table.grid.send("#{event_calendar.id}_grid_#{week.first}_#{week.last}!") do
51
51
  tbody do
52
52
  tr do
53
53
  week.each do |day|
54
54
  day_grid = td.day
55
- day_grid = day_grid.send(day.month == calendar.date.month ? 'current_month' : day < calendar.date ? 'previous_month' : 'next_month')
55
+ day_grid = day_grid.send(day.month == event_calendar.date.month ? 'current_month' : day < event_calendar.date ? 'previous_month' : 'next_month')
56
56
  day_grid = day_grid.send([6, 7].include?(day.cwday) ? 'weekend' : 'weekday')
57
57
  day_grid = day_grid.send(day.strftime('%A').downcase)
58
58
  day_grid = day_grid.send(day.strftime('%B').downcase)
59
59
  day_grid = day_grid.send("day_#{day.strftime('%d').gsub(/^0/, '')}")
60
60
  day_grid = day_grid.today if day == Time.now.to_date
61
- day_grid = day_grid.scheduled if calendar.events.any? { |event| event.start_date <= day && event.end_date >= day }
62
- day_grid.send("#{calendar.id}_day_#{day}!") {}
61
+ day_grid = day_grid.scheduled if event_calendar.events.any? { |event| event.start_date <= day && event.end_date >= day }
62
+ day_grid.send("#{event_calendar.id}_day_#{day}!") {}
63
63
  end
64
64
  end
65
65
  end
66
66
  end
67
67
  unless week.events.empty?
68
- table.events.send("#{calendar.id}_events_#{week.first}_#{week.last}!") do
68
+ table.events.send("#{event_calendar.id}_events_#{week.first}_#{week.last}!") do
69
69
  tbody do
70
70
  tr.grid do
71
71
  week.each { |day| td('') }
@@ -78,7 +78,7 @@ div.calendar.send(calendar.date.strftime('%B').downcase).send("#{calendar.id}!")
78
78
  else
79
79
  html_options = { :class => 'event' }
80
80
  html_options[:colspan] = cell[:span] unless cell[:span] == 1
81
- html_options[:class] << " #{calendar.event_class}" unless calendar.event_class.nil?
81
+ html_options[:class] << " #{event_calendar.event_class}" unless event_calendar.event_class.nil?
82
82
  if cell[:continued]
83
83
  html_options[:class] << ' continuation' if cell[:event].start_date < week.first
84
84
  html_options[:class] << ' continued' if cell[:event].end_date > week.last
@@ -86,8 +86,8 @@ div.calendar.send(calendar.date.strftime('%B').downcase).send("#{calendar.id}!")
86
86
  event_id = "event_#{cell[:event].id}"
87
87
  begin
88
88
  td.send("#{event_id}!", html_options) do
89
- calendar.event_output.call(cell[:event]) + div.fields do
90
- calendar.event_fields.each do |field|
89
+ event_calendar.event_output.call(cell[:event]) + div.fields do
90
+ event_calendar.event_fields.each do |field|
91
91
  span(cell[:event].send(field), :title => field)
92
92
  end
93
93
  end
@@ -0,0 +1,67 @@
1
+ class EventCalendar
2
+
3
+ # Contains an array of days representing a calendar week
4
+ class Week < Array
5
+
6
+ extend ActiveSupport::Memoizable
7
+
8
+ # Accepts two arguments, an array of days and an array of events
9
+ def initialize(days, events)
10
+ super days
11
+ add_associated_events_to_days(events)
12
+ end
13
+
14
+ # Returns an array of arrays containing hashes of events to fit in an HTML calendar week row.
15
+ #
16
+ # Each hash in the array represents a table cell <tt>td</tt> when the calendar is generated.
17
+ # If the hash is empty, an empty <tt>td</tt> would be generated. Otherwise, the <tt>td</tt>
18
+ # will contain information related to the associated event. An event hash contains:
19
+ #
20
+ # :event => The event object.
21
+ # :span => The number of days this event takes up in the current week row.
22
+ # :continued => A boolean determining if this event starts before or ends after the current week.
23
+ #
24
+ # For example:
25
+ #
26
+ # puts @week.events.inspect
27
+ #
28
+ # # [
29
+ # # [{}, {}, { :event => ..., :span => 1, :continued => false }, { :event => ..., :span => 1, :continued => false}, {}, {}, {}],
30
+ # # [{}, {}, { :event => ..., :span => 1, :continued => false }, {}, {}, {}, { :event => ..., :span => 1, :continued => true }],
31
+ # # [{}, {}, { :event => ..., :span => 1, :continued => false }, {}, {}, {}, {}],
32
+ # # [{}, {}, { :event => ..., :span => 1, :continued => false }, {}, {}, {}, {}],
33
+ # # [{}, {}, { :event => ..., :span => 4, :continued => false }, {}]
34
+ # # ]
35
+ def events
36
+ events = []
37
+ day_events_index = inject({}) { |hash, day| hash.merge! day => 0 }
38
+ until all? { |day| day_events_index[day] == day.events.size }
39
+ row = []
40
+ each_with_index do |day, index|
41
+ cell_count = row.inject(0) { |sum, cell| sum += (cell.empty? ? 1 : cell[:span]) }
42
+ next if cell_count > index || cell_count >= 7
43
+
44
+ cell = {}
45
+ unless day_events_index[day] == day.events.size
46
+ cell[:event] = day.events[day_events_index[day]]
47
+ cell[:span] = cell[:event].days(first, last)
48
+ cell[:continued] = cell[:event].days != cell[:span]
49
+ day_events_index[day] += 1
50
+ end
51
+ row << cell
52
+ end
53
+ events << row
54
+ end
55
+ events
56
+ end
57
+ memoize :events
58
+
59
+ protected
60
+
61
+ # Loops through each day in this week and adds any associated events to its <tt>:events</tt> array.
62
+ def add_associated_events_to_days(events)
63
+ each { |day| day.events = events.select { |event| event.start_date == day || (event.start_date < day && event.end_date >= day && day == first) } }
64
+ end
65
+
66
+ end
67
+ end
@@ -1,28 +1,26 @@
1
1
  require 'fileutils'
2
2
 
3
- CALENDAR_ROOT = File.join(File.dirname(__FILE__), '..', '..')
4
- ASSETS_ROOT = File.join(CALENDAR_ROOT, 'assets')
5
- SANDBOX_ROOT = File.join(CALENDAR_ROOT, 'sandbox')
3
+ EVENT_CALENDAR_ROOT = File.join(File.dirname(__FILE__), '..', '..')
4
+ ASSETS_ROOT = File.join(EVENT_CALENDAR_ROOT, 'assets')
5
+ SANDBOX_ROOT = File.join(EVENT_CALENDAR_ROOT, 'sandbox')
6
6
 
7
- namespace :calendar do
7
+ namespace :event_calendar do
8
8
  namespace :generate do
9
- desc 'Generates css for the calendar'
9
+ desc 'Generates css for the event calendar'
10
10
  task :css => :build_css do
11
- puts File.read(File.join(ASSETS_ROOT, 'stylesheets', 'calendar.css'))
11
+ puts File.read(File.join(ASSETS_ROOT, 'stylesheets', 'event_calendar.css'))
12
12
  end
13
13
 
14
- desc 'Generates js for the calendar'
14
+ desc 'Generates js for the event calendar'
15
15
  task :js do
16
- puts File.read(File.join(ASSETS_ROOT, 'javascripts', 'calendar.js'))
16
+ puts File.read(File.join(ASSETS_ROOT, 'javascripts', 'event_calendar.prototype.js'))
17
17
  end
18
18
 
19
- desc 'Creates a sandbox in the gem root for testing'
19
+ desc 'Creates a sandbox in the current working directory for testing'
20
20
  task :sandbox => [:build_css] do
21
- $:.unshift(File.join(CALENDAR_ROOT, 'lib'))
21
+ $:.unshift(File.join(EVENT_CALENDAR_ROOT, 'lib'))
22
22
  require 'timecop'
23
- require 'active_support'
24
- require 'markaby'
25
- require 'calendar'
23
+ require 'event_calendar'
26
24
 
27
25
  FileUtils.mkdir(SANDBOX_ROOT) unless File.exists?(SANDBOX_ROOT)
28
26
  FileUtils.cp_r(File.join(ASSETS_ROOT, '.'), SANDBOX_ROOT)
@@ -42,7 +40,7 @@ namespace :calendar do
42
40
  Event.new('Event 4 has a longer title', 3.days.from_now, 5.days.from_now),
43
41
  Event.new('Event 5 spans across multiple weeks', 4.days.from_now, 12.days.from_now)
44
42
  ]
45
- @calendar = Calendar.new(Time.now.year, Time.now.month, :events => events)
43
+ @event_calendar = EventCalendar.new(Time.now.year, Time.now.month, :events => events)
46
44
  end
47
45
 
48
46
  File.open(File.join(SANDBOX_ROOT, 'index.html'), 'w+') do |file|
@@ -50,24 +48,24 @@ namespace :calendar do
50
48
  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
51
49
  <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
52
50
  <head>
53
- <title>Calendar</title>
51
+ <title>Event Calendar</title>
54
52
  <meta http-equiv="Content-Type" content="application/xhtml+xml; charset=UTF-8" />
55
- <link href="stylesheets/yui-2.7.0.css" media="screen" rel="stylesheet" type="text/css" />
56
- <link href="stylesheets/calendar.css" media="screen" rel="stylesheet" type="text/css" />
57
- <script src="javascripts/prototype-1.6.1.js" type="text/javascript"></script>
58
- <script src="javascripts/calendar.js" type="text/javascript"></script>
53
+ <link href="http://yui.yahooapis.com/combo?2.7.0/build/reset-fonts-grids/reset-fonts-grids.css&amp;2.7.0/build/base/base-min.css" media="screen" rel="stylesheet" type="text/css" />
54
+ <link href="stylesheets/event_calendar.css" media="screen" rel="stylesheet" type="text/css" />
55
+ <script src="http://ajax.googleapis.com/ajax/libs/prototype/1.6.1.0/prototype.js" type="text/javascript"></script>
56
+ <script src="javascripts/event_calendar.prototype.js" type="text/javascript"></script>
59
57
  <script type="text/javascript">
60
58
  //<![CDATA[
61
59
  document.observe('dom:loaded', function() {
62
- $$('.calendar').each(function(element) {
63
- new Calendar(element);
60
+ $$('.event_calendar').each(function(element) {
61
+ new EventCalendar(element);
64
62
  });
65
63
  });
66
64
  //]]>
67
65
  </script>
68
66
  </head>
69
67
  <body>
70
- #{@calendar}
68
+ #{@event_calendar}
71
69
  </body>
72
70
  </html>
73
71
  EOF
@@ -78,8 +76,8 @@ namespace :calendar do
78
76
  require 'haml'
79
77
  require 'sass/engine'
80
78
 
81
- sass = File.join(CALENDAR_ROOT, 'lib', 'calendar', 'stylesheet.sass')
82
- File.open(File.join(ASSETS_ROOT, 'stylesheets', 'calendar.css'), 'w+') do |file|
79
+ sass = File.join(EVENT_CALENDAR_ROOT, 'lib', 'event_calendar', 'stylesheet.sass')
80
+ File.open(File.join(ASSETS_ROOT, 'stylesheets', 'event_calendar.css'), 'w+') do |file|
83
81
  file.write Sass::Engine.new(File.read(sass)).to_css
84
82
  end
85
83
  end
data/test/date_test.rb ADDED
@@ -0,0 +1,14 @@
1
+ require 'test_helper'
2
+
3
+ class DateTest < Test::Unit::TestCase
4
+
5
+ context 'a date' do
6
+ setup { @date = Time.now.to_date }
7
+
8
+ should 'have an events attr_accessor' do
9
+ assert @date.respond_to?(:events)
10
+ assert @date.respond_to?(:events=)
11
+ end
12
+ end
13
+
14
+ end
@@ -0,0 +1,82 @@
1
+ require 'test_helper'
2
+
3
+ class EventCalendarTest < Test::Unit::TestCase
4
+
5
+ context 'an EventCalendar instance' do
6
+ setup do
7
+ Timecop.freeze(Date.civil(2009, 10, 6)) do
8
+ @event_calendar = EventCalendar.new(Time.now.year, Time.now.month,
9
+ :events => events,
10
+ :date_format_string => '%B',
11
+ :date_format_symbol => :year,
12
+ :date_format_proc => proc { |date| date.year },
13
+ :date_format_integer => 2009
14
+ )
15
+ end
16
+ end
17
+
18
+ should 'return the first day of its specified month and year when calling date' do
19
+ assert_equal Date.civil(2009, 10, 1), @event_calendar.date
20
+ end
21
+
22
+ context 'when calling evaluate_date_format_option it' do
23
+ setup { @date = @event_calendar.date }
24
+
25
+ should 'evaluate strings' do
26
+ assert_equal @date.strftime('%B'), @event_calendar.evaluate_date_format_option(:date_format_string, @date)
27
+ end
28
+
29
+ should 'evaluate symbols' do
30
+ assert_equal @date.year, @event_calendar.evaluate_date_format_option(:date_format_symbol, @date)
31
+ end
32
+
33
+ should 'evaluate procs' do
34
+ assert_equal @date.year, @event_calendar.evaluate_date_format_option(:date_format_proc, @date)
35
+ end
36
+
37
+ should 'return all other values' do
38
+ assert_equal 2009, @event_calendar.evaluate_date_format_option(:date_format_integer, @date)
39
+ end
40
+ end
41
+
42
+ context 'when calling method_missing it' do
43
+ should 'read an option' do
44
+ assert_equal @event_calendar.options[:date_format_string], @event_calendar.date_format_string
45
+ end
46
+
47
+ should 'write an option' do
48
+ assert_equal @event_calendar.options[:date_format_string], @event_calendar.date_format_string
49
+ @event_calendar.date_format_string = 'testing'
50
+ assert_equal 'testing', @event_calendar.options[:date_format_string]
51
+ end
52
+
53
+ should 'call super if an option does not exist' do
54
+ assert_raises NoMethodError do
55
+ @event_calendar.missing_option
56
+ end
57
+ end
58
+ end
59
+
60
+ context 'when rendering it' do
61
+ should 'render the default template' do
62
+ assert_match /id="#{@event_calendar.id}"/, @event_calendar.to_s
63
+ end
64
+
65
+ should 'read and render a different template file' do
66
+ @event_calendar.template = File.join(fixtures_path, 'template.mab')
67
+ assert_equal '<div class="event_calendar"></div>', @event_calendar.to_s
68
+ end
69
+
70
+ should 'render a template string' do
71
+ @event_calendar.template = "div.testing do\nend"
72
+ assert_equal '<div class="testing"></div>', @event_calendar.to_s
73
+ end
74
+ end
75
+
76
+ should 'return an array of weeks containing seven days when calling weeks' do
77
+ @weeks = (Date.civil(2009, 9, 27)..Date.civil(2009, 10, 31)).to_a.in_groups_of(7).collect { |week| EventCalendar::Week.new(week, @event_calendar.events) }
78
+ assert_equal @weeks, @event_calendar.weeks
79
+ end
80
+ end
81
+
82
+ end
@@ -0,0 +1,59 @@
1
+ require 'test_helper'
2
+
3
+ class EventTest < Test::Unit::TestCase
4
+
5
+ context 'an EventCalendar::Event' do
6
+ setup do
7
+ @event_title = 'testing'
8
+ @start_time = Time.local(2010, 1, 3, 10, 5, 0)
9
+ @end_time = Time.local(2010, 1, 5, 11, 35, 0)
10
+ @raw_event = Event.new(@event_title, @start_time, @end_time)
11
+ @event = EventCalendar::Event.new(@raw_event, EventCalendar.default_options.merge({
12
+ :event_title_symbol => :title,
13
+ :event_title_string => 'string',
14
+ :event_title_proc => proc { |event| event.title + '_proc' }
15
+ }))
16
+ end
17
+
18
+ should 'have a start_date' do
19
+ assert_equal @start_time.to_date, @event.start_date
20
+ end
21
+
22
+ should 'have an end_date' do
23
+ assert_equal @end_time.to_date, @event.end_date
24
+ end
25
+
26
+ should 'return the number of days it spans' do
27
+ assert_equal 3, @event.days
28
+ end
29
+
30
+ should 'return the number of days it spans with a starting week offset' do
31
+ assert_equal 2, @event.days(@event.start_date + 1.day)
32
+ end
33
+
34
+ should 'return the number of days it spans with an ending week offset' do
35
+ assert_equal 2, @event.days(@event.start_date, @event.end_date - 1.day)
36
+ end
37
+
38
+ should 'return the number of days it spans with a starting and ending week offset' do
39
+ assert_equal 1, @event.days(@event.start_date + 1.day, @event.end_date - 1.day)
40
+ end
41
+
42
+ should 'evaluate missing attributes as symbols' do
43
+ assert_equal @event_title, @event.title_symbol
44
+ end
45
+
46
+ should 'evaluate missing attributes as objects' do
47
+ assert_equal 'string', @event.title_string
48
+ end
49
+
50
+ should 'evaluate missing attributes as procs and pass the raw event' do
51
+ assert_equal @event_title + '_proc', @event.title_proc
52
+ end
53
+
54
+ should 'delegate all other missing attributes to the raw event' do
55
+ assert_equal @raw_event.id, @event.id
56
+ end
57
+ end
58
+
59
+ end