school_days 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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