medjool 0.1
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/.gitignore +18 -0
- data/LICENSE +20 -0
- data/README.md +24 -0
- data/Rakefile +46 -0
- data/lib/medjool.rb +14 -0
- data/lib/medjool/parser.rb +102 -0
- data/lib/medjool/regexs.rb +10 -0
- data/medjool.gemspec +21 -0
- data/test/helper.rb +15 -0
- data/test/test_medjool.rb +53 -0
- metadata +120 -0
data/.gitignore
ADDED
data/LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2013 andygeers
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
|
7
|
+
the Software without restriction, including without limitation the rights to
|
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
9
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
|
10
|
+
subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
17
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
18
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
19
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
20
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
medjool
|
|
2
|
+
=======
|
|
3
|
+
Date parsing with context.
|
|
4
|
+
|
|
5
|
+
The Date.parse in ActiveSupport method is awesome. The only thing it lacks is the ability to provide a bit more context to override what 'now' is considered to be. By default, Medjool defers date parsing directly to Date.parse, but on subsequent calls it will make sure that the result is after the previously parsed date.
|
|
6
|
+
|
|
7
|
+
Usage
|
|
8
|
+
=====
|
|
9
|
+
|
|
10
|
+
parser = Medjool::Parser.new({:now => "2013-08-26".to_date})
|
|
11
|
+
parser.parse("Tuesday") => "2013-08-27"
|
|
12
|
+
parser.parse("Wednesday") => "2013-08-28"
|
|
13
|
+
parser.parse("Tuesday") => "2013-09-03"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
Tests
|
|
17
|
+
=====
|
|
18
|
+
To run the tests, type:
|
|
19
|
+
|
|
20
|
+
rake test
|
|
21
|
+
|
|
22
|
+
License
|
|
23
|
+
=======
|
|
24
|
+
Medjool is licensed under an MIT license.
|
data/Rakefile
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
require 'date'
|
|
2
|
+
|
|
3
|
+
def version
|
|
4
|
+
contents = File.read File.expand_path('../lib/medjool.rb', __FILE__)
|
|
5
|
+
contents[/VERSION = "([^"]+)"/, 1]
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
task :test do
|
|
9
|
+
$:.unshift './test'
|
|
10
|
+
Dir.glob('test/test_*.rb').each { |t| require File.basename(t) }
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
desc "Generate RCov test coverage and open in your browser"
|
|
14
|
+
task :coverage do
|
|
15
|
+
require 'rcov'
|
|
16
|
+
sh "rm -fr coverage"
|
|
17
|
+
sh "rcov test/test_*.rb"
|
|
18
|
+
sh "open coverage/index.html"
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
desc "Open an irb session preloaded with this library"
|
|
22
|
+
task :console do
|
|
23
|
+
sh "irb -Ilib -rchronic"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
desc "Release Chronic version #{version}"
|
|
27
|
+
task :release => :build do
|
|
28
|
+
unless `git branch` =~ /^\* master$/
|
|
29
|
+
puts "You must be on the master branch to release!"
|
|
30
|
+
exit!
|
|
31
|
+
end
|
|
32
|
+
sh "git commit --allow-empty -a -m 'Release #{version}'"
|
|
33
|
+
sh "git tag v#{version}"
|
|
34
|
+
sh "git push origin master"
|
|
35
|
+
sh "git push origin v#{version}"
|
|
36
|
+
sh "gem push pkg/chronic-#{version}.gem"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
desc "Build a gem from the gemspec"
|
|
40
|
+
task :build do
|
|
41
|
+
sh "mkdir -p pkg"
|
|
42
|
+
sh "gem build chronic.gemspec"
|
|
43
|
+
sh "mv chronic-#{version}.gem pkg"
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
task :default => :test
|
data/lib/medjool.rb
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
require 'active_support/core_ext'
|
|
2
|
+
|
|
3
|
+
class Medjool::Parser
|
|
4
|
+
|
|
5
|
+
attr_reader :context
|
|
6
|
+
|
|
7
|
+
def initialize(context = {})
|
|
8
|
+
@context = context
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def parse(text)
|
|
12
|
+
if Medjool::DATE_MATCHER.match(text)
|
|
13
|
+
base_date = Date.parse(text)
|
|
14
|
+
|
|
15
|
+
unless @context[:now]
|
|
16
|
+
# In the absence of context, always fall back on the default guess
|
|
17
|
+
guess_date = base_date
|
|
18
|
+
else
|
|
19
|
+
# Determine the ambiguity level for this data
|
|
20
|
+
ambiguity = determine_ambiguity(text)
|
|
21
|
+
|
|
22
|
+
now = @context[:now].to_date
|
|
23
|
+
|
|
24
|
+
case ambiguity
|
|
25
|
+
when :none
|
|
26
|
+
return base_date
|
|
27
|
+
|
|
28
|
+
when :weekly
|
|
29
|
+
# Pick the nearest week that meets the context
|
|
30
|
+
guess_date = now + (base_date.wday - now.wday).days - 1.week
|
|
31
|
+
|
|
32
|
+
while guess_date < now
|
|
33
|
+
guess_date += 1.week
|
|
34
|
+
end
|
|
35
|
+
when :monthly
|
|
36
|
+
# Pick the nearest month where the provided day of the month meets the context
|
|
37
|
+
y = now.year
|
|
38
|
+
m = now.month - 1
|
|
39
|
+
guess_date = nil
|
|
40
|
+
|
|
41
|
+
while guess_date.nil? || guess_date < now
|
|
42
|
+
m += 1
|
|
43
|
+
if m > 12
|
|
44
|
+
m = 1
|
|
45
|
+
y += 1
|
|
46
|
+
end
|
|
47
|
+
begin
|
|
48
|
+
guess_date = Date.new(y, m, base_date.day)
|
|
49
|
+
rescue ArgumentError
|
|
50
|
+
# This might happen e.g. 31st Feb,
|
|
51
|
+
# so just carry on to the next month
|
|
52
|
+
next
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
when :yearly
|
|
57
|
+
# Skip ahead a year at a time unless we meet the context
|
|
58
|
+
guess_date = Date.new(now.year, base_date.month, base_date.day)
|
|
59
|
+
while guess_date < now
|
|
60
|
+
guess_date += 1.year
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
@context[:now] = guess_date
|
|
67
|
+
return guess_date
|
|
68
|
+
else
|
|
69
|
+
return nil
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
protected
|
|
74
|
+
|
|
75
|
+
def determine_ambiguity(text)
|
|
76
|
+
if /#{Medjool::DM_DM_YYYY_MATCHER}|#{Medjool::YYYY_MM_DD_MATCHER}/.match(text)
|
|
77
|
+
# Plain as day
|
|
78
|
+
return :none
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Work out how much detail we have
|
|
82
|
+
data_present = {
|
|
83
|
+
:day_name => Medjool::DAYNAME_MATCHER.match(text),
|
|
84
|
+
:month => Medjool::MONTH_MATCHER.match(text),
|
|
85
|
+
:ordinal => Medjool::ORDINAL_MATCHER.match(text)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if data_present[:month] && data_present[:ordinal]
|
|
89
|
+
# 1st December
|
|
90
|
+
return :yearly
|
|
91
|
+
elsif data_present[:ordinal]
|
|
92
|
+
# Monday 1 or just 1st
|
|
93
|
+
return :monthly
|
|
94
|
+
elsif data_present[:day_name] && !data_present[:month]
|
|
95
|
+
# Tuesday
|
|
96
|
+
return :weekly
|
|
97
|
+
else
|
|
98
|
+
# Unknown
|
|
99
|
+
return nil
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
module Medjool
|
|
2
|
+
DAYNAME_MATCHER = /(\s*Mon(day)?,?\s*|\s*Tue(s(day)?)?,?\s*|\s*Wed(nesday)?,?\s*|\s*Thur(s(day)?)?,?\s*|\s*Fri(day)?,?\s*|\s*Sat(urday)?,?\s*|\s*Sun(day)?,?\s*)/
|
|
3
|
+
MONTH_MATCHER = /\s*(Jan(uary)?|Feb(ruary)?|Mar(ch)?|Apr(il)?|May|June?|July?|Aug(ust)?|Sept(ember)?|Oct(ober)?|Nov(ember)?|Dec(ember)?),?\s*/
|
|
4
|
+
YYYY_MM_DD_MATCHER = /(\s*[0-9]{4}-[0-9]{2}-[0-9]{2}\s*)/
|
|
5
|
+
DM_DM_YYYY_MATCHER = /(\s*[0-9]{2}[-\/][0-9]{2}[-\/][0-9]{2,4}\s*)/
|
|
6
|
+
ORDINAL_MATCHER = /(\s*([0-9]{1,2}(st|rd|nd|th)?),?\s*)/
|
|
7
|
+
YEAR_MATCHER = /(\s*([0-9]{4}\s*|\s*'?[0-9]{2}),?\s*)/
|
|
8
|
+
TEXT_DATE_MATCHER = /(#{DAYNAME_MATCHER}|#{ORDINAL_MATCHER}|#{YEAR_MATCHER}|#{MONTH_MATCHER})/
|
|
9
|
+
DATE_MATCHER = /^(#{TEXT_DATE_MATCHER}+|#{DM_DM_YYYY_MATCHER}|#{YYYY_MM_DD_MATCHER})/
|
|
10
|
+
end
|
data/medjool.gemspec
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
$:.unshift File.expand_path('../lib', __FILE__)
|
|
2
|
+
require 'medjool'
|
|
3
|
+
|
|
4
|
+
Gem::Specification.new do |s|
|
|
5
|
+
s.name = 'medjool'
|
|
6
|
+
s.version = Medjool::VERSION
|
|
7
|
+
#s.rubyforge_project = 'medjool'
|
|
8
|
+
s.summary = 'Date parsing with context'
|
|
9
|
+
s.description = 'Medjool is a date parser written in pure Ruby, which processes dates within a context.'
|
|
10
|
+
s.authors = ['Andy Geers']
|
|
11
|
+
s.email = ['andy@geero.net']
|
|
12
|
+
s.homepage = 'http://github.com/andygeers/medjool'
|
|
13
|
+
s.rdoc_options = ['--charset=UTF-8']
|
|
14
|
+
s.extra_rdoc_files = %w[README.md LICENSE]
|
|
15
|
+
s.files = `git ls-files`.split("\n")
|
|
16
|
+
s.test_files = `git ls-files -- test`.split("\n")
|
|
17
|
+
|
|
18
|
+
s.add_dependency 'activesupport', '~> 3.2.12'
|
|
19
|
+
s.add_development_dependency 'rake'
|
|
20
|
+
s.add_development_dependency 'minitest', '~> 5.0'
|
|
21
|
+
end
|
data/test/helper.rb
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
unless defined? Medjool
|
|
2
|
+
$:.unshift File.expand_path('../../lib', __FILE__)
|
|
3
|
+
require 'medjool'
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
require 'minitest'
|
|
7
|
+
require 'minitest/autorun'
|
|
8
|
+
require 'active_support/core_ext/string'
|
|
9
|
+
require 'timecop'
|
|
10
|
+
|
|
11
|
+
class TestCase < MiniTest::Test
|
|
12
|
+
def self.test(name, &block)
|
|
13
|
+
define_method("test_#{name.gsub(/\W/, '_')}", &block) if block
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
require 'helper'
|
|
2
|
+
|
|
3
|
+
class TestMedjool < TestCase
|
|
4
|
+
|
|
5
|
+
def setup
|
|
6
|
+
# Friday 23rd August 2013
|
|
7
|
+
Timecop.freeze("2013-08-23".to_date)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def teardown
|
|
11
|
+
Timecop.return
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def test_parse_without_context
|
|
15
|
+
@variations = ["Monday", "1st July", "2nd July 2010", "1 July 2009", "Monday 2nd July 2009", "Monday 2nd", "Monday 2", "Monday 2 July"]
|
|
16
|
+
@variations.each do |t|
|
|
17
|
+
assert_equal Date.parse(t), Medjool.parse(t)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def test_parse_with_context
|
|
22
|
+
assert_equal Date.parse("Monday") + 1.week, Medjool.parse("Monday", { :now => 1.week.from_now })
|
|
23
|
+
|
|
24
|
+
assert_equal "2014-03-31".to_date, Medjool.parse("31st", { :now => "2014-02-01" })
|
|
25
|
+
|
|
26
|
+
assert_equal "2014-03-31".to_date, Medjool.parse("31st", { :now => "2014-02-01" })
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def test_day_month_and_ordinal
|
|
30
|
+
assert_equal "2013-10-01".to_date, Medjool.parse("Monday 1st October", {:now => "2013-08-23".to_date})
|
|
31
|
+
assert_equal "2014-10-01".to_date, Medjool.parse("Monday 1st October", {:now => "2013-10-02".to_date})
|
|
32
|
+
|
|
33
|
+
assert_equal "2013-10-01".to_date, Medjool.parse("1st October", {:now => "2013-08-23".to_date})
|
|
34
|
+
assert_equal "2014-10-01".to_date, Medjool.parse("1st October", {:now => "2013-10-02".to_date})
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def test_ordinal_only
|
|
38
|
+
assert_equal "2013-09-01".to_date, Medjool.parse("1st", {:now => "2013-08-23".to_date})
|
|
39
|
+
assert_equal "2013-08-01".to_date, Medjool.parse("1st", {:now => "2013-08-01".to_date})
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def test_day_only
|
|
43
|
+
assert_equal "2013-08-27".to_date, Medjool.parse("Tuesday", {:now => "2013-08-26".to_date})
|
|
44
|
+
assert_equal "2013-08-27".to_date, Medjool.parse("Tuesday", {:now => "2013-08-27".to_date})
|
|
45
|
+
assert_equal "2013-09-03".to_date, Medjool.parse("Tuesday", {:now => "2013-08-28".to_date})
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def test_absolute_dates
|
|
49
|
+
parser = Medjool::Parser.new
|
|
50
|
+
assert_equal "2013-07-06".to_date, parser.parse("06/07/2013")
|
|
51
|
+
assert_equal "2013-07-06".to_date, parser.parse("06/07/2013")
|
|
52
|
+
end
|
|
53
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: medjool
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
hash: 9
|
|
5
|
+
prerelease:
|
|
6
|
+
segments:
|
|
7
|
+
- 0
|
|
8
|
+
- 1
|
|
9
|
+
version: "0.1"
|
|
10
|
+
platform: ruby
|
|
11
|
+
authors:
|
|
12
|
+
- Andy Geers
|
|
13
|
+
autorequire:
|
|
14
|
+
bindir: bin
|
|
15
|
+
cert_chain: []
|
|
16
|
+
|
|
17
|
+
date: 2013-08-26 00:00:00 Z
|
|
18
|
+
dependencies:
|
|
19
|
+
- !ruby/object:Gem::Dependency
|
|
20
|
+
name: activesupport
|
|
21
|
+
prerelease: false
|
|
22
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
|
23
|
+
none: false
|
|
24
|
+
requirements:
|
|
25
|
+
- - ~>
|
|
26
|
+
- !ruby/object:Gem::Version
|
|
27
|
+
hash: 23
|
|
28
|
+
segments:
|
|
29
|
+
- 3
|
|
30
|
+
- 2
|
|
31
|
+
- 12
|
|
32
|
+
version: 3.2.12
|
|
33
|
+
type: :runtime
|
|
34
|
+
version_requirements: *id001
|
|
35
|
+
- !ruby/object:Gem::Dependency
|
|
36
|
+
name: rake
|
|
37
|
+
prerelease: false
|
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
|
39
|
+
none: false
|
|
40
|
+
requirements:
|
|
41
|
+
- - ">="
|
|
42
|
+
- !ruby/object:Gem::Version
|
|
43
|
+
hash: 3
|
|
44
|
+
segments:
|
|
45
|
+
- 0
|
|
46
|
+
version: "0"
|
|
47
|
+
type: :development
|
|
48
|
+
version_requirements: *id002
|
|
49
|
+
- !ruby/object:Gem::Dependency
|
|
50
|
+
name: minitest
|
|
51
|
+
prerelease: false
|
|
52
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
|
53
|
+
none: false
|
|
54
|
+
requirements:
|
|
55
|
+
- - ~>
|
|
56
|
+
- !ruby/object:Gem::Version
|
|
57
|
+
hash: 31
|
|
58
|
+
segments:
|
|
59
|
+
- 5
|
|
60
|
+
- 0
|
|
61
|
+
version: "5.0"
|
|
62
|
+
type: :development
|
|
63
|
+
version_requirements: *id003
|
|
64
|
+
description: Medjool is a date parser written in pure Ruby, which processes dates within a context.
|
|
65
|
+
email:
|
|
66
|
+
- andy@geero.net
|
|
67
|
+
executables: []
|
|
68
|
+
|
|
69
|
+
extensions: []
|
|
70
|
+
|
|
71
|
+
extra_rdoc_files:
|
|
72
|
+
- README.md
|
|
73
|
+
- LICENSE
|
|
74
|
+
files:
|
|
75
|
+
- .gitignore
|
|
76
|
+
- LICENSE
|
|
77
|
+
- README.md
|
|
78
|
+
- Rakefile
|
|
79
|
+
- lib/medjool.rb
|
|
80
|
+
- lib/medjool/parser.rb
|
|
81
|
+
- lib/medjool/regexs.rb
|
|
82
|
+
- medjool.gemspec
|
|
83
|
+
- test/helper.rb
|
|
84
|
+
- test/test_medjool.rb
|
|
85
|
+
homepage: http://github.com/andygeers/medjool
|
|
86
|
+
licenses: []
|
|
87
|
+
|
|
88
|
+
post_install_message:
|
|
89
|
+
rdoc_options:
|
|
90
|
+
- --charset=UTF-8
|
|
91
|
+
require_paths:
|
|
92
|
+
- lib
|
|
93
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
94
|
+
none: false
|
|
95
|
+
requirements:
|
|
96
|
+
- - ">="
|
|
97
|
+
- !ruby/object:Gem::Version
|
|
98
|
+
hash: 3
|
|
99
|
+
segments:
|
|
100
|
+
- 0
|
|
101
|
+
version: "0"
|
|
102
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
103
|
+
none: false
|
|
104
|
+
requirements:
|
|
105
|
+
- - ">="
|
|
106
|
+
- !ruby/object:Gem::Version
|
|
107
|
+
hash: 3
|
|
108
|
+
segments:
|
|
109
|
+
- 0
|
|
110
|
+
version: "0"
|
|
111
|
+
requirements: []
|
|
112
|
+
|
|
113
|
+
rubyforge_project:
|
|
114
|
+
rubygems_version: 1.8.24
|
|
115
|
+
signing_key:
|
|
116
|
+
specification_version: 3
|
|
117
|
+
summary: Date parsing with context
|
|
118
|
+
test_files:
|
|
119
|
+
- test/helper.rb
|
|
120
|
+
- test/test_medjool.rb
|