school_days 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt ADDED
@@ -0,0 +1,4 @@
1
+ === 0.0.1 2011-01-21
2
+
3
+ * 1 major enhancement:
4
+ * Initial release
data/Manifest.txt ADDED
@@ -0,0 +1,28 @@
1
+ History.txt
2
+ Manifest.txt
3
+ PostInstall.txt
4
+ README.rdoc
5
+ Rakefile
6
+ lib/school_days.rb
7
+ lib/extensions/date.rb
8
+ lib/extensions/fixnum.rb
9
+ lib/school_days/calculator.rb
10
+ lib/school_days/config.rb
11
+ rails_generators/school_days_config/USAGE
12
+ rails_generators/school_days_config/school_days_config_generator.rb
13
+ rails_generators/school_days_config/templates/school_days.rb
14
+ rails_generators/school_days_config/templates/school_days.yml
15
+ script/console
16
+ script/destroy
17
+ script/generate
18
+ test/fixtures/double_session_test.yml
19
+ test/fixtures/invalid_config_test.yml
20
+ test/fixtures/simple_test.yml
21
+ test/test_config.rb
22
+ test/test_date_extensions.rb
23
+ test/test_fixnum_extensions.rb
24
+ test/test_generator_helper.rb
25
+ test/test_helper.rb
26
+ test/test_school_days.rb
27
+ test/test_school_days_config_generator.rb
28
+ test/test_school_days_calculator.rb
data/PostInstall.txt ADDED
@@ -0,0 +1,2 @@
1
+
2
+ For more information on school_days, see http://github.com/rwilcox/school_days
data/README.rdoc ADDED
@@ -0,0 +1,136 @@
1
+ = school_days
2
+
3
+ * http://github.com/rwilcox/school_days
4
+
5
+ == DESCRIPTION:
6
+
7
+ Like Business Time (https://github.com/bokmann/business_time), this gem has support for domain specific dates.
8
+
9
+ Unlike Business Time, School Days focuses on the demands of keeping a school calendar
10
+
11
+
12
+ == FEATURES:
13
+
14
+ With School Days you can:
15
+
16
+ * 1.days.from_now.is_school_day
17
+ * Date.civil(2011, 05, 31).school_night?
18
+ * 2.school_days.from_now
19
+
20
+ In the school days configuration you can
21
+ * Set the start and end date of a school session (as in: when does school break for summer, then resume)
22
+ * Be able to set exceptional "in" days ("Normally, there is no school on Saturday, but we need to in this case")
23
+ * Be able to set exceptional "out" days (Weather days, for example)
24
+
25
+ == PROBLEMS
26
+ School Days only handles school day calculations. Unlike business time, which includes hour and minute level calculations, School Days deals only with days. This is a reasonable tradeoff, and does have some implementation advantages.
27
+
28
+ For example, most school districts define a school day as being at least 4 hours of instructional time. If we supported sub-day calculations then the possibility of school being cancelled after 2 hours exists, and school days vs school hours getting unsynced is a possibility.
29
+
30
+ Yes, I acknowledge that this might be a problem, as technically a school year is N hours long (not M days). Schools might gain an extra day, in time of prolonged emergency, by adding an extra few minutes onto their school day (for the last 10 weeks, for example).
31
+
32
+ This gem focuses on the naive solution of managing this at the day level, and avoids this kind of accounting practice.
33
+
34
+ Another potential problem is using the N.school_day.before/after syntax will not handle exceptionally included days that land outside the earliest start date and latest end date defined in the school sessions section.
35
+
36
+ If you really need to define an exceptional included day outside the school year please submit a patch... else just extend your start dates or end dates. Yes, I do consider this a major drawback, and may reconsider this feature in the future... right now I don't need it enough to hold up release of this gem.
37
+
38
+
39
+ == SYNOPSIS:
40
+
41
+ Yes, I know a lot about school calendars and how they operate. My mom is a school teacher (Hi Mom!!!), who taught in the same school us kids went to... so I learned a lot about school scheduling.
42
+
43
+ I learned even more over the years when various disasters (snow storms, fires) happened and I watched the school calendar being adjusted in various ways.
44
+
45
+ == REQUIREMENTS:
46
+
47
+ This gem is meant to be used in a Rails (2) project. It will probably work in a Rails 3 environment, but this hasn't been tested yet (and I know the generator will not work)
48
+
49
+ == INSTALL:
50
+
51
+ gem install school_days
52
+
53
+ in your Rails project, execute the generator to get a yaml file that gives you calendar configuration options
54
+
55
+ script/generate school_days_config
56
+
57
+ This will install an initializer and a yaml file (config/school_days.yml).
58
+
59
+
60
+ == CONFIGURING SCHOOL DAYS:
61
+
62
+ School days are a fickle beast. This gem has intelligent defaults, but I've also seen an amazing amount of variety in school calendars
63
+
64
+ config/school_days.yml has one top level key (school_days) with the following subkeys:
65
+
66
+ * school_sessions
67
+
68
+ A school might have one session (think a school that runs from August until June, with a 3 month summer vacation). A school might also run on an increasingly popular 10 week session with 2 week vacation, running all year.
69
+
70
+ The school sessions structure also allows this gem to be used by higher education (semester, trimester, and quarter system schools), in addition to primary/secondary education.
71
+
72
+ Each subkey in the sessions section are named (arbitrary name), but each subkey must have a start_date and end_date.
73
+
74
+
75
+ * exceptions
76
+
77
+ By default, school_days assumes that every week day during a session is a school day. This is incorrect, and you can configure these exceptions here.
78
+
79
+ This key has two subkeys:
80
+
81
+ * holidays
82
+
83
+ This is a flat list of dates where there is no class
84
+
85
+ * included_days
86
+
87
+ I've seen schools have school days on a Saturday, in very rare circumstances. included_days list days where you would normally not have school (weekends)... but you do this year.
88
+
89
+ Days listed in any exceptions subkey have extreme override abilities - you could set your school year to end on June 1, 2011... but an included_day which includes June 2, 2011 means that June 2, 2011 is a school day.
90
+
91
+ == WHAT THIS GEM DOES NOT DO:
92
+
93
+ This gem is meant for ONE school calendar. This gem does not address the situation where you are (for example) writing a state level website and have multiple school districts each with their own calendar.
94
+
95
+ If you do need such functionality, I suggest you make a pull request.
96
+
97
+ It also does not deal with in-service days. This gem is only worried about days children are in school.
98
+
99
+ This gem also relies on manual configuration every year. It can not possibly know the school holidays of your individual school, so it doesn't try. I realize this requires someone to remember to update the configuration to put new school information in.
100
+
101
+
102
+ == NOTE ON PATCHES/PULL REQUESTS:
103
+
104
+ * Fork the project.
105
+ * Make your feature addition or bug fix.
106
+ * Add tests for it. This is important so I don’t break it in a future version unintentionally.
107
+ * Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
108
+ * Send me a pull request. Bonus points for topic branches.
109
+
110
+ * I use GitFlow <https://github.com/nvie/gitflow> to develop, so bonus points for that.
111
+
112
+
113
+ == LICENSE:
114
+
115
+ (The MIT License)
116
+
117
+ Copyright (c) 2011 Ryan Wilcox
118
+
119
+ Permission is hereby granted, free of charge, to any person obtaining
120
+ a copy of this software and associated documentation files (the
121
+ 'Software'), to deal in the Software without restriction, including
122
+ without limitation the rights to use, copy, modify, merge, publish,
123
+ distribute, sublicense, and/or sell copies of the Software, and to
124
+ permit persons to whom the Software is furnished to do so, subject to
125
+ the following conditions:
126
+
127
+ The above copyright notice and this permission notice shall be
128
+ included in all copies or substantial portions of the Software.
129
+
130
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
131
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
132
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
133
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
134
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
135
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
136
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,26 @@
1
+ require 'rubygems'
2
+ gem 'hoe', '>= 2.1.0'
3
+ require 'hoe'
4
+ require 'fileutils'
5
+ require './lib/school_days'
6
+
7
+ Hoe.plugin :newgem
8
+ # Hoe.plugin :website
9
+ # Hoe.plugin :cucumberfeatures
10
+
11
+ # Generate all the Rake tasks
12
+ # Run 'rake -T' to see list of generated tasks (from gem root directory)
13
+ $hoe = Hoe.spec 'school_days' do
14
+ self.developer 'Ryan Wilcox', 'rwilcox@wilcoxd.com'
15
+ self.post_install_message = 'PostInstall.txt' # TODO remove if post-install message not required
16
+ self.rubyforge_name = self.name # TODO this is default value
17
+ self.extra_deps = [['activesupport','>= 2.0.2'], ["shoulda", "=2.11.3"]]
18
+
19
+ end
20
+
21
+ require 'newgem/tasks'
22
+ Dir['tasks/**/*.rake'].each { |t| load t }
23
+
24
+ # TODO - want other tests/tasks run by default? Add them to the list
25
+ # remove_task :default
26
+ # task :default => [:spec, :features]
@@ -0,0 +1,36 @@
1
+
2
+
3
+ module SchoolDays
4
+ module DateExtensions
5
+ def school_day?
6
+ # first, check the exceptional days and see if we can get a quick match
7
+ return false if SchoolDays.config.holiday_exceptions.include? self
8
+ return true if SchoolDays.config.included_day_exceptions.include? self
9
+
10
+ # Now, the laborous part.
11
+
12
+ # First, check to see if we are a week day
13
+ weekday = [1,2,3,4,5].include?(self.wday)
14
+ has_school = false
15
+
16
+ if weekday
17
+ has_school = true
18
+
19
+ # ok, now check to see if this weekday is in the school sessions
20
+ has_school = SchoolDays.config.school_sessions.any? do |current_session|
21
+ current_session[:start_date] <= self && current_session[:end_date] >= self
22
+ end
23
+ end
24
+
25
+ has_school
26
+ end
27
+
28
+ def school_night?
29
+ (self + 1).school_day?
30
+ end
31
+ end
32
+ end
33
+
34
+ class Date
35
+ include SchoolDays::DateExtensions
36
+ end
@@ -0,0 +1,18 @@
1
+ # hook into fixnum so we can say things like:
2
+ # 5.school_days.from_now
3
+ # 7.school_days.ago
4
+
5
+ module SchoolDays
6
+ module FixnumExtensions
7
+ def school_days
8
+ SchoolDays::Calculator.new(self)
9
+ end
10
+ alias :school_day :school_days
11
+
12
+ end
13
+ end
14
+
15
+ class Fixnum
16
+ include SchoolDays::FixnumExtensions
17
+ end
18
+
@@ -0,0 +1,60 @@
1
+
2
+ module SchoolDays
3
+ class Calculator
4
+ def initialize(days)
5
+ @days = days
6
+ end
7
+
8
+ def after(time = Time.now)
9
+ # example: 2.school_days.after(tuesday)
10
+ date = time
11
+ date = time.to_date if time.is_a?(Time)
12
+
13
+ @days.times do
14
+ begin
15
+ date = date + 1
16
+ raise SchoolDays::DateNotInSchoolCalendar unless is_in_school_year?(date)
17
+ # (if we are not in the school year at all, stop calculating, because
18
+ # once we go outside we'll never find another school day. WD-rpw 01-26-2011)
19
+ end until date.school_day?
20
+ end
21
+ date
22
+ end
23
+ alias_method :from_now, :after
24
+
25
+ def before(time = Time.now)
26
+ # example: 2.school_days.after(tuesday)
27
+ date = time
28
+ date = time.to_date if time.is_a?(Time)
29
+
30
+ @days.times do
31
+ begin
32
+ date = date - 1
33
+ raise SchoolDays::DateNotInSchoolCalendar unless is_in_school_year?(date)
34
+ # (if we are not in the school year at all, stop calculating, because
35
+ # once we go outside we'll never find another school day. WD-rpw 01-26-2011)
36
+ end until date.school_day?
37
+ end
38
+ date
39
+ end
40
+ alias_method :until, :before
41
+
42
+ # a new method, more of a helper for this API then an actual public method...
43
+ # however, this will return the session a date is associated with, or nil
44
+ # if this falls totally outside the known school year
45
+ def school_session_for_date(date)
46
+ SchoolDays.config.school_sessions.detect do |school_session|
47
+ school_session[:start_date] < date && date < school_session[:end_date]
48
+ end
49
+ end
50
+
51
+ # another semi-private method. TODO: it seems useful to expose this, but how?
52
+ def is_in_school_year?(date)
53
+ # included exceptional days can be outside the school year
54
+ return true if SchoolDays.config.included_day_exceptions.include? date
55
+
56
+ # ok, now check ranges
57
+ SchoolDays.config.school_year_start < date && date < SchoolDays.config.school_year_end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,66 @@
1
+ require 'singleton'
2
+ require 'yaml'
3
+ require 'date'
4
+
5
+ module SchoolDays
6
+
7
+ # controls the behavior of this gem. Currently this gem only supports
8
+ # loading information from a YAML file (TODO: implement programatic ways)
9
+ # ... well, you could do it, but I don't care about that code path yet...
10
+ class ConfigBase
11
+ attr_accessor :school_sessions
12
+ attr_accessor :holiday_exceptions
13
+ attr_accessor :included_day_exceptions
14
+
15
+ def reset
16
+ self.school_sessions = {}
17
+ self.holiday_exceptions = []
18
+ self.included_day_exceptions = []
19
+ end
20
+
21
+ def load(filename)
22
+ reset
23
+ data = YAML::load(File.open(filename))
24
+ sessions = data["school_days"]["school_sessions"]
25
+
26
+ self.school_sessions = sessions.collect do |session_name, session_value|
27
+ if session_value["start_date"].nil? || session_value["end_date"].nil?
28
+ raise "start_date or end_date is blank for #{session_name} in #{filename}"
29
+ end
30
+
31
+ {:start_date => Date.parse(session_value["start_date"]),
32
+ :end_date => Date.parse(session_value["end_date"])}
33
+ end
34
+
35
+ data["school_days"]["exceptions"]["holidays"].each do |holiday|
36
+ self.holiday_exceptions << Date.parse(holiday)
37
+ end
38
+
39
+ data["school_days"]["exceptions"]["included_days"].each do |extra_day|
40
+ self.included_day_exceptions << Date.parse(extra_day)
41
+ end
42
+ end
43
+
44
+ def school_year_start
45
+ res = self.school_sessions.min do |a, b|
46
+ a[:start_date] <=> b[:start_date]
47
+ end
48
+ res[:start_date]
49
+ end
50
+
51
+ def school_year_end
52
+ res = self.school_sessions.max do |a, b|
53
+ a[:end_date] <=> b[:end_date]
54
+ end
55
+ res[:end_date]
56
+ end
57
+ end
58
+
59
+ class Config < ConfigBase
60
+ include Singleton
61
+ end
62
+
63
+ def self.config
64
+ Config.instance
65
+ end
66
+ end
@@ -0,0 +1,17 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ require File.dirname(__FILE__) + '/school_days/config'
5
+ require File.dirname(__FILE__) + '/school_days/calculator'
6
+
7
+ require File.dirname(__FILE__) + '/extensions/date'
8
+ require File.dirname(__FILE__) + '/extensions/fixnum'
9
+
10
+ module SchoolDays
11
+ class DateNotInSchoolCalendar < RuntimeError
12
+ end
13
+ end
14
+
15
+ module SchoolDays
16
+ VERSION = '1.0.0'
17
+ end
@@ -0,0 +1,5 @@
1
+ Description:
2
+ Creates a school_days.yml config file and an initializer that loads the school days gem into your Rails app.
3
+
4
+ Usage:
5
+ script/generate school_days_config
@@ -0,0 +1,17 @@
1
+ class SchoolDaysConfigGenerator < Rails::Generator::Base
2
+ def manifest
3
+ record do |m|
4
+ m.file('school_days.rb', "config/initializers/school_days.rb")
5
+ m.file('school_days.yml', "config/school_days.yml")
6
+ end
7
+ end
8
+
9
+ protected
10
+ def banner
11
+ <<-EOS
12
+ Creates a school_days.yml config file and an initializer that loads the school days gem into your Rails app.
13
+
14
+ USAGE: #{$0} #{spec.name} name
15
+ EOS
16
+ end
17
+ end
@@ -0,0 +1,3 @@
1
+ SchoolDays.config.load("#{RAILS_ROOT}/config/school_days.yml")
2
+
3
+ # TODO: manually loading, like Business Time?? WD-rpw 01-21-2011
@@ -0,0 +1,14 @@
1
+ school_days:
2
+ school_sessions:
3
+ school_year:
4
+ start_date: Aug 29, 2011
5
+ end_date: June 1, 2012
6
+ exceptions:
7
+ holidays:
8
+ - Jan 01, 2010
9
+ - July 4th, 2010
10
+ - December 25th, 2010
11
+ included_days:
12
+ - Aug 29, 2011
13
+ - May 29, 2011
14
+
data/script/console ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+ # File: script/console
3
+ irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
4
+
5
+ libs = " -r irb/completion"
6
+ # Perhaps use a console_lib to store any extra methods I may want available in the cosole
7
+ # libs << " -r #{File.dirname(__FILE__) + '/../lib/console_lib/console_logger.rb'}"
8
+ libs << " -r #{File.dirname(__FILE__) + '/../lib/school_days.rb'}"
9
+ puts "Loading school_days gem"
10
+ exec "#{irb} #{libs} --simple-prompt"
data/script/destroy ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/destroy'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
14
+ RubiGen::Scripts::Destroy.new.run(ARGV)
data/script/generate ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/generate'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
14
+ RubiGen::Scripts::Generate.new.run(ARGV)
@@ -0,0 +1,15 @@
1
+ school_days:
2
+ school_sessions:
3
+ fall_semester:
4
+ start_date: Aug 29, 2011
5
+ end_date: December 15, 2011
6
+ spring_semester:
7
+ start_date: January 15, 2012
8
+ end_date: May 15, 2012
9
+ exceptions:
10
+ holidays:
11
+ - Jan 23, 2012
12
+ - May 1, 2012
13
+ included_days:
14
+ - Aug 27, 2011
15
+ - May 12, 2012
@@ -0,0 +1,16 @@
1
+ school_days:
2
+ school_sessions:
3
+ fall_semester:
4
+ start_date: Aug 29, 2011
5
+ end_date: December 15, 2012
6
+ spring_semester:
7
+ start_start: January 15, 2012
8
+ end_date: May 15, 2012
9
+ exceptions:
10
+ holidays:
11
+ - Jan 01, 2010
12
+ - July 4th, 2010
13
+ - December 25th, 2010
14
+ included_days:
15
+ - Aug 29, 2011
16
+ - May 29, 2011
@@ -0,0 +1,14 @@
1
+ school_days:
2
+ school_sessions:
3
+ school_year:
4
+ start_date: Aug 30, 2010
5
+ end_date: June 1, 2011
6
+ exceptions:
7
+ holidays:
8
+ - Jan 01, 2011
9
+ - July 4th, 2011
10
+ - December 25th, 2010
11
+ - May 31, 2011
12
+ included_days:
13
+ - Aug 28, 2010
14
+ - May 29, 2011
@@ -0,0 +1,69 @@
1
+ require File.dirname(__FILE__) + '/test_helper.rb'
2
+
3
+ class TestConfig < Test::Unit::TestCase
4
+ should "load a file with one session with no errors" do
5
+ conf = SchoolDays::ConfigBase.new
6
+ assert_nothing_raised(Exception) { conf.load( fixture_path() + "/simple_test.yml" ) }
7
+ end
8
+
9
+ should "load a file with two sessions correctly" do
10
+ conf = SchoolDays::ConfigBase.new
11
+ assert_nothing_raised(Exception) { conf.load( fixture_path() + "/double_session_test.yml" ) }
12
+ assert conf.school_sessions.length == 2
13
+ end
14
+
15
+ context "holiday exceptions" do
16
+ should "only include listed holiday exceptions, as Ruby Date objects" do
17
+ conf = SchoolDays::ConfigBase.new
18
+ conf.load( fixture_path() + "/simple_test.yml" )
19
+
20
+ assert_contains( conf.holiday_exceptions, Date.parse("Jan 01, 2011") )
21
+ assert_does_not_contain( conf.holiday_exceptions, Date.parse("Jan 02, 2011") )
22
+ end
23
+ end
24
+
25
+ context "exceptional included days" do
26
+ should "only include exceptional included days, as Ruby date objects" do
27
+ conf = SchoolDays::ConfigBase.new
28
+ conf.load( fixture_path() + "/simple_test.yml" )
29
+
30
+ assert_contains( conf.included_day_exceptions, Date.parse("May 29, 2011") )
31
+ assert_does_not_contain( conf.included_day_exceptions, Date.parse("May 31, 2011") )
32
+ end
33
+ end
34
+
35
+ context "when loading an invalid config file" do
36
+ should "throw a Runtime error when we're missing start or end dates for a session" do
37
+ conf = SchoolDays::ConfigBase.new
38
+ assert_raise(RuntimeError) { conf.load( fixture_path() + "/invalid_config_test.yml" ) }
39
+ end
40
+ end
41
+
42
+ context "when loading a file with one session" do
43
+ should "be able to find the start date" do
44
+ conf = SchoolDays::ConfigBase.new
45
+ conf.load( fixture_path() + "/simple_test.yml" )
46
+ assert_equal( Date.civil(2010, 8, 30).to_s, conf.school_year_start.to_s )
47
+ end
48
+
49
+ should "be able to find the end date" do
50
+ conf = SchoolDays::ConfigBase.new
51
+ conf.load( fixture_path() + "/simple_test.yml" )
52
+ assert_equal( Date.civil(2011, 6, 1).to_s, conf.school_year_end.to_s )
53
+ end
54
+ end
55
+
56
+ context "when loading a file with multiple sessions" do
57
+ should "be able to find the first start date" do
58
+ conf = SchoolDays::ConfigBase.new
59
+ conf.load( fixture_path() + "/double_session_test.yml" )
60
+ assert_equal( Date.civil(2011, 8, 29).to_s, conf.school_year_start.to_s )
61
+ end
62
+
63
+ should "be able to find the last end date" do
64
+ conf = SchoolDays::ConfigBase.new
65
+ conf.load( fixture_path() + "/double_session_test.yml" )
66
+ assert_equal( Date.civil(2012, 5, 15).to_s, conf.school_year_end.to_s )
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,114 @@
1
+ require File.dirname(__FILE__) + '/test_helper.rb'
2
+
3
+
4
+ class TestDateExtensions < Test::Unit::TestCase
5
+
6
+ context "Date instances" do
7
+ setup do
8
+ SchoolDays.config.load( fixture_path() + "/simple_test.yml" )
9
+ end
10
+
11
+ should "Date should respond to school_day?" do
12
+ assert_nothing_raised(Exception) { Date.today.school_day? }
13
+ end
14
+
15
+ should "return true for school_day if the day is a weekday" do
16
+ days = (24..28).to_a # In January 2011 these are all weedays
17
+ days.each do |day|
18
+ date = Date.civil(2011, 01, day)
19
+ assert date.school_day?
20
+ end
21
+ end
22
+
23
+ should "return false for school_day? if the day is a weekend" do
24
+ weekend = Date.civil(2011, 01, 29)
25
+ assert !weekend.school_day?
26
+ end
27
+
28
+ should "return true if the day is a school night" do
29
+ date = Date.civil(2011, 1, 27) #Thursday
30
+ assert date.school_night?
31
+ end
32
+
33
+ should "return false if the day is not a school night" do
34
+ date = Date.civil(2011, 1, 28) #Friday
35
+ assert !date.school_night?
36
+ end
37
+
38
+ should "return true before an exceptional included day" do
39
+ date = Date.civil(2011, 5, 28)
40
+ assert date.school_night?
41
+ end
42
+
43
+ should "return false for school_day? if the day is flagged as a holiday" do
44
+ days = (24..28).to_a # In January 2011 these are all weedays
45
+ days.each do |day|
46
+ date = Date.civil(2011, 5, 31) # our simple_test.yml defines this as a holiday
47
+ # May 31, 2011 is a Tuesday.
48
+
49
+ assert !date.school_day?
50
+ end
51
+ end
52
+
53
+ should "return true for school_day? if a normally off day is flagged as an included day" do
54
+ date = Date.civil(2011, 5, 29) # while it is a Sunday in 2011, it's on our list
55
+ # of exceptional included days, so it must be included
56
+
57
+ assert date.school_day?
58
+ end
59
+
60
+ should "return true when the date is equal to the start date of a session" do
61
+ d = Date.civil(2010, 8, 30) # a monday
62
+ assert d.school_day?
63
+ end
64
+
65
+ should "return true when the date is equal to the last date of a session" do
66
+ d = Date.civil(2011, 6, 1)
67
+ assert d.school_day?
68
+ end
69
+
70
+ context "when looking at a single school session" do
71
+ setup do
72
+
73
+ end
74
+
75
+ should "return false for school_day? if the date is outside the session (and not in exceptional included days)" do
76
+ date = Date.civil(2011, 6, 2) # June 2 is a Thursday in 2011... but it's also outside the school session
77
+ assert !date.school_day?(), "Day outside school session still thinks its a school day"
78
+ end
79
+
80
+ should "return true for school_day? if the date is inside the session (and not in exceptional included days)" do
81
+ date = Date.civil(2010, 10, 18)
82
+ assert date.school_day?
83
+ end
84
+ end
85
+
86
+ context "when looking at multiple school sessions" do
87
+ setup do
88
+ SchoolDays.config.load( fixture_path() + "/double_session_test.yml" )
89
+ end
90
+
91
+ should "return false for school_day? if a weekday is is outside ALL of the sessions (and not in exceptional included days)" do
92
+ date = Date.civil(2012, 6, 2) # June 2 is a Thursday in 2011... but it's also outside the school session
93
+ assert !date.school_day?(), "Day outside school session still thinks its a school day"
94
+ end
95
+
96
+ should "return false for school_day if the weekday is between school sessions" do
97
+ date = Date.civil(2012, 12, 30) # Dec 30 is a Friday in 2011... but it's in between two school sessions
98
+ assert !date.school_day?(), "Day between school sessions still thinks its a school day"
99
+ end
100
+
101
+ should "return false for school_day? if a weekend is is outside ALL of the sessions (and not in exceptional included days)" do
102
+ date = Date.civil(2012, 5, 31) # a thursday in 2012
103
+ assert !date.school_day?
104
+ end
105
+
106
+ should "return true for school_day? if the date is outside one session, but inside another" do
107
+ date = Date.civil(2012, 1, 16)
108
+ assert date
109
+ end
110
+ end
111
+
112
+ end
113
+
114
+ end
@@ -0,0 +1,94 @@
1
+ require File.dirname(__FILE__) + '/test_helper.rb'
2
+
3
+
4
+ class TestFixnumExtensions < Test::Unit::TestCase
5
+
6
+ context "when adding a school day" do
7
+ setup do
8
+ SchoolDays.config.load( fixture_path() + "/double_session_test.yml" )
9
+ end
10
+
11
+ should "move to tomorrow (if tomorrow is a school day)" do
12
+ date = Date.civil(2011, 10, 18) # a Tuesday
13
+ assert_equal( date + 1, 1.school_day.after(date) )
14
+ end
15
+
16
+ should "move to Monday (if tomorrow is a weekend)" do
17
+ date = Date.civil(2011, 10, 21) # a Friday
18
+ assert_equal( date + 3, 1.school_days.after(date) )
19
+ end
20
+
21
+ should "move forward one week when adding 5 school days" do
22
+ date = Date.civil(2011, 10, 17) # a Monday
23
+ assert_equal( date + 7, 5.school_days.after(date) )
24
+ end
25
+
26
+ should "move forward onto an exceptional day" do
27
+ date = Date.civil(2012, 5, 11)
28
+ assert_equal( date + 1, 1.school_day.after(date) )
29
+ end
30
+
31
+ should "skip exceptional holidays" do
32
+ date = Date.civil(2012, 4, 30) # is a Tuesday, but May 1 is a holiday
33
+ assert_equal( (date + 2).to_s, (1.school_day.after(date)).to_s )
34
+ # so we expect May 2
35
+ end
36
+
37
+ should "move to the next session if we have multiple sessions" do
38
+ date = Date.civil(2011, 12, 12) # a Monday before semester break
39
+ assert_equal( Date.civil(2012, 1, 17), 5.school_days.after(date) )
40
+ end
41
+
42
+ should "elegantly handle a situation where we go outside the school year" do
43
+ date = Date.civil(2012, 5, 14)
44
+ assert_raise(SchoolDays::DateNotInSchoolCalendar) { 1.school_day.after(date) }
45
+ end
46
+ end
47
+
48
+ context "when subtracting a school day" do
49
+ setup do
50
+ SchoolDays.config.load( fixture_path() + "/double_session_test.yml" )
51
+ end
52
+
53
+ should "move to yesterday (if yesterday is a school day)" do
54
+ date = Date.civil(2011, 10, 18) # a Tuesday
55
+ assert_equal( date - 1, 1.school_day.before(date) )
56
+ end
57
+
58
+ should "move to Friday (if yesterday is a weekend)" do
59
+ date = Date.civil(2011, 10, 17) # a Monday
60
+ assert_equal( date - 3, 1.school_day.before(date) )
61
+ end
62
+
63
+ should "move back one week when adding 5 school days" do
64
+ date = Date.civil(2011, 10, 21) #a Friday
65
+ assert_equal( date - 7, 5.school_days.before(date) )
66
+ end
67
+
68
+ should "move backwards onto an exceptional day" do
69
+ date = Date.civil(2012, 5, 14)
70
+
71
+ assert date.school_day?
72
+ assert_equal( Date.civil(2012, 5, 12), 1.school_day.before(date) )
73
+
74
+ end
75
+
76
+ should "skip exceptional holidays" do
77
+ date = Date.civil(2012, 1, 25)
78
+ assert_equal( Date.civil(2012, 1, 20), 2.school_days.before(date) )
79
+ end
80
+
81
+ should "move to the previous session if we have multiple sessions" do
82
+ date = Date.civil(2012, 1, 16)
83
+ assert_equal( Date.civil(2011, 12, 14), 2.school_days.before(date) )
84
+ end
85
+
86
+ should "elegantly handle a situation where we go outside the school year" do
87
+ date = Date.civil(2011, 8, 26)
88
+ assert_raise(SchoolDays::DateNotInSchoolCalendar) { 1.school_day.before(date) }
89
+ end
90
+
91
+ end
92
+
93
+ end
94
+
@@ -0,0 +1,29 @@
1
+ begin
2
+ require File.dirname(__FILE__) + '/test_helper'
3
+ rescue LoadError
4
+ require 'test/unit'
5
+ end
6
+ require 'fileutils'
7
+
8
+ # Must set before requiring generator libs.
9
+ TMP_ROOT = File.dirname(__FILE__) + "/tmp" unless defined?(TMP_ROOT)
10
+ PROJECT_NAME = "myproject" unless defined?(PROJECT_NAME)
11
+ app_root = File.join(TMP_ROOT, PROJECT_NAME)
12
+ if defined?(APP_ROOT)
13
+ APP_ROOT.replace(app_root)
14
+ else
15
+ APP_ROOT = app_root
16
+ end
17
+ if defined?(RAILS_ROOT)
18
+ RAILS_ROOT.replace(app_root)
19
+ else
20
+ RAILS_ROOT = app_root
21
+ end
22
+
23
+ begin
24
+ require 'rubigen'
25
+ rescue LoadError
26
+ require 'rubygems'
27
+ require 'rubigen'
28
+ end
29
+ require 'rubigen/helpers/generator_test_helper'
@@ -0,0 +1,10 @@
1
+ require 'stringio'
2
+ require 'test/unit'
3
+ require 'rubygems'
4
+ require 'shoulda'
5
+
6
+ require File.dirname(__FILE__) + '/../lib/school_days'
7
+
8
+ def fixture_path()
9
+ File.dirname(__FILE__) + '/fixtures/'
10
+ end
@@ -0,0 +1,11 @@
1
+ require File.dirname(__FILE__) + '/test_helper.rb'
2
+
3
+ class TestSchoolDays < Test::Unit::TestCase
4
+
5
+ def setup
6
+ end
7
+
8
+ def test_truth
9
+ assert true
10
+ end
11
+ end
@@ -0,0 +1,50 @@
1
+ require File.dirname(__FILE__) + '/test_helper.rb'
2
+
3
+
4
+ class TestSchoolDaysCalculatorTest < Test::Unit::TestCase
5
+
6
+ context "identifying if a date is in the range of the whole school year" do
7
+ context "with a single session" do
8
+ setup do
9
+ SchoolDays.config.load( fixture_path() + "/simple_test.yml" )
10
+ end
11
+
12
+ should "return true when it falls in the school year" do
13
+ date = Date.civil(2010, 9, 9) # a Thursday
14
+ calc = SchoolDays::Calculator.new(nil)
15
+
16
+ assert calc.is_in_school_year?(date)
17
+ end
18
+
19
+ should "return false when it falls OUTSIDE of the school year" do
20
+ date = Date.civil(2011, 6, 2) # a friday
21
+ calc = SchoolDays::Calculator.new(nil)
22
+
23
+ assert !calc.is_in_school_year?(date)
24
+ end
25
+ end
26
+
27
+ context "with many sessions" do
28
+ setup do
29
+ SchoolDays.config.load( fixture_path() + "/double_session_test.yml" )
30
+ end
31
+
32
+ should "return true when it falls in the school year" do
33
+ date = Date.civil(2011, 9, 9) # a Friday
34
+ calc = SchoolDays::Calculator.new(nil)
35
+
36
+ assert calc.is_in_school_year?(date)
37
+
38
+ date = Date.civil(2012, 1, 16)
39
+ assert calc.is_in_school_year?(date)
40
+ end
41
+
42
+ should "return false when it falls OUTSIDE of the school year" do
43
+ date = Date.civil(2012, 5, 31) # a Thursday
44
+ calc = SchoolDays::Calculator.new(nil)
45
+
46
+ assert !calc.is_in_school_year?(date)
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,51 @@
1
+ require File.join(File.dirname(__FILE__), "test_generator_helper.rb")
2
+
3
+ =begin
4
+ # TODO: figure out why this test fails. RPW 01-21-2011
5
+
6
+ require 'rails_generator'
7
+
8
+ class TestSchoolDaysConfigGenerator < Test::Unit::TestCase
9
+ include RubiGen::GeneratorTestHelper
10
+
11
+ def setup
12
+ bare_setup
13
+ end
14
+
15
+ def teardown
16
+ bare_teardown
17
+ end
18
+
19
+ # Some generator-related assertions:
20
+ # assert_generated_file(name, &block) # block passed the file contents
21
+ # assert_directory_exists(name)
22
+ # assert_generated_class(name, &block)
23
+ # assert_generated_module(name, &block)
24
+ # assert_generated_test_for(name, &block)
25
+ # The assert_generated_(class|module|test_for) &block is passed the body of the class/module within the file
26
+ # assert_has_method(body, *methods) # check that the body has a list of methods (methods with parentheses not supported yet)
27
+ #
28
+ # Other helper methods are:
29
+ # app_root_files - put this in teardown to show files generated by the test method (e.g. p app_root_files)
30
+ # bare_setup - place this in setup method to create the APP_ROOT folder for each test
31
+ # bare_teardown - place this in teardown method to destroy the TMP_ROOT or APP_ROOT folder after each test
32
+
33
+ def test_generator_without_options
34
+ name = "myapp"
35
+ run_generator('school_days_config', [name], sources)
36
+ assert_directory_exists "some/directory"
37
+ assert_generated_file "some_file"
38
+ end
39
+
40
+ private
41
+ def sources
42
+ [RubiGen::PathSource.new(:test, File.join(File.dirname(__FILE__),"..", generator_path))
43
+ ]
44
+ end
45
+
46
+ def generator_path
47
+ "rails_generators"
48
+ end
49
+ end
50
+
51
+ =end
metadata ADDED
@@ -0,0 +1,155 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: school_days
3
+ version: !ruby/object:Gem::Version
4
+ hash: 23
5
+ prerelease: false
6
+ segments:
7
+ - 1
8
+ - 0
9
+ - 0
10
+ version: 1.0.0
11
+ platform: ruby
12
+ authors:
13
+ - Ryan Wilcox
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-01-26 00:00:00 -05:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: activesupport
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 11
30
+ segments:
31
+ - 2
32
+ - 0
33
+ - 2
34
+ version: 2.0.2
35
+ type: :runtime
36
+ version_requirements: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ name: shoulda
39
+ prerelease: false
40
+ requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - "="
44
+ - !ruby/object:Gem::Version
45
+ hash: 37
46
+ segments:
47
+ - 2
48
+ - 11
49
+ - 3
50
+ version: 2.11.3
51
+ type: :runtime
52
+ version_requirements: *id002
53
+ - !ruby/object:Gem::Dependency
54
+ name: hoe
55
+ prerelease: false
56
+ requirement: &id003 !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ hash: 47
62
+ segments:
63
+ - 2
64
+ - 8
65
+ - 0
66
+ version: 2.8.0
67
+ type: :development
68
+ version_requirements: *id003
69
+ description: |-
70
+ Like Business Time (https://github.com/bokmann/business_time), this gem has support for domain specific dates.
71
+
72
+ Unlike Business Time, School Days focuses on the demands of keeping a school calendar
73
+ email:
74
+ - rwilcox@wilcoxd.com
75
+ executables: []
76
+
77
+ extensions: []
78
+
79
+ extra_rdoc_files:
80
+ - History.txt
81
+ - Manifest.txt
82
+ - PostInstall.txt
83
+ files:
84
+ - History.txt
85
+ - Manifest.txt
86
+ - PostInstall.txt
87
+ - README.rdoc
88
+ - Rakefile
89
+ - lib/school_days.rb
90
+ - lib/extensions/date.rb
91
+ - lib/extensions/fixnum.rb
92
+ - lib/school_days/calculator.rb
93
+ - lib/school_days/config.rb
94
+ - rails_generators/school_days_config/USAGE
95
+ - rails_generators/school_days_config/school_days_config_generator.rb
96
+ - rails_generators/school_days_config/templates/school_days.rb
97
+ - rails_generators/school_days_config/templates/school_days.yml
98
+ - script/console
99
+ - script/destroy
100
+ - script/generate
101
+ - test/fixtures/double_session_test.yml
102
+ - test/fixtures/invalid_config_test.yml
103
+ - test/fixtures/simple_test.yml
104
+ - test/test_config.rb
105
+ - test/test_date_extensions.rb
106
+ - test/test_fixnum_extensions.rb
107
+ - test/test_generator_helper.rb
108
+ - test/test_helper.rb
109
+ - test/test_school_days.rb
110
+ - test/test_school_days_config_generator.rb
111
+ - test/test_school_days_calculator.rb
112
+ has_rdoc: true
113
+ homepage: http://github.com/rwilcox/school_days
114
+ licenses: []
115
+
116
+ post_install_message: PostInstall.txt
117
+ rdoc_options:
118
+ - --main
119
+ - README.rdoc
120
+ require_paths:
121
+ - lib
122
+ required_ruby_version: !ruby/object:Gem::Requirement
123
+ none: false
124
+ requirements:
125
+ - - ">="
126
+ - !ruby/object:Gem::Version
127
+ hash: 3
128
+ segments:
129
+ - 0
130
+ version: "0"
131
+ required_rubygems_version: !ruby/object:Gem::Requirement
132
+ none: false
133
+ requirements:
134
+ - - ">="
135
+ - !ruby/object:Gem::Version
136
+ hash: 3
137
+ segments:
138
+ - 0
139
+ version: "0"
140
+ requirements: []
141
+
142
+ rubyforge_project: school_days
143
+ rubygems_version: 1.3.7
144
+ signing_key:
145
+ specification_version: 3
146
+ summary: Like Business Time (https://github.com/bokmann/business_time), this gem has support for domain specific dates
147
+ test_files:
148
+ - test/test_config.rb
149
+ - test/test_date_extensions.rb
150
+ - test/test_fixnum_extensions.rb
151
+ - test/test_generator_helper.rb
152
+ - test/test_helper.rb
153
+ - test/test_school_days.rb
154
+ - test/test_school_days_calculator.rb
155
+ - test/test_school_days_config_generator.rb