minute 0.1 → 0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. data/README.md +86 -1
  2. data/lib/minute.rb +74 -70
  3. metadata +15 -4
data/README.md CHANGED
@@ -1,4 +1,89 @@
1
1
  Minute
2
2
  ======
3
3
 
4
- Natural Language Date/Time parsing library for Ruby
4
+ Flexible Natural Language Date/Time parsing library for Ruby (1.9.x)
5
+
6
+ ## Feature
7
+
8
+ * Flexible Natural Language parsing
9
+ * Source code is small, readable and maintainable
10
+ * Can parse short or long sentence when date/time is included
11
+
12
+ ## Approach
13
+
14
+ [Chronic](http://) has been an amazing gem, but I learned its limitation. It cannot parse long sentence or it will give you `nil` as a resultant value. Chronic's code design is difficult to maintain.
15
+
16
+ I was thinking what if I can create a simple Ruby library with the same approach, but more flexible and more features. Thus, Minute was created to address this problem.
17
+
18
+ Examples:
19
+
20
+ ```ruby
21
+
22
+ Chronic.parse("I have to meet my friend on December 13 in Dallas, TX")
23
+ #=> nil
24
+
25
+ Minute.parse("I have to meet my friend on December 13 in Dallas, TX")
26
+ #=> 2012-12-13 00:00:00 -0600
27
+
28
+ ```
29
+
30
+
31
+ ## Installation
32
+
33
+ **Gemfile**
34
+
35
+ gem 'minute'
36
+
37
+ **RubyGem**
38
+
39
+ gem install minute
40
+
41
+ ## Usage
42
+
43
+
44
+ ```ruby
45
+
46
+ require 'minute'
47
+
48
+ Minute.parse("Hi! I am Ruby. I was born on Feb 24 1993. My father taught me to make programmers happy.")
49
+ #=> 1993-02-24 00:00:00 -0600
50
+
51
+ Minute.parse("Birthday party is tomorrow night!", now: Time.now)
52
+ #=> => 2012-05-01 11:20:48 -0500
53
+
54
+ ```
55
+
56
+
57
+ ## Speed
58
+
59
+ Minute is using `String#index?` or Regular Expression to recognize the date or time. `String#index?` is using [Rabin-Karp String Search algorithm](http://en.wikipedia.org/wiki/Rabin%E2%80%93Karp_string_search_algorithm), which it is making the search faster.
60
+
61
+ user system total real
62
+ minute 12.750000 0.080000 12.830000 ( 12.833267)
63
+ chronic 97.860000 0.130000 97.990000 ( 97.983466)
64
+
65
+
66
+ ## TODO
67
+
68
+ Minute is still young. Some features are being added soon.
69
+
70
+ Planned to add the following:
71
+
72
+ * This december
73
+ * Last Janurary
74
+ * between 8am and 5pm
75
+ * between Jan and July
76
+ * every weekend
77
+ * this morning
78
+ * tonight
79
+ * and more..
80
+
81
+ ## Contribution
82
+
83
+ If you discover bugs or want to help or improve it, Pull Requests are welcome.
84
+
85
+ Ensure that your Pull request is using a topic branch for patching, improving, refactoring, etc.
86
+
87
+ ## License
88
+
89
+ Minute is licensed under the MIT License.
@@ -1,101 +1,105 @@
1
+ require 'small'
2
+ # Public: Minute is a natural language Date/Time parsing for Ruby Library
3
+ #
4
+ # Examples:
5
+ #
6
+ # require 'minute'
7
+ #
8
+ # Minute.parse("Hi I am Ruby I was born on Feb 24, 1993.")
9
+ #
10
+ # Minute.parse("I came back from May 13, 2025", now: (Time.now + 23.years))
11
+ #
1
12
  class Minute
2
13
 
3
14
  VERSION = "0.1"
4
15
 
5
- def self.add_unit(name, value)
6
- class_eval("def self.#{name}; #{value} end", __FILE__, __LINE__ + 1)
7
- end
16
+ MONTHS = Date::MONTHNAMES.zip(Date::ABBR_MONTHNAMES).flatten.compact.map(&:downcase).freeze
8
17
 
9
- def self.rule(options = {})
10
- rules.merge! options
11
- end
18
+ DATE_WITH_ORDINAL = /(\d{1,2})(st|nd|rd|th)?\s+(#{MONTHS.join("|")})(\s+\d{4})?/
19
+ ENGLISH_DATE = /(#{MONTHS.join("|")}),?\s(\d{1,2})(\s+\d{4})?/
20
+ DATE = /(\d{1,4})-?(\d{1,4})-?(\d{1,4})?/
12
21
 
13
- def self.rules
14
- @rules ||= {}
22
+ # Public: Create a match pattern to capture date/time strings.
23
+ #
24
+ # Examples:
25
+ #
26
+ # match "tomorrow", lambda {|time| time + 1.day}
27
+ #
28
+ # match "yesterday" do |time|
29
+ # time - 1.day
30
+ # end
31
+ #
32
+ # Returns Hash of match patterns.
33
+ def self.match(pattern, block = nil, &blk)
34
+ patterns.merge!({pattern => (block ? block : blk)})
15
35
  end
16
36
 
37
+ # Public: parse the String to capture Date/Time
38
+ #
39
+ # Examples:
40
+ #
41
+ # Minute.parse("tomorrow")
42
+ #
43
+ # Returns an instance of Time
17
44
  def self.parse(text, options = {})
18
45
  obj = new(options)
19
- obj.parse_string(text)
46
+ obj.parse_string(text.to_s.downcase.strip)
47
+ end
48
+
49
+ def self.patterns
50
+ @patterns ||= {}
20
51
  end
21
52
 
53
+ attr_reader :now, :current_time
54
+
22
55
  def initialize(options = {})
23
- @time = options[:now] || Time.now
24
- @current_time = @time
56
+ @now = options[:now] || Time.now
57
+ @current_time = @now
25
58
  end
26
59
 
27
60
  def parse_string(text)
28
- rules.keys.each do |key|
29
- if key.is_a?(Regexp) && text =~ key
30
- @current_time = rules[key].call(@time, Regexp.last_match)
31
- elsif key.is_a?(String) && text.include?(key)
32
- @current_time = rules[key].call(@time)
61
+ patterns.keys.each do |pattern|
62
+ begin
63
+ if pattern.is_a?(Regexp) && text =~ pattern
64
+ @now = patterns[pattern].call(current_time, Regexp.last_match)
65
+ elsif pattern.is_a?(String) && text.include?(pattern)
66
+ @now = patterns[pattern].call(current_time)
67
+ end
68
+ rescue
69
+ @now
33
70
  end
34
71
  end
35
- current_time
72
+ @now
36
73
  end
37
74
 
38
- def rules
39
- @rules ||= self.class.rules
75
+ def patterns
76
+ @patterns ||= self.class.patterns
40
77
  end
41
78
 
42
- def to_date(time = nil)
43
- time ||= @current_time
44
- Date.new(time.year, time.month, time.day)
45
- end
46
-
47
- def current_time
48
- @current_time
79
+ match 'tomorrow', lambda {|time| time + 1.day }
80
+ match 'yesterday', lambda {|time| time - 1.day }
81
+ match 'today', lambda {|time| time }
82
+
83
+ match /(last|next|\d+)\s+(year|month|week|day|hour|minute|second)s?(\s+ago)?/ do |time, m|
84
+ case m[1]
85
+ when "next" then time + 1.send(m[2].to_sym)
86
+ when "last" then time - 1.send(m[2].to_sym)
87
+ when /\d+/
88
+ if !m[3].nil?
89
+ time - m[1].to_i.send(m[2].to_sym)
90
+ else
91
+ time + m[1].to_i.send(m[2].to_sym)
92
+ end
93
+ end
49
94
  end
50
95
 
51
- add_unit 'year', 31536000
52
- add_unit 'month', 3600 * 24 * 30
53
- add_unit 'week', 3600 * 24 * 7
54
- add_unit 'day', 3600 * 24
55
- add_unit 'hour', 3600
56
- add_unit 'minute', 60
57
- add_unit 'second', 1
58
-
59
- rule 'tomorrow' => lambda {|time| time + day }
60
- rule 'yesterday' => lambda {|time| time - day }
61
- rule 'today' => lambda {|time| time }
62
-
63
- rule 'next year' => lambda {|time| time + year }
64
- rule 'next month' => lambda {|time| time + month }
65
- rule 'next week' => lambda {|time| time + week }
66
- rule 'next day' => lambda {|time| time + day }
67
- rule 'next hour' => lambda {|time| time + hour }
68
- rule 'next minute' => lambda {|time| time + minute }
69
- rule 'next second' => lambda {|time| time + second }
70
-
71
- rule 'last year' => lambda {|time| time - year }
72
- rule 'last month' => lambda {|time| time - month }
73
- rule 'last week' => lambda {|time| time - week }
74
- rule 'last day' => lambda {|time| time - day }
75
- rule 'last hour' => lambda {|time| time - hour }
76
- rule 'last minute' => lambda {|time| time - minute }
77
- rule 'last second' => lambda {|time| time - second }
78
-
79
- rule /(\d+) years?/ => lambda {|time, m| time + (m[1].to_i * year) }
80
- rule /(\d+) years? ago/ => lambda {|time, m| time - (m[1].to_i * year) }
81
- rule /(\d+) months?/ => lambda {|time, m| time + (m[1].to_i * month) }
82
- rule /(\d+) months? ago/ => lambda {|time, m| time - (m[1].to_i * month) }
83
- rule /(\d+) weeks?/ => lambda {|time, m| time + (m[1].to_i * week) }
84
- rule /(\d+) weeks? ago/ => lambda {|time, m| time - (m[1].to_i * week) }
85
- rule /(\d+) days?/ => lambda {|time, m| time + (m[1].to_i * day) }
86
- rule /(\d+) days? ago/ => lambda {|time, m| time - (m[1].to_i * day) }
87
- rule /(\d+) hours?/ => lambda {|time, m| time + (m[1].to_i * hour) }
88
- rule /(\d+) hours? ago/ => lambda {|time, m| time - (m[1].to_i * hour) }
89
- rule /(\d+) minutes?/ => lambda {|time, m| time + (m[1].to_i * minute) }
90
- rule /(\d+) minutes? ago/ => lambda {|time, m| time - (m[1].to_i * minute) }
91
- rule /(\d+) seconds?/ => lambda {|time, m| time + (m[1].to_i * second) }
92
- rule /(\d+) seconds? ago/ => lambda {|time, m| time - (m[1].to_i * second) }
96
+ match DATE, lambda {|time, m| Time.parse(m[1..-1].compact.join("-")) }
97
+ match ENGLISH_DATE, lambda {|time, m| Time.parse(m[1..-1].compact.join(" ")) }
98
+ match DATE_WITH_ORDINAL, lambda {|time, m| Time.parse(m[1..-1].compact.join(" ")) }
93
99
 
94
100
  # TODO
95
101
  # this december
96
102
  # last december
97
- # 13 dec 2010
98
- # dec 13 2010
99
103
  # next jan 13
100
104
  # every wed
101
105
  # this morning
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: minute
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.1'
4
+ version: '0.2'
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,8 +9,19 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-04-23 00:00:00.000000000 Z
13
- dependencies: []
12
+ date: 2012-04-30 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: small
16
+ requirement: &70134369380160 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - =
20
+ - !ruby/object:Gem::Version
21
+ version: '0.3'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70134369380160
14
25
  description:
15
26
  email: bryann83@gmail.com
16
27
  executables: []
@@ -39,7 +50,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
39
50
  version: '0'
40
51
  requirements: []
41
52
  rubyforge_project:
42
- rubygems_version: 1.8.15
53
+ rubygems_version: 1.8.11
43
54
  signing_key:
44
55
  specification_version: 3
45
56
  summary: Natural Language Date/Time parsing library for Ruby