tickle 1.2.0 → 2.0.0.rc1
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.
- checksums.yaml +4 -4
- data/.gitignore +2 -1
- data/.travis.yml +6 -4
- data/CHANGES.md +6 -24
- data/Gemfile +13 -4
- data/LICENCE +1 -1
- data/README.md +2 -2
- data/Rakefile +4 -7
- data/benchmarks/main.rb +71 -0
- data/lib/ext/array.rb +6 -0
- data/lib/ext/date_and_time.rb +64 -0
- data/lib/ext/string.rb +39 -0
- data/lib/tickle.rb +24 -117
- data/lib/tickle/filters.rb +58 -0
- data/lib/tickle/handler.rb +223 -80
- data/lib/tickle/helpers.rb +51 -0
- data/lib/tickle/patterns.rb +115 -0
- data/lib/tickle/repeater.rb +232 -116
- data/lib/tickle/tickle.rb +98 -220
- data/lib/tickle/tickled.rb +174 -0
- data/lib/tickle/token.rb +94 -0
- data/lib/tickle/version.rb +1 -1
- data/spec/helpers_spec.rb +36 -0
- data/spec/patterns_spec.rb +240 -0
- data/spec/spec_helper.rb +41 -0
- data/spec/tickle_spec.rb +543 -0
- data/spec/token_spec.rb +82 -0
- data/test/helper.rb +0 -2
- data/test/test_parsing.rb +14 -23
- data/tickle.gemspec +2 -9
- metadata +32 -113
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1d8b35a7f362cfb13d80ef3236f7f1f036cd7ebc
|
4
|
+
data.tar.gz: cec9f257100839052b8ea4e7b7a64fa560eebd01
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d3d2636db14e75ce64453d3bf42bfdb4f22fde65af54a01b5113a58447ab46e9530a39f16771265a28e345a16b22417ae6dca699c8828cd9d4825d756db38af0
|
7
|
+
data.tar.gz: e972f8974b427bcb5ad3be4333b4ac0951a82ada15f9cc8def342ff7414dd636e4169f757f579a3f17baa9c1578a265bc33865ee596f1e1aee2f2a4123be052b
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
@@ -1,12 +1,14 @@
|
|
1
1
|
language: ruby
|
2
2
|
rvm:
|
3
|
-
-
|
4
|
-
-
|
5
|
-
-
|
3
|
+
- 1.9.3
|
4
|
+
- 2.0.0
|
5
|
+
- 2.1.1
|
6
|
+
- ruby-head
|
7
|
+
- jruby-19mode
|
8
|
+
# - rbx-2.1.1 # not working right now, will look at later
|
6
9
|
|
7
10
|
# whitelist
|
8
11
|
branches:
|
9
12
|
only:
|
10
13
|
- master
|
11
14
|
- develop
|
12
|
-
- v1-branch
|
data/CHANGES.md
CHANGED
@@ -1,39 +1,21 @@
|
|
1
1
|
# CH CH CH CH CHANGES! #
|
2
2
|
|
3
|
-
##
|
3
|
+
## Saturday the 7th of November 2015, 2.0.0.rc1 ##
|
4
4
|
|
5
|
-
-
|
6
|
-
-
|
7
|
-
-
|
8
|
-
- Fixed bug with "today and" in the tests
|
9
|
-
- Fixed a bug previously fixed on the v2 branch in 2f2a32ce9 with @start
|
10
|
-
- Added Timecop because we're beyond the date chosen in the tests. Tempus fugit!
|
5
|
+
- Rewrite of the internals.
|
6
|
+
- API should be pretty much the same from the outside, but enough things on the inside change to warrant the major version bump.
|
7
|
+
- Chronic updated to 0.10.2.
|
11
8
|
|
12
9
|
----
|
13
10
|
|
14
|
-
##
|
15
|
-
|
16
|
-
* Bit of easier debugging added in.
|
17
|
-
* Bug fixes, thanks to https://github.com/dodomarocgenex for finding and helping with them.
|
18
|
-
|
19
|
-
----
|
20
|
-
|
21
|
-
## Wednesday the 22nd of February 2017, v1.1.0 ##
|
22
|
-
|
23
|
-
* Numerizer duplication removed. Thanks to https://github.com/bjonord.
|
24
|
-
* Some very minor changes to the project, no other code changes.
|
25
|
-
|
26
|
-
----
|
27
|
-
|
28
|
-
|
29
|
-
## Monday the 11th of November 2015, v1.0.2 ##
|
11
|
+
## Monday the 11th of November 2013, v1.0.2 ##
|
30
12
|
|
31
13
|
* Shoulda and simplecov aren't runtime dependencies, fixed that in the gemfile.
|
32
14
|
* Got the version number right this time ;-)
|
33
15
|
|
34
16
|
----
|
35
17
|
|
36
|
-
## Monday the 11th of November
|
18
|
+
## Monday the 11th of November 2013, v1.0.1 ##
|
37
19
|
|
38
20
|
* Moved library to new maintainer [https://github.com/yb66/tickle](@yb66)
|
39
21
|
* Moved library to [http://semver.org/](semver).
|
data/Gemfile
CHANGED
@@ -2,9 +2,18 @@ source "https://rubygems.org"
|
|
2
2
|
|
3
3
|
gemspec
|
4
4
|
|
5
|
-
group :
|
6
|
-
gem "
|
5
|
+
group :development do
|
6
|
+
gem "wirble"
|
7
|
+
gem "maruku"
|
8
|
+
gem "yard"
|
9
|
+
gem "pry-byebug"
|
10
|
+
gem "activesupport"
|
7
11
|
end
|
8
12
|
|
9
|
-
|
10
|
-
gem "
|
13
|
+
group :test do
|
14
|
+
gem "rspec"
|
15
|
+
gem "rspec-its"
|
16
|
+
gem "timecop"
|
17
|
+
gem "simplecov"
|
18
|
+
gem "rake"
|
19
|
+
end
|
data/LICENCE
CHANGED
data/README.md
CHANGED
@@ -11,7 +11,7 @@ Take a look at the `develop` branch where all this stuff will be happening.
|
|
11
11
|
|
12
12
|
Tickle is a natural language parser for recurring events.
|
13
13
|
|
14
|
-
Tickle is designed to be a complement to [Chronic](https://rubygems.org/gems/chronic) and can interpret things such as "every 2 days, every Sunday, Sundays, Weekly, etc.
|
14
|
+
Tickle is designed to be a complement to [Chronic](https://rubygems.org/gems/chronic) and can interpret things such as "every 2 days", "every Sunday", “Sundays", “Weekly", etc.
|
15
15
|
|
16
16
|
Tickle has one main method, `Tickle.parse`, which returns the next time the event should occur, at which point you simply call `Tickle.parse` again.
|
17
17
|
|
@@ -407,7 +407,7 @@ or if you're using Bundler, add this to the Gemfile:
|
|
407
407
|
|
408
408
|
Chronic gem:
|
409
409
|
|
410
|
-
`gem install
|
410
|
+
`gem install chronic`
|
411
411
|
|
412
412
|
thoughtbot's [shoulda](https://rubygems.org/gems/shoulda):
|
413
413
|
|
data/Rakefile
CHANGED
@@ -1,11 +1,8 @@
|
|
1
|
-
require '
|
2
|
-
|
3
|
-
|
4
|
-
test.pattern = 'test/**/test_*.rb'
|
5
|
-
test.verbose = true
|
6
|
-
end
|
1
|
+
require 'rspec/core/rake_task'
|
2
|
+
|
3
|
+
RSpec::Core::RakeTask.new(:spec)
|
7
4
|
|
8
|
-
task :default => :
|
5
|
+
task :default => :spec
|
9
6
|
|
10
7
|
|
11
8
|
desc "(Re-) generate documentation and place it in the docs/ dir. Open the index.html file in there to read it."
|
data/benchmarks/main.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'benchmark'
|
2
|
+
require_relative "../lib/tickle.rb"
|
3
|
+
warn Benchmark.measure {
|
4
|
+
Tickle.parse('day')
|
5
|
+
# Tickle.parse('week')
|
6
|
+
Tickle.parse('month')
|
7
|
+
Tickle.parse('year')
|
8
|
+
Tickle.parse('daily')
|
9
|
+
# Tickle.parse('weekly')
|
10
|
+
Tickle.parse('monthly')
|
11
|
+
Tickle.parse('yearly')
|
12
|
+
Tickle.parse('3 days')
|
13
|
+
# Tickle.parse('3 weeks')
|
14
|
+
Tickle.parse('3 months')
|
15
|
+
Tickle.parse('3 years')
|
16
|
+
Tickle.parse('other day')
|
17
|
+
# Tickle.parse('other week')
|
18
|
+
Tickle.parse('other month')
|
19
|
+
Tickle.parse('other year')
|
20
|
+
Tickle.parse('Monday')
|
21
|
+
Tickle.parse('Wednesday')
|
22
|
+
Tickle.parse('Friday')
|
23
|
+
Tickle.parse('February', {:start=>Time.parse("2020-04-01 00:00:00 +0000"), :now=>Time.parse("2020-04-01 00:00:00 +0000")})
|
24
|
+
Tickle.parse('May', {:start=>Time.parse("2020-04-01 00:00:00 +0000"), :now=>Time.parse("2020-04-01 00:00:00 +0000")})
|
25
|
+
Tickle.parse('june', {:start=>Time.parse("2020-04-01 00:00:00 +0000"), :now=>Time.parse("2020-04-01 00:00:00 +0000")})
|
26
|
+
Tickle.parse('beginning of the week')
|
27
|
+
Tickle.parse('middle of the week')
|
28
|
+
Tickle.parse('end of the week')
|
29
|
+
Tickle.parse('beginning of the month', {:start=>Time.parse("2020-04-01 00:00:00 +0000"), :now=>Time.parse("2020-04-01 00:00:00 +0000")})
|
30
|
+
Tickle.parse('middle of the month', {:start=>Time.parse("2020-04-01 00:00:00 +0000"), :now=>Time.parse("2020-04-01 00:00:00 +0000")})
|
31
|
+
Tickle.parse('end of the month', {:start=>Time.parse("2020-04-01 00:00:00 +0000"), :now=>Time.parse("2020-04-01 00:00:00 +0000")})
|
32
|
+
Tickle.parse('beginning of the year', {:start=>Time.parse("2020-04-01 00:00:00 +0000"), :now=>Time.parse("2020-04-01 00:00:00 +0000")})
|
33
|
+
Tickle.parse('middle of the year', {:start=>Time.parse("2020-04-01 00:00:00 +0000"), :now=>Time.parse("2020-04-01 00:00:00 +0000")})
|
34
|
+
Tickle.parse('end of the year', {:start=>Time.parse("2020-04-01 00:00:00 +0000"), :now=>Time.parse("2020-04-01 00:00:00 +0000")})
|
35
|
+
Tickle.parse('the 3rd of May', {:start=>Time.parse("2020-04-01 00:00:00 +0000"), :now=>Time.parse("2020-04-01 00:00:00 +0000")})
|
36
|
+
Tickle.parse('the 3rd of February', {:start=>Time.parse("2020-04-01 00:00:00 +0000"), :now=>Time.parse("2020-04-01 00:00:00 +0000")})
|
37
|
+
Tickle.parse('the 3rd of February 2022', {:start=>Time.parse("2020-04-01 00:00:00 +0000"), :now=>Time.parse("2020-04-01 00:00:00 +0000")})
|
38
|
+
Tickle.parse('the 3rd of Feb 2022', {:start=>Time.parse("2020-04-01 00:00:00 +0000"), :now=>Time.parse("2020-04-01 00:00:00 +0000")})
|
39
|
+
Tickle.parse('the 4th of the month', {:start=>Time.parse("2020-04-01 00:00:00 +0000"), :now=>Time.parse("2020-04-01 00:00:00 +0000")})
|
40
|
+
Tickle.parse('the 10th of the month', {:start=>Time.parse("2020-04-01 00:00:00 +0000"), :now=>Time.parse("2020-04-01 00:00:00 +0000")})
|
41
|
+
Tickle.parse('the tenth of the month', {:start=>Time.parse("2020-04-01 00:00:00 +0000"), :now=>Time.parse("2020-04-01 00:00:00 +0000")})
|
42
|
+
Tickle.parse('first', {:start=>Time.parse("2020-04-01 00:00:00 +0000"), :now=>Time.parse("2020-04-01 00:00:00 +0000")})
|
43
|
+
Tickle.parse('the first of the month', {:start=>Time.parse("2020-04-01 00:00:00 +0000"), :now=>Time.parse("2020-04-01 00:00:00 +0000")})
|
44
|
+
Tickle.parse('the thirtieth', {:start=>Time.parse("2020-04-01 00:00:00 +0000"), :now=>Time.parse("2020-04-01 00:00:00 +0000")})
|
45
|
+
Tickle.parse('the fifth', {:start=>Time.parse("2020-04-01 00:00:00 +0000"), :now=>Time.parse("2020-04-01 00:00:00 +0000")})
|
46
|
+
Tickle.parse('the 1st Wednesday of the month', {:start=>Time.parse("2020-04-01 00:00:00 +0000"), :now=>Time.parse("2020-04-01 00:00:00 +0000")})
|
47
|
+
Tickle.parse('the 3rd Sunday of May', {:start=>Time.parse("2020-04-01 00:00:00 +0000"), :now=>Time.parse("2020-04-01 00:00:00 +0000")})
|
48
|
+
Tickle.parse('the 3rd Sunday of the month', {:start=>Time.parse("2020-04-01 00:00:00 +0000"), :now=>Time.parse("2020-04-01 00:00:00 +0000")})
|
49
|
+
Tickle.parse('the 23rd of June', {:start=>Time.parse("2020-04-01 00:00:00 +0000"), :now=>Time.parse("2020-04-01 00:00:00 +0000")})
|
50
|
+
Tickle.parse('the twenty third of June', {:start=>Time.parse("2020-04-01 00:00:00 +0000"), :now=>Time.parse("2020-04-01 00:00:00 +0000")})
|
51
|
+
Tickle.parse('the thirty first of July', {:start=>Time.parse("2020-04-01 00:00:00 +0000"), :now=>Time.parse("2020-04-01 00:00:00 +0000")})
|
52
|
+
Tickle.parse('the twenty first', {:start=>Time.parse("2020-04-01 00:00:00 +0000"), :now=>Time.parse("2020-04-01 00:00:00 +0000")})
|
53
|
+
Tickle.parse('the twenty first of the month', {:start=>Time.parse("2020-04-01 00:00:00 +0000"), :now=>Time.parse("2020-04-01 00:00:00 +0000")})
|
54
|
+
Tickle.parse('starting today and ending one week from now')
|
55
|
+
Tickle.parse('starting tomorrow and ending one week from now')
|
56
|
+
Tickle.parse('starting Monday repeat every month')
|
57
|
+
Tickle.parse('starting May 13th repeat every week')
|
58
|
+
Tickle.parse('starting May 13th repeat every other day')
|
59
|
+
Tickle.parse('every other day starts May 13th')
|
60
|
+
Tickle.parse('every other day starts May 13')
|
61
|
+
Tickle.parse('every other day starting May 13th')
|
62
|
+
Tickle.parse('every other day starting May 13')
|
63
|
+
Tickle.parse('every week starts this wednesday')
|
64
|
+
Tickle.parse('every week starting this wednesday')
|
65
|
+
Tickle.parse('every other day starting May 1st 2021')
|
66
|
+
Tickle.parse('every other day starting May 1 2021')
|
67
|
+
Tickle.parse('every other week starting this Sunday')
|
68
|
+
Tickle.parse('every week starting this wednesday until May 13th')
|
69
|
+
Tickle.parse('every week starting this wednesday ends May 13th')
|
70
|
+
Tickle.parse('every week starting this wednesday ending May 13th')
|
71
|
+
}
|
data/lib/ext/array.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
class Date
|
2
|
+
# returns the days in the sending month
|
3
|
+
def days_in_month
|
4
|
+
d,m,y = mday,month,year
|
5
|
+
d += 1 while Date.valid_civil?(y,m,d)
|
6
|
+
d - 1
|
7
|
+
end
|
8
|
+
|
9
|
+
|
10
|
+
# no idea what this is for
|
11
|
+
def bump(attr, amount=nil)
|
12
|
+
amount ||= 1
|
13
|
+
case attr
|
14
|
+
when :day then
|
15
|
+
Date.civil(self.year, self.month, self.day + amount)
|
16
|
+
when :wday then
|
17
|
+
amount = Date::ABBR_DAYNAMES.index(amount) if amount.is_a?(String)
|
18
|
+
raise Exception, "specified day of week invalid. Use #{Date::ABBR_DAYNAMES}" unless amount
|
19
|
+
diff = (amount > self.wday) ? (amount - self.wday) : (7 - (self.wday - amount))
|
20
|
+
Date.civil(self.year, self.month, self.day + diff)
|
21
|
+
when :week then
|
22
|
+
Date.civil(self.year, self.month, self.day + (7*amount))
|
23
|
+
when :month then
|
24
|
+
Date.civil(self.year, self.month+amount, self.day)
|
25
|
+
when :year then
|
26
|
+
Date.civil(self.year + amount, self.month, self.day)
|
27
|
+
when :sec then
|
28
|
+
Date.civil(self.year, self.month, self.day, self.hour, self.minute, self.sec + amount)
|
29
|
+
else
|
30
|
+
raise Exception, "type \"#{attr}\" not supported."
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class Time
|
36
|
+
|
37
|
+
# same again, no idea what this is for
|
38
|
+
def bump(attr, amount=nil)
|
39
|
+
amount ||= 1
|
40
|
+
case attr
|
41
|
+
when :sec then
|
42
|
+
Time.local(self.year, self.month, self.day, self.hour, self.min, self.sec + amount)
|
43
|
+
when :min then
|
44
|
+
Time.local(self.year, self.month, self.day, self.hour, self.min + amount, self.sec)
|
45
|
+
when :hour then
|
46
|
+
Time.local(self.year, self.month, self.day, self.hour + amount, self.min, self.sec)
|
47
|
+
when :day then
|
48
|
+
Time.local(self.year, self.month, self.day + amount, self.hour, self.min, self.sec)
|
49
|
+
when :wday then
|
50
|
+
amount = Time::RFC2822_DAY_NAME.index(amount) if amount.is_a?(String)
|
51
|
+
raise Exception, "specified day of week invalid. Use #{Time::RFC2822_DAY_NAME}" unless amount
|
52
|
+
diff = (amount > self.wday) ? (amount - self.wday) : (7 - (self.wday - amount))
|
53
|
+
Time.local(self.year, self.month, self.day + diff, self.hour, self.min, self.sec)
|
54
|
+
when :week then
|
55
|
+
Time.local(self.year, self.month, self.day + (amount * 7), self.hour, self.min, self.sec)
|
56
|
+
when :month then
|
57
|
+
Time.local(self.year, self.month + amount, self.day, self.hour, self.min, self.sec)
|
58
|
+
when :year then
|
59
|
+
Time.local(self.year + amount, self.month, self.day, self.hour, self.min, self.sec)
|
60
|
+
else
|
61
|
+
raise Exception, "type \"#{attr}\" not supported."
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
data/lib/ext/string.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
module Tickle
|
2
|
+
class Ordinal < ::String
|
3
|
+
# returns true if the sending string is a text or numeric ordinal (e.g. first or 1st)
|
4
|
+
def is_ordinal?
|
5
|
+
scanner = %w{first second third fourth fifth sixth seventh eighth ninth tenth eleventh twelfth thirteenth fourteenth fifteenth sixteenth seventeenth eighteenth nineteenth twenty thirty thirtieth}
|
6
|
+
regex = /\b(\d*)(st|nd|rd|th)\b/
|
7
|
+
!(self =~ regex).nil? || scanner.include?(self.downcase)
|
8
|
+
end
|
9
|
+
|
10
|
+
def ordinal_as_number
|
11
|
+
return self unless self.is_ordinal?
|
12
|
+
scanner = {/first/ => '1st',
|
13
|
+
/second/ => '2nd',
|
14
|
+
/third/ => '3rd',
|
15
|
+
/fourth/ => '4th',
|
16
|
+
/fifth/ => '5th',
|
17
|
+
/sixth/ => '6th',
|
18
|
+
/seventh/ => '7th',
|
19
|
+
/eighth/ => '8th',
|
20
|
+
/ninth/ => '9th',
|
21
|
+
/tenth/ => '10th',
|
22
|
+
/eleventh/ => '11th',
|
23
|
+
/twelfth/ => '12th',
|
24
|
+
/thirteenth/ => '13th',
|
25
|
+
/fourteenth/ => '14th',
|
26
|
+
/fifteenth/ => '15th',
|
27
|
+
/sixteenth/ => '16th',
|
28
|
+
/seventeenth/ => '17th',
|
29
|
+
/eighteenth/ => '18th',
|
30
|
+
/nineteenth/ => '19th',
|
31
|
+
/twentieth/ => '20th',
|
32
|
+
/thirtieth/ => '30th',
|
33
|
+
}
|
34
|
+
result = self
|
35
|
+
scanner.keys.each {|scanner_item| result = scanner[scanner_item] if scanner_item =~ self}
|
36
|
+
return result.gsub(/\b(\d*)(st|nd|rd|th)\b/, '\1')
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/tickle.rb
CHANGED
@@ -9,29 +9,40 @@
|
|
9
9
|
|
10
10
|
$LOAD_PATH.unshift(File.dirname(__FILE__)) # For use/testing when no gem is installed
|
11
11
|
|
12
|
-
if ENV["DEBUG"]
|
13
|
-
warn "DEBUG MODE ON"
|
14
|
-
require 'pry-byebug'
|
15
|
-
require 'pry-state'
|
16
|
-
binding.pry
|
17
|
-
end
|
18
|
-
|
19
12
|
require 'date'
|
20
13
|
require 'time'
|
21
|
-
require '
|
14
|
+
require 'chronic'
|
22
15
|
|
23
16
|
require 'tickle/tickle'
|
24
17
|
require 'tickle/handler'
|
25
18
|
require 'tickle/repeater'
|
19
|
+
require_relative "tickle/tickled.rb"
|
20
|
+
require_relative "ext/array.rb"
|
21
|
+
require_relative "ext/date_and_time.rb"
|
22
|
+
require_relative "ext/string.rb"
|
23
|
+
|
24
|
+
# these are required not because they're used by the library
|
25
|
+
# but because they clobber so much that testing
|
26
|
+
# without them will miss possible problems
|
27
|
+
require 'active_support/core_ext/string/conversions'
|
28
|
+
require 'active_support/core_ext/date/calculations'
|
29
|
+
require 'active_support/core_ext/date_time/calculations'
|
30
|
+
require 'active_support/core_ext/time/calculations'
|
26
31
|
|
27
|
-
|
32
|
+
# Tickle is a natural language parser for recurring events.
|
33
|
+
module Tickle
|
28
34
|
|
29
|
-
def self.
|
35
|
+
def self.parse(asked, options = {})
|
36
|
+
# check to see if a datetime was passed
|
37
|
+
# if so, give it back
|
38
|
+
# TODO Consider converting to a Tickled
|
39
|
+
return asked if asked.respond_to? :day
|
30
40
|
|
31
|
-
|
32
|
-
|
41
|
+
tickled = Tickled.new asked.dup, options
|
42
|
+
_parse tickled
|
33
43
|
end
|
34
44
|
|
45
|
+
|
35
46
|
def self.is_date(str)
|
36
47
|
begin
|
37
48
|
Date.parse(str.to_s)
|
@@ -40,108 +51,4 @@ module Tickle #:nodoc:
|
|
40
51
|
return false
|
41
52
|
end
|
42
53
|
end
|
43
|
-
end
|
44
|
-
|
45
|
-
class Date #:nodoc:
|
46
|
-
# returns the days in the sending month
|
47
|
-
def days_in_month
|
48
|
-
d,m,y = mday,month,year
|
49
|
-
d += 1 while Date.valid_civil?(y,m,d)
|
50
|
-
d - 1
|
51
|
-
end
|
52
|
-
|
53
|
-
def bump(attr, amount=nil)
|
54
|
-
amount ||= 1
|
55
|
-
case attr
|
56
|
-
when :day then
|
57
|
-
Date.civil(self.year, self.month, self.day).next_day(amount)
|
58
|
-
when :wday then
|
59
|
-
amount = Date::ABBR_DAYNAMES.index(amount) if amount.is_a?(String)
|
60
|
-
raise Exception, "specified day of week invalid. Use #{Date::ABBR_DAYNAMES}" unless amount
|
61
|
-
diff = (amount > self.wday) ? (amount - self.wday) : (7 - (self.wday - amount))
|
62
|
-
Date.civil(self.year, self.month, self.day).next_day(diff)
|
63
|
-
when :week then
|
64
|
-
Date.civil(self.year, self.month, self.day).next_day(7*amount)
|
65
|
-
when :month then
|
66
|
-
Date.civil(self.year, self.month, self.day).next_month(amount)
|
67
|
-
when :year then
|
68
|
-
Date.civil(self.year, self.month, self.day).next_year(amount)
|
69
|
-
else
|
70
|
-
raise Exception, "type \"#{attr}\" not supported."
|
71
|
-
end
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
class Time #:nodoc:
|
76
|
-
def bump(attr, amount=nil)
|
77
|
-
amount ||= 1
|
78
|
-
case attr
|
79
|
-
when :sec then
|
80
|
-
Time.local(self.year, self.month, self.day, self.hour, self.min, self.sec) + amount
|
81
|
-
when :min then
|
82
|
-
Time.local(self.year, self.month, self.day, self.hour, self.min, self.sec) + (amount * 60)
|
83
|
-
when :hour then
|
84
|
-
Time.local(self.year, self.month, self.day, self.hour, self.min, self.sec) + (amount * 60 * 60)
|
85
|
-
when :day then
|
86
|
-
Time.local(self.year, self.month, self.day, self.hour, self.min, self.sec) + (amount * 60 * 60 * 24)
|
87
|
-
when :wday then
|
88
|
-
amount = Time::RFC2822_DAY_NAME.index(amount) if amount.is_a?(String)
|
89
|
-
raise Exception, "specified day of week invalid. Use #{Time::RFC2822_DAY_NAME}" unless amount
|
90
|
-
diff = (amount > self.wday) ? (amount - self.wday) : (7 - (self.wday - amount))
|
91
|
-
DateTime.civil(self.year, self.month, self.day, self.hour, self.min, self.sec, self.zone).next_day(diff)
|
92
|
-
when :week then
|
93
|
-
DateTime.civil(self.year, self.month, self.day, self.hour, self.min, self.sec, self.zone).next_day(amount * 7)
|
94
|
-
when :month then
|
95
|
-
DateTime.civil(self.year, self.month, self.day, self.hour, self.min, self.sec, self.zone).next_month(amount)
|
96
|
-
when :year then
|
97
|
-
DateTime.civil(self.year, self.month, self.day, self.hour, self.min, self.sec, self.zone).next_year(amount)
|
98
|
-
else
|
99
|
-
raise Exception, "type \"#{attr}\" not supported."
|
100
|
-
end.to_time.localtime
|
101
|
-
end
|
102
|
-
end
|
103
|
-
|
104
|
-
class String #:nodoc:
|
105
|
-
# returns true if the sending string is a text or numeric ordinal (e.g. first or 1st)
|
106
|
-
def is_ordinal?
|
107
|
-
scanner = %w{first second third fourth fifth sixth seventh eighth ninth tenth eleventh twelfth thirteenth fourteenth fifteenth sixteenth seventeenth eighteenth nineteenth twenty thirty thirtieth}
|
108
|
-
regex = /\b(\d*)(st|nd|rd|th)\b/
|
109
|
-
!(self =~ regex).nil? || scanner.include?(self.downcase)
|
110
|
-
end
|
111
|
-
|
112
|
-
def ordinal_as_number
|
113
|
-
return self unless self.is_ordinal?
|
114
|
-
scanner = {/first/ => '1st',
|
115
|
-
/second/ => '2nd',
|
116
|
-
/third/ => '3rd',
|
117
|
-
/fourth/ => '4th',
|
118
|
-
/fifth/ => '5th',
|
119
|
-
/sixth/ => '6th',
|
120
|
-
/seventh/ => '7th',
|
121
|
-
/eighth/ => '8th',
|
122
|
-
/ninth/ => '9th',
|
123
|
-
/tenth/ => '10th',
|
124
|
-
/eleventh/ => '11th',
|
125
|
-
/twelfth/ => '12th',
|
126
|
-
/thirteenth/ => '13th',
|
127
|
-
/fourteenth/ => '14th',
|
128
|
-
/fifteenth/ => '15th',
|
129
|
-
/sixteenth/ => '16th',
|
130
|
-
/seventeenth/ => '17th',
|
131
|
-
/eighteenth/ => '18th',
|
132
|
-
/nineteenth/ => '19th',
|
133
|
-
/twentieth/ => '20th',
|
134
|
-
/thirtieth/ => '30th',
|
135
|
-
}
|
136
|
-
result = self
|
137
|
-
scanner.keys.each {|scanner_item| result = scanner[scanner_item] if scanner_item =~ self}
|
138
|
-
return result.gsub(/\b(\d*)(st|nd|rd|th)\b/, '\1')
|
139
|
-
end
|
140
|
-
end
|
141
|
-
|
142
|
-
class Array #:nodoc:
|
143
|
-
# compares two arrays to determine if they both contain the same elements
|
144
|
-
def same?(y)
|
145
|
-
self.sort == y.sort
|
146
|
-
end
|
147
|
-
end
|
54
|
+
end
|