metra_schedule 0.2.1.2 → 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/README.rdoc +2 -0
- data/Rakefile +16 -0
- data/VERSION +1 -0
- data/lib/metra/extensions/time_extension.rb +9 -0
- data/lib/metra/train.rb +1 -2
- data/lib/metra.rb +3 -0
- data/metra_schedule.gemspec +91 -0
- data/test/timecop/History.rdoc +91 -0
- data/test/timecop/LICENSE +22 -0
- data/test/timecop/README.rdoc +68 -0
- data/test/timecop/Rakefile +55 -0
- data/test/timecop/VERSION.yml +4 -0
- data/test/timecop/lib/timecop/time_extensions.rb +72 -0
- data/test/timecop/lib/timecop/time_stack_item.rb +113 -0
- data/test/timecop/lib/timecop/timecop.rb +103 -0
- data/test/timecop/lib/timecop.rb +2 -0
- data/test/unit/test_time_extension.rb +12 -0
- data/test/unit/test_train.rb +14 -0
- metadata +42 -14
data/.gitignore
ADDED
data/README.rdoc
CHANGED
data/Rakefile
CHANGED
@@ -21,3 +21,19 @@ Rake::TestTask.new(:test_integrations) do |t|
|
|
21
21
|
t.test_files = FileList['test/integration/test*.rb']
|
22
22
|
t.warning = false
|
23
23
|
end
|
24
|
+
|
25
|
+
begin
|
26
|
+
require 'jeweler'
|
27
|
+
Jeweler::Tasks.new do |gemspec|
|
28
|
+
gemspec.name = "metra_schedule"
|
29
|
+
gemspec.summary = "Chicago Metra parser and scheduler"
|
30
|
+
gemspec.description = "metra_schedule provides a ruby object interface to the Chicago metra train schedule"
|
31
|
+
gemspec.email = "blakesmith0@gmail.com"
|
32
|
+
gemspec.homepage = "http://github.com/blakesmith/metra_schedule"
|
33
|
+
gemspec.authors = ["Blake Smith"]
|
34
|
+
gemspec.add_dependency('nokogiri', '>=1.4.1')
|
35
|
+
end
|
36
|
+
rescue LoadError
|
37
|
+
puts "Jeweler not available. Install it with: gem install jeweler"
|
38
|
+
end
|
39
|
+
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.2.2
|
data/lib/metra/train.rb
CHANGED
@@ -36,7 +36,7 @@ module MetraSchedule
|
|
36
36
|
def departure_and_arrival(start, destination)
|
37
37
|
departure = @stops.find {|s| s.station == start}.time
|
38
38
|
arrival = @stops.find {|s| s.station == destination}.time
|
39
|
-
{:departure => departure, :arrival => arrival}
|
39
|
+
{:departure => departure.to_today, :arrival => arrival.to_today}
|
40
40
|
end
|
41
41
|
|
42
42
|
def my_travel_time
|
@@ -88,6 +88,5 @@ module MetraSchedule
|
|
88
88
|
return "#{my_travel_minutes} minutes" if my_travel_minutes > 1
|
89
89
|
end
|
90
90
|
|
91
|
-
|
92
91
|
end
|
93
92
|
end
|
data/lib/metra.rb
CHANGED
@@ -4,7 +4,10 @@ require File.join(File.dirname(__FILE__), 'metra', 'train')
|
|
4
4
|
require File.join(File.dirname(__FILE__), 'metra', 'stop')
|
5
5
|
require File.join(File.dirname(__FILE__), 'metra', 'parser')
|
6
6
|
require File.join(File.dirname(__FILE__), 'metra', 'cacher')
|
7
|
+
require File.join(File.dirname(__FILE__), 'metra', 'extensions', 'time_extension')
|
7
8
|
|
8
9
|
class Metra
|
9
10
|
include MetraSchedule::InstanceMethods
|
10
11
|
end
|
12
|
+
|
13
|
+
Time.send(:include, MetraSchedule::Extensions::TimeExtension)
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{metra_schedule}
|
8
|
+
s.version = "0.2.2"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Blake Smith"]
|
12
|
+
s.date = %q{2010-01-05}
|
13
|
+
s.description = %q{metra_schedule provides a ruby object interface to the Chicago metra train schedule}
|
14
|
+
s.email = %q{blakesmith0@gmail.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE",
|
17
|
+
"README.rdoc"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".gitignore",
|
21
|
+
"LICENSE",
|
22
|
+
"README.rdoc",
|
23
|
+
"Rakefile",
|
24
|
+
"VERSION",
|
25
|
+
"lib/metra.rb",
|
26
|
+
"lib/metra/cacher.rb",
|
27
|
+
"lib/metra/classmethods.rb",
|
28
|
+
"lib/metra/extensions/time_extension.rb",
|
29
|
+
"lib/metra/line.rb",
|
30
|
+
"lib/metra/parser.rb",
|
31
|
+
"lib/metra/stop.rb",
|
32
|
+
"lib/metra/train.rb",
|
33
|
+
"lib/metra/train_data.rb",
|
34
|
+
"metra_schedule.gemspec",
|
35
|
+
"test/fixture/UP_NW.html",
|
36
|
+
"test/functional/test_all_filters.rb",
|
37
|
+
"test/integration/test_line_integration.rb",
|
38
|
+
"test/test_helper.rb",
|
39
|
+
"test/timecop/History.rdoc",
|
40
|
+
"test/timecop/LICENSE",
|
41
|
+
"test/timecop/README.rdoc",
|
42
|
+
"test/timecop/Rakefile",
|
43
|
+
"test/timecop/VERSION.yml",
|
44
|
+
"test/timecop/lib/timecop.rb",
|
45
|
+
"test/timecop/lib/timecop/time_extensions.rb",
|
46
|
+
"test/timecop/lib/timecop/time_stack_item.rb",
|
47
|
+
"test/timecop/lib/timecop/timecop.rb",
|
48
|
+
"test/unit/test_cacher.rb",
|
49
|
+
"test/unit/test_line.rb",
|
50
|
+
"test/unit/test_metra.rb",
|
51
|
+
"test/unit/test_parser.rb",
|
52
|
+
"test/unit/test_stop.rb",
|
53
|
+
"test/unit/test_time_extension.rb",
|
54
|
+
"test/unit/test_train.rb"
|
55
|
+
]
|
56
|
+
s.homepage = %q{http://github.com/blakesmith/metra_schedule}
|
57
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
58
|
+
s.require_paths = ["lib"]
|
59
|
+
s.rubygems_version = %q{1.3.5}
|
60
|
+
s.summary = %q{Chicago Metra parser and scheduler}
|
61
|
+
s.test_files = [
|
62
|
+
"test/integration/test_line_integration.rb",
|
63
|
+
"test/functional/test_all_filters.rb",
|
64
|
+
"test/timecop/lib/timecop/time_stack_item.rb",
|
65
|
+
"test/timecop/lib/timecop/timecop.rb",
|
66
|
+
"test/timecop/lib/timecop/time_extensions.rb",
|
67
|
+
"test/timecop/lib/timecop.rb",
|
68
|
+
"test/unit/test_time_extension.rb",
|
69
|
+
"test/unit/test_train.rb",
|
70
|
+
"test/unit/test_line.rb",
|
71
|
+
"test/unit/test_metra.rb",
|
72
|
+
"test/unit/test_stop.rb",
|
73
|
+
"test/unit/test_parser.rb",
|
74
|
+
"test/unit/test_cacher.rb",
|
75
|
+
"test/test_helper.rb"
|
76
|
+
]
|
77
|
+
|
78
|
+
if s.respond_to? :specification_version then
|
79
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
80
|
+
s.specification_version = 3
|
81
|
+
|
82
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
83
|
+
s.add_runtime_dependency(%q<nokogiri>, [">= 1.4.1"])
|
84
|
+
else
|
85
|
+
s.add_dependency(%q<nokogiri>, [">= 1.4.1"])
|
86
|
+
end
|
87
|
+
else
|
88
|
+
s.add_dependency(%q<nokogiri>, [">= 1.4.1"])
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
@@ -0,0 +1,91 @@
|
|
1
|
+
=== 0.3.4 / unreleased (RC2 released 2009-11-28)
|
2
|
+
|
3
|
+
* Maintenance
|
4
|
+
* Fix various timezone-related issues. Notably, when traveling to a DateTime
|
5
|
+
instance specified in a non-local timezone, convert provided DateTime
|
6
|
+
instance to a local instance and return that from DateTime.now.
|
7
|
+
Code contributed by Michaël Witrant [piglop]
|
8
|
+
* Fix bug that would not allow Timecop to be used when Ruby's 'date'
|
9
|
+
library had not been previously loaded.
|
10
|
+
Code contributed by Tuomas Kareinen [tuomas]
|
11
|
+
* Fix bug when traveling to a DateTime across a DST boundary that
|
12
|
+
resulted in DateTime's being off by an hour.
|
13
|
+
* Migrate argument parsing into Timecop::TimeStackItem to reduce the
|
14
|
+
responsibility of the Timecop class.
|
15
|
+
|
16
|
+
=== 0.3.3 2009-10-30
|
17
|
+
|
18
|
+
* Revoked due to regression.
|
19
|
+
|
20
|
+
=== 0.3.2 / 2009-10-24
|
21
|
+
|
22
|
+
* Revoked due to regression.
|
23
|
+
|
24
|
+
=== 0.3.1 / 2009-09-30
|
25
|
+
|
26
|
+
* Maintenance
|
27
|
+
* DRY up the Timecop class internals.
|
28
|
+
|
29
|
+
=== 0.3.0 / 2009-09-20
|
30
|
+
|
31
|
+
* API
|
32
|
+
* Completely remove Timecop#unset_all (deprecated by Timecop#return in 0.2.0)
|
33
|
+
* Return Time.now from #freeze, #travel and #return -- code contributed by Keith Bennett [keithrbennett]
|
34
|
+
|
35
|
+
* Maintenance
|
36
|
+
* Fix bug that left Time#mock_time set in some instances
|
37
|
+
* Upped build dependency to jeweler ~> 1.2.1
|
38
|
+
* Don't pollute top-level namespace with classes/constants
|
39
|
+
|
40
|
+
* Documentation
|
41
|
+
* Clearer examples in the README, better description in the gemspec
|
42
|
+
* Improve RDoc
|
43
|
+
|
44
|
+
=== 0.2.1 / 2009-03-06
|
45
|
+
* API Changes
|
46
|
+
|
47
|
+
* Introduced a 5th style of arguments to be passed into #travel and #freeze. Now, if you pass in a single integer value,
|
48
|
+
it will be interpreted as a relative offset in seconds from the current Time.now. Previously this was interpreted as
|
49
|
+
only the year, similar to calling Time.local(2008) --> Jan. 1, 2008. This is no longer the case.
|
50
|
+
|
51
|
+
* Documentation
|
52
|
+
|
53
|
+
* Moved to Textile for the README.
|
54
|
+
|
55
|
+
* Added documentation for the new feature, and fixed a few typos.
|
56
|
+
|
57
|
+
=== 0.2.0 / 2008-12-23
|
58
|
+
|
59
|
+
* API Changes
|
60
|
+
|
61
|
+
* Timecop#travel no longer freezes time. Rather, it computes the current offset between the new "now" and the real "now", and
|
62
|
+
returns times as if Time had continued to move forward
|
63
|
+
|
64
|
+
* Timecop#freeze now behaves exactly as the old Timecop#travel behaved. Unless you depended on the actual freezing of time
|
65
|
+
(which I think would be rare), you should be able to continue to use #travel without worry.
|
66
|
+
|
67
|
+
* Timecop#return is now exposed (previously Timecop#unset_all, but not well advertised). It will completely unmock time,
|
68
|
+
and will probably be rarely used outside of the actual implementation of this library.
|
69
|
+
|
70
|
+
* More Test Coverage
|
71
|
+
|
72
|
+
* Tests now explicitly cover the cases when the Date and DateTime objects are not loaded, and ensures proper functionality
|
73
|
+
in their absence and existence.
|
74
|
+
|
75
|
+
* Still haven't done regression testing against anything other than a few version of 1.8.6 (including REE). We should
|
76
|
+
probably try to get this tested on both 1.8.7 and 1.9.1.
|
77
|
+
|
78
|
+
* Documentation
|
79
|
+
|
80
|
+
* Fixed up a lot of the poorly-formatted rdoc syntax. The public API should now be properly published in the rdoc,
|
81
|
+
and the internals are omitted.
|
82
|
+
|
83
|
+
=== 0.1.0 / 2008-11-09
|
84
|
+
|
85
|
+
* Initial Feature Set
|
86
|
+
|
87
|
+
* Temporarily (or permanently if you prefer) change the concept of Time.now, DateTime.now (if defined), and Date.today (if defined)
|
88
|
+
* Timecop#travel api allows an argument to be passed in as one of: 1) Time instance, 2) DateTime instance, 3) Date instance,
|
89
|
+
4) individual arguments (year, month, day, hour, minute, second)
|
90
|
+
* Nested calls to Timecop#travel are supported -- each block will maintain it's interpretation of now.
|
91
|
+
|
@@ -0,0 +1,22 @@
|
|
1
|
+
(The MIT License)
|
2
|
+
|
3
|
+
Copyright (c) 2008
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
'Software'), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
19
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
20
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
21
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
22
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
@@ -0,0 +1,68 @@
|
|
1
|
+
= timecop
|
2
|
+
|
3
|
+
* Source[http://github.com/jtrupiano/timecop]
|
4
|
+
* Documentation[http://johntrupiano.rubyforge.org/timecop]
|
5
|
+
|
6
|
+
== DESCRIPTION
|
7
|
+
|
8
|
+
A gem providing "time travel" and "time freezing" capabilities, making it dead simple to test time-dependent code. It provides a unified method to mock Time.now, Date.today, and DateTime.now in a single call.
|
9
|
+
|
10
|
+
== INSTALL
|
11
|
+
|
12
|
+
gem install timecop
|
13
|
+
|
14
|
+
== FEATURES
|
15
|
+
|
16
|
+
* Freeze time to a specific point.
|
17
|
+
* Travel back to a specific point in time, but allow time to continue moving forward from there.
|
18
|
+
* No dependencies, can be used with _any_ ruby project
|
19
|
+
* Timecop api allows arguments to be passed into #freeze and #travel as one of the following:
|
20
|
+
* Time instance
|
21
|
+
* DateTime instance
|
22
|
+
* Date instance
|
23
|
+
* individual arguments (year, month, day, hour, minute, second)
|
24
|
+
* a single integer argument that is interpreted as an offset in seconds from Time.now
|
25
|
+
* Nested calls to Timecop#travel and Timecop#freeze are supported -- each block will maintain its interpretation of now.
|
26
|
+
|
27
|
+
== USAGE
|
28
|
+
|
29
|
+
Run a time-sensitive test
|
30
|
+
|
31
|
+
joe = User.find(1)
|
32
|
+
joe.purchase_home()
|
33
|
+
assert !joe.mortgage_due?
|
34
|
+
# move ahead a month and assert that the mortgage is due
|
35
|
+
Timecop.freeze(Date.today + 30) do
|
36
|
+
assert joe.mortgage_due?
|
37
|
+
end
|
38
|
+
|
39
|
+
Set the time for the test environment of a rails app -- this is particularly helpful if your whole application is time-sensitive. It allows you to build your test data at a single point in time, and to move in/out of that time as appropriate (within your tests)
|
40
|
+
|
41
|
+
in config/environments/test.rb
|
42
|
+
|
43
|
+
config.after_initialize do
|
44
|
+
# Set Time.now to September 1, 2008 10:05:00 AM (at this instant), but allow it to move forward
|
45
|
+
t = Time.local(2008, 9, 1, 10, 5, 0)
|
46
|
+
Timecop.travel(t)
|
47
|
+
end
|
48
|
+
|
49
|
+
=== The difference between Timecop.freeze and Timecop.travel
|
50
|
+
|
51
|
+
#freeze is used to statically mock the concept of now. As your program executes, Time.now will not change unless you make subsequent calls into the Timecop API. #travel, on the other hand, computes an offset between what we currently think Time.now is (recall that we support nested traveling) and the time passed in. It uses this offset to simulate the passage of time. To demonstrate, consider the following code snippets:
|
52
|
+
|
53
|
+
new_time = Time.local(2008, 9, 1, 12, 0, 0)
|
54
|
+
Timecop.freeze(new_time)
|
55
|
+
sleep(10)
|
56
|
+
new_time == Time.now # ==> true
|
57
|
+
|
58
|
+
Timecop.return # "turn off" Timecop
|
59
|
+
Timecop.travel(new_time)
|
60
|
+
sleep(10)
|
61
|
+
new_time == Time.now # ==> false
|
62
|
+
|
63
|
+
== REFERENCES
|
64
|
+
|
65
|
+
* {0.3.4 release}[http://blog.smartlogicsolutions.com/2009/12/07/timecop-0-3-4-released/]
|
66
|
+
* {0.3.0 release}[http://blog.smartlogicsolutions.com/2009/09/20/timecop-0-3-0-released/]
|
67
|
+
* {0.2.0 release}[http://blog.smartlogicsolutions.com/2008/12/24/timecop-2-released-freeze-and-rebase-time-ruby/]
|
68
|
+
* {0.1.0 release}[http://blog.smartlogicsolutions.com/2008/11/19/timecop-freeze-time-in-ruby-for-better-testing/]
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'rake/rdoctask'
|
4
|
+
|
5
|
+
gem 'jeweler', '~> 1.3.0'
|
6
|
+
|
7
|
+
begin
|
8
|
+
require 'jeweler'
|
9
|
+
Jeweler::Tasks.new do |s|
|
10
|
+
s.name = "timecop"
|
11
|
+
s.rubyforge_project = 'johntrupiano' # if different than lowercase project name
|
12
|
+
s.description = %q(A gem providing "time travel" and "time freezing" capabilities, making it dead simple to test time-dependent code. It provides a unified method to mock Time.now, Date.today, and DateTime.now in a single call.)
|
13
|
+
s.summary = s.description # More details later??
|
14
|
+
s.email = "jtrupiano@gmail.com"
|
15
|
+
s.homepage = "http://github.com/jtrupiano/timecop"
|
16
|
+
s.authors = ["John Trupiano"]
|
17
|
+
s.files = FileList["[A-Z]*", "{bin,lib,test}/**/*"]
|
18
|
+
end
|
19
|
+
Jeweler::GemcutterTasks.new
|
20
|
+
Jeweler::RubyforgeTasks.new do |rubyforge|
|
21
|
+
rubyforge.doc_task = "rdoc"
|
22
|
+
rubyforge.remote_doc_path = "timecop"
|
23
|
+
end
|
24
|
+
rescue LoadError
|
25
|
+
puts "Jeweler, or one of its dependencies, is not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
26
|
+
end
|
27
|
+
|
28
|
+
# Override the test task and instruct them how to actually run the tests.
|
29
|
+
Rake.application.send(:eval, "@tasks.delete('test')")
|
30
|
+
desc "Does not execute tests. Manually run shell script ./run_tests.sh to execute tests."
|
31
|
+
task :test do
|
32
|
+
puts <<-MSG
|
33
|
+
In order to run the test suite, run: cd test && ./run_tests.sh
|
34
|
+
The tests need to be run with different libraries loaded, which rules out using Rake
|
35
|
+
to automate them.
|
36
|
+
MSG
|
37
|
+
end
|
38
|
+
|
39
|
+
require 'rake/rdoctask'
|
40
|
+
Rake::RDocTask.new do |rdoc|
|
41
|
+
if File.exist?('VERSION')
|
42
|
+
version = File.read('VERSION')
|
43
|
+
else
|
44
|
+
version = ""
|
45
|
+
end
|
46
|
+
|
47
|
+
rdoc.rdoc_dir = 'rdoc'
|
48
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
49
|
+
rdoc.title = "timecop #{version}"
|
50
|
+
rdoc.rdoc_files.include('README*')
|
51
|
+
rdoc.rdoc_files.include('History.rdoc')
|
52
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
53
|
+
end
|
54
|
+
|
55
|
+
task :default => :test
|
@@ -0,0 +1,72 @@
|
|
1
|
+
|
2
|
+
class Time #:nodoc:
|
3
|
+
class << self
|
4
|
+
# Time we are behaving as
|
5
|
+
def mock_time
|
6
|
+
mocked_time_stack_item = Timecop.top_stack_item
|
7
|
+
mocked_time_stack_item.nil? ? nil : mocked_time_stack_item.time
|
8
|
+
end
|
9
|
+
|
10
|
+
# Alias the original now
|
11
|
+
alias_method :now_without_mock_time, :now
|
12
|
+
|
13
|
+
# Define now_with_mock_time
|
14
|
+
def now_with_mock_time
|
15
|
+
mock_time || now_without_mock_time
|
16
|
+
end
|
17
|
+
|
18
|
+
# Alias now to now_with_mock_time
|
19
|
+
alias_method :now, :now_with_mock_time
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
if Object.const_defined?(:Date) && Date.respond_to?(:today)
|
24
|
+
class Date #:nodoc:
|
25
|
+
class << self
|
26
|
+
# Date we are behaving as
|
27
|
+
def mock_date
|
28
|
+
mocked_time_stack_item = Timecop.top_stack_item
|
29
|
+
mocked_time_stack_item.nil? ? nil : mocked_time_stack_item.date
|
30
|
+
end
|
31
|
+
|
32
|
+
# Alias the original today
|
33
|
+
alias_method :today_without_mock_date, :today
|
34
|
+
|
35
|
+
# Define today_with_mock_date
|
36
|
+
def today_with_mock_date
|
37
|
+
mock_date || today_without_mock_date
|
38
|
+
end
|
39
|
+
|
40
|
+
# Alias today to today_with_mock_date
|
41
|
+
alias_method :today, :today_with_mock_date
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
if Object.const_defined?(:DateTime) && DateTime.respond_to?(:now)
|
47
|
+
class DateTime #:nodoc:
|
48
|
+
class << self
|
49
|
+
# Time we are behaving as
|
50
|
+
def mock_time
|
51
|
+
mocked_time_stack_item = Timecop.top_stack_item
|
52
|
+
mocked_time_stack_item.nil? ? nil : mocked_time_stack_item.datetime
|
53
|
+
end
|
54
|
+
|
55
|
+
# Fake alias :now_without_mock_time :now
|
56
|
+
# It appears that the DateTime library itself references Time.now
|
57
|
+
# for it's interpretation of now which caused
|
58
|
+
# DateTime.now_without_mock_time to incorrectly return the frozen time.
|
59
|
+
def now_without_mock_time
|
60
|
+
Time.now_without_mock_time.send :to_datetime
|
61
|
+
end
|
62
|
+
|
63
|
+
# Define now_with_mock_time
|
64
|
+
def now_with_mock_time
|
65
|
+
mock_time || now_without_mock_time
|
66
|
+
end
|
67
|
+
|
68
|
+
# Alias now to now_with_mock_time
|
69
|
+
alias_method :now, :now_with_mock_time
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
|
2
|
+
class Timecop
|
3
|
+
# A data class for carrying around "time movement" objects. Makes it easy to keep track of the time
|
4
|
+
# movements on a simple stack.
|
5
|
+
class TimeStackItem #:nodoc:
|
6
|
+
|
7
|
+
attr_reader :mock_type
|
8
|
+
def initialize(mock_type, *args)
|
9
|
+
raise "Unknown mock_type #{mock_type}" unless [:freeze, :travel].include?(mock_type)
|
10
|
+
@mock_type = mock_type
|
11
|
+
@time = parse_time(*args)
|
12
|
+
@travel_offset = compute_travel_offset
|
13
|
+
@dst_adjustment = compute_dst_adjustment
|
14
|
+
end
|
15
|
+
|
16
|
+
def year
|
17
|
+
time.year
|
18
|
+
end
|
19
|
+
|
20
|
+
def month
|
21
|
+
time.month
|
22
|
+
end
|
23
|
+
|
24
|
+
def day
|
25
|
+
time.day
|
26
|
+
end
|
27
|
+
|
28
|
+
def hour
|
29
|
+
time.hour
|
30
|
+
end
|
31
|
+
|
32
|
+
def min
|
33
|
+
time.min
|
34
|
+
end
|
35
|
+
|
36
|
+
def sec
|
37
|
+
time.sec
|
38
|
+
end
|
39
|
+
|
40
|
+
def utc_offset
|
41
|
+
time.utc_offset
|
42
|
+
end
|
43
|
+
|
44
|
+
def travel_offset
|
45
|
+
@travel_offset
|
46
|
+
end
|
47
|
+
|
48
|
+
def time #:nodoc:
|
49
|
+
if travel_offset.nil?
|
50
|
+
@time
|
51
|
+
else
|
52
|
+
Time.now_without_mock_time + travel_offset
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def date
|
57
|
+
time.send(:to_date)
|
58
|
+
end
|
59
|
+
|
60
|
+
def datetime
|
61
|
+
# DateTime doesn't know about DST, so let's remove its presence
|
62
|
+
our_offset = utc_offset + dst_adjustment
|
63
|
+
DateTime.new(year, month, day, hour, min, sec, utc_offset_to_rational(our_offset))
|
64
|
+
end
|
65
|
+
|
66
|
+
def dst_adjustment
|
67
|
+
@dst_adjustment
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
def rational_to_utc_offset(rational)
|
72
|
+
((24.0 / rational.denominator) * rational.numerator) * (60 * 60)
|
73
|
+
end
|
74
|
+
|
75
|
+
def utc_offset_to_rational(utc_offset)
|
76
|
+
Rational(utc_offset, 24 * 60 * 60)
|
77
|
+
end
|
78
|
+
|
79
|
+
def parse_time(*args)
|
80
|
+
arg = args.shift
|
81
|
+
if arg.is_a?(Time)
|
82
|
+
arg.getlocal
|
83
|
+
elsif Object.const_defined?(:DateTime) && arg.is_a?(DateTime)
|
84
|
+
offset_difference = Time.now.utc_offset - rational_to_utc_offset(arg.offset)
|
85
|
+
Time.local(arg.year, arg.month, arg.day, arg.hour, arg.min, arg.sec) + offset_difference
|
86
|
+
elsif Object.const_defined?(:Date) && arg.is_a?(Date)
|
87
|
+
Time.local(arg.year, arg.month, arg.day, 0, 0, 0)
|
88
|
+
elsif args.empty? && arg.kind_of?(Integer)
|
89
|
+
Time.now + arg
|
90
|
+
else # we'll just assume it's a list of y/m/d/h/m/s
|
91
|
+
year = arg || 0
|
92
|
+
month = args.shift || 1
|
93
|
+
day = args.shift || 1
|
94
|
+
hour = args.shift || 0
|
95
|
+
minute = args.shift || 0
|
96
|
+
second = args.shift || 0
|
97
|
+
Time.local(year, month, day, hour, minute, second)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def compute_dst_adjustment
|
102
|
+
return 0 if !(time.dst? ^ Time.now.dst?)
|
103
|
+
return -1 * 60 * 60 if time.dst?
|
104
|
+
return 60 * 60
|
105
|
+
end
|
106
|
+
|
107
|
+
def compute_travel_offset
|
108
|
+
return nil if mock_type == :freeze
|
109
|
+
@time - Time.now_without_mock_time
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
require File.join(File.dirname(__FILE__), 'time_extensions')
|
3
|
+
require File.join(File.dirname(__FILE__), 'time_stack_item')
|
4
|
+
|
5
|
+
# Timecop
|
6
|
+
# * Wrapper class for manipulating the extensions to the Time, Date, and DateTime objects
|
7
|
+
# * Allows us to "freeze" time in our Ruby applications.
|
8
|
+
# * Optionally allows time travel to simulate a running clock, such time is not technically frozen.
|
9
|
+
#
|
10
|
+
# This is very useful when your app's functionality is dependent on time (e.g.
|
11
|
+
# anything that might expire). This will allow us to alter the return value of
|
12
|
+
# Date.today, Time.now, and DateTime.now, such that our application code _never_ has to change.
|
13
|
+
class Timecop
|
14
|
+
include Singleton
|
15
|
+
|
16
|
+
# Allows you to run a block of code and "fake" a time throughout the execution of that block.
|
17
|
+
# This is particularly useful for writing test methods where the passage of time is critical to the business
|
18
|
+
# logic being tested. For example:
|
19
|
+
#
|
20
|
+
# joe = User.find(1)
|
21
|
+
# joe.purchase_home()
|
22
|
+
# assert !joe.mortgage_due?
|
23
|
+
# Timecop.freeze(2008, 10, 5) do
|
24
|
+
# assert joe.mortgage_due?
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
# freeze and travel will respond to several different arguments:
|
28
|
+
# 1. Timecop.freeze(time_inst)
|
29
|
+
# 2. Timecop.freeze(datetime_inst)
|
30
|
+
# 3. Timecop.freeze(date_inst)
|
31
|
+
# 4. Timecop.freeze(offset_in_seconds)
|
32
|
+
# 5. Timecop.freeze(year, month, day, hour=0, minute=0, second=0)
|
33
|
+
#
|
34
|
+
# When a block is also passed, Time.now, DateTime.now and Date.today are all reset to their
|
35
|
+
# previous values after the block has finished executing. This allows us to nest multiple
|
36
|
+
# calls to Timecop.travel and have each block maintain it's concept of "now."
|
37
|
+
#
|
38
|
+
# * Note: Timecop.freeze will actually freeze time. This can cause unanticipated problems if
|
39
|
+
# benchmark or other timing calls are executed, which implicitly expect Time to actually move
|
40
|
+
# forward.
|
41
|
+
#
|
42
|
+
# * Rails Users: Be especially careful when setting this in your development environment in a
|
43
|
+
# rails project. Generators will load your environment, including the migration generator,
|
44
|
+
# which will lead to files being generated with the timestamp set by the Timecop.freeze call
|
45
|
+
# in your dev environment
|
46
|
+
#
|
47
|
+
# Returns the frozen time.
|
48
|
+
def self.freeze(*args, &block)
|
49
|
+
instance().send(:travel, :freeze, *args, &block)
|
50
|
+
Time.now
|
51
|
+
end
|
52
|
+
|
53
|
+
# Allows you to run a block of code and "fake" a time throughout the execution of that block.
|
54
|
+
# See Timecop#freeze for a sample of how to use (same exact usage syntax)
|
55
|
+
#
|
56
|
+
# * Note: Timecop.travel will not freeze time (as opposed to Timecop.freeze). This is a particularly
|
57
|
+
# good candidate for use in environment files in rails projects.
|
58
|
+
#
|
59
|
+
# Returns the 'new' current Time.
|
60
|
+
def self.travel(*args, &block)
|
61
|
+
instance().send(:travel, :travel, *args, &block)
|
62
|
+
Time.now
|
63
|
+
end
|
64
|
+
|
65
|
+
# Reverts back to system's Time.now, Date.today and DateTime.now (if it exists).
|
66
|
+
#
|
67
|
+
# Returns Time.now, which is now the real current time.
|
68
|
+
def self.return
|
69
|
+
instance().send(:unmock!)
|
70
|
+
Time.now
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.top_stack_item #:nodoc:
|
74
|
+
instance().instance_variable_get(:@_stack).last
|
75
|
+
end
|
76
|
+
|
77
|
+
protected
|
78
|
+
|
79
|
+
def initialize #:nodoc:
|
80
|
+
@_stack = []
|
81
|
+
end
|
82
|
+
|
83
|
+
def travel(mock_type, *args, &block) #:nodoc:
|
84
|
+
stack_item = TimeStackItem.new(mock_type, *args)
|
85
|
+
|
86
|
+
# store this time traveling on our stack...
|
87
|
+
@_stack << stack_item
|
88
|
+
|
89
|
+
if block_given?
|
90
|
+
begin
|
91
|
+
yield
|
92
|
+
ensure
|
93
|
+
# pull it off the stack...
|
94
|
+
@_stack.pop
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def unmock! #:nodoc:
|
100
|
+
@_stack = []
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "../", "test_helper.rb")
|
2
|
+
|
3
|
+
class TestTimeExtension < Test::Unit::TestCase
|
4
|
+
|
5
|
+
def test_to_today
|
6
|
+
sample = Time.local(2009, 5, 5, 7, 30, 45)
|
7
|
+
today = Time.now
|
8
|
+
expected = Time.local(today.year, today.month, today.day, sample.hour, sample.min, sample.sec)
|
9
|
+
assert_equal(expected, sample.to_today)
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
data/test/unit/test_train.rb
CHANGED
@@ -82,6 +82,20 @@ class TestLine < Test::Unit::TestCase
|
|
82
82
|
assert_equal(Time.parse("12:40"), train.my_arrival)
|
83
83
|
end
|
84
84
|
|
85
|
+
def test_my_departure_and_my_arrival_with_cached_from_another_day
|
86
|
+
Timecop.freeze(2010, 1, 5)
|
87
|
+
stop1 = MetraSchedule::Stop.new :station => :barrington, :time => Time.parse("12:30")
|
88
|
+
stop2 = MetraSchedule::Stop.new :station => :arlington_heights, :time => Time.parse("12:40")
|
89
|
+
@@t = MetraSchedule::Train.new :train_num => 651, :bike_limit => 12, :schedule => :weekday, :direction => :inbound, :stops => [stop1, stop2]
|
90
|
+
|
91
|
+
Timecop.freeze(2010, 1, 6)
|
92
|
+
l = Metra.new.line(:up_nw).from(:barrington).to(:arlington_heights)
|
93
|
+
l.engines = [@@t]
|
94
|
+
train = l.trains.first
|
95
|
+
assert_equal(Time.parse("12:30"), train.my_departure)
|
96
|
+
assert_equal(Time.parse("12:40"), train.my_arrival)
|
97
|
+
end
|
98
|
+
|
85
99
|
def test_my_departure_and_my_arrival_with_no_start_or_destination
|
86
100
|
stop1 = MetraSchedule::Stop.new :station => :barrington, :time => Time.parse("12:30")
|
87
101
|
stop2 = MetraSchedule::Stop.new :station => :arlington_heights, :time => Time.parse("12:40")
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: metra_schedule
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Blake Smith
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date:
|
12
|
+
date: 2010-01-05 00:00:00 -06:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -22,43 +22,58 @@ dependencies:
|
|
22
22
|
- !ruby/object:Gem::Version
|
23
23
|
version: 1.4.1
|
24
24
|
version:
|
25
|
-
description: metra_schedule provides a ruby object interface to the Chicago metra train schedule
|
25
|
+
description: metra_schedule provides a ruby object interface to the Chicago metra train schedule
|
26
26
|
email: blakesmith0@gmail.com
|
27
27
|
executables: []
|
28
28
|
|
29
29
|
extensions: []
|
30
30
|
|
31
|
-
extra_rdoc_files:
|
32
|
-
|
31
|
+
extra_rdoc_files:
|
32
|
+
- LICENSE
|
33
|
+
- README.rdoc
|
33
34
|
files:
|
34
|
-
-
|
35
|
+
- .gitignore
|
35
36
|
- LICENSE
|
36
37
|
- README.rdoc
|
38
|
+
- Rakefile
|
39
|
+
- VERSION
|
37
40
|
- lib/metra.rb
|
38
41
|
- lib/metra/cacher.rb
|
39
42
|
- lib/metra/classmethods.rb
|
43
|
+
- lib/metra/extensions/time_extension.rb
|
40
44
|
- lib/metra/line.rb
|
41
45
|
- lib/metra/parser.rb
|
42
46
|
- lib/metra/stop.rb
|
43
47
|
- lib/metra/train.rb
|
44
48
|
- lib/metra/train_data.rb
|
45
|
-
-
|
49
|
+
- metra_schedule.gemspec
|
46
50
|
- test/fixture/UP_NW.html
|
51
|
+
- test/functional/test_all_filters.rb
|
52
|
+
- test/integration/test_line_integration.rb
|
53
|
+
- test/test_helper.rb
|
54
|
+
- test/timecop/History.rdoc
|
55
|
+
- test/timecop/LICENSE
|
56
|
+
- test/timecop/README.rdoc
|
57
|
+
- test/timecop/Rakefile
|
58
|
+
- test/timecop/VERSION.yml
|
59
|
+
- test/timecop/lib/timecop.rb
|
60
|
+
- test/timecop/lib/timecop/time_extensions.rb
|
61
|
+
- test/timecop/lib/timecop/time_stack_item.rb
|
62
|
+
- test/timecop/lib/timecop/timecop.rb
|
47
63
|
- test/unit/test_cacher.rb
|
48
64
|
- test/unit/test_line.rb
|
49
65
|
- test/unit/test_metra.rb
|
50
66
|
- test/unit/test_parser.rb
|
51
67
|
- test/unit/test_stop.rb
|
68
|
+
- test/unit/test_time_extension.rb
|
52
69
|
- test/unit/test_train.rb
|
53
|
-
- test/functional/test_all_filters.rb
|
54
|
-
- test/integration/test_line_integration.rb
|
55
70
|
has_rdoc: true
|
56
71
|
homepage: http://github.com/blakesmith/metra_schedule
|
57
72
|
licenses: []
|
58
73
|
|
59
74
|
post_install_message:
|
60
|
-
rdoc_options:
|
61
|
-
|
75
|
+
rdoc_options:
|
76
|
+
- --charset=UTF-8
|
62
77
|
require_paths:
|
63
78
|
- lib
|
64
79
|
required_ruby_version: !ruby/object:Gem::Requirement
|
@@ -79,6 +94,19 @@ rubyforge_project:
|
|
79
94
|
rubygems_version: 1.3.5
|
80
95
|
signing_key:
|
81
96
|
specification_version: 3
|
82
|
-
summary:
|
83
|
-
test_files:
|
84
|
-
|
97
|
+
summary: Chicago Metra parser and scheduler
|
98
|
+
test_files:
|
99
|
+
- test/integration/test_line_integration.rb
|
100
|
+
- test/functional/test_all_filters.rb
|
101
|
+
- test/timecop/lib/timecop/time_stack_item.rb
|
102
|
+
- test/timecop/lib/timecop/timecop.rb
|
103
|
+
- test/timecop/lib/timecop/time_extensions.rb
|
104
|
+
- test/timecop/lib/timecop.rb
|
105
|
+
- test/unit/test_time_extension.rb
|
106
|
+
- test/unit/test_train.rb
|
107
|
+
- test/unit/test_line.rb
|
108
|
+
- test/unit/test_metra.rb
|
109
|
+
- test/unit/test_stop.rb
|
110
|
+
- test/unit/test_parser.rb
|
111
|
+
- test/unit/test_cacher.rb
|
112
|
+
- test/test_helper.rb
|