spanner 0.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/README.rdoc +14 -0
- data/Rakefile +28 -0
- data/VERSION +1 -0
- data/lib/spanner.rb +82 -0
- data/spec/spanner_spec.rb +36 -0
- data/spec/spec.opts +7 -0
- data/spec/spec_helper.rb +1 -0
- metadata +62 -0
data/README.rdoc
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
= Spanner
|
2
|
+
|
3
|
+
Easy way to parse natural language time spans as periods expressed in seconds. Supports float point notions of spans as well.
|
4
|
+
|
5
|
+
== Usage
|
6
|
+
|
7
|
+
require 'spanner'
|
8
|
+
|
9
|
+
Spanner.parse('1s')
|
10
|
+
=> 1
|
11
|
+
|
12
|
+
Spanner.parse('23 hours 12 minutes')
|
13
|
+
=> 83520
|
14
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
begin
|
2
|
+
require 'jeweler'
|
3
|
+
Jeweler::Tasks.new do |s|
|
4
|
+
s.name = "spanner"
|
5
|
+
s.description = s.summary = "Natural language time span parsing"
|
6
|
+
s.email = "joshbuddy@gmail.com"
|
7
|
+
s.homepage = "http://github.com/joshbuddy/spanner"
|
8
|
+
s.authors = ["Joshua Hull"]
|
9
|
+
s.files = FileList["[A-Z]*", "{lib,spec}/**/*"]
|
10
|
+
end
|
11
|
+
Jeweler::GemcutterTasks.new
|
12
|
+
rescue LoadError
|
13
|
+
puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
14
|
+
end
|
15
|
+
|
16
|
+
require 'spec'
|
17
|
+
require 'spec/rake/spectask'
|
18
|
+
task :spec => 'spec:all'
|
19
|
+
namespace(:spec) do
|
20
|
+
Spec::Rake::SpecTask.new(:all) do |t|
|
21
|
+
t.spec_opts ||= []
|
22
|
+
t.spec_opts << "-rubygems -rdirge"
|
23
|
+
t.spec_opts << "--options" << "spec/spec.opts"
|
24
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1
|
data/lib/spanner.rb
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
class Spanner
|
2
|
+
|
3
|
+
ParseError = Class.new(RuntimeError)
|
4
|
+
|
5
|
+
def self.parse(str, opts = nil)
|
6
|
+
Spanner.new(str, opts).parse
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_reader :value, :raise_on_error, :from
|
10
|
+
|
11
|
+
def initialize(value, opts)
|
12
|
+
@value = value
|
13
|
+
@on_error = opts && opts.key?(:on_error) ? opts[:on_error] : :raise
|
14
|
+
|
15
|
+
|
16
|
+
@from = if opts && opts.key?(:from)
|
17
|
+
case opts[:from]
|
18
|
+
when :now
|
19
|
+
Time.new.to_i
|
20
|
+
else
|
21
|
+
opts[:from].to_i
|
22
|
+
end
|
23
|
+
else
|
24
|
+
0
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def error(err)
|
29
|
+
if on_error == :raise
|
30
|
+
raise ParseError.new(err)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def parse
|
35
|
+
parts = []
|
36
|
+
part_contextualized = nil
|
37
|
+
value.scan(/[\+\-]?(?:\d*\.\d+|\d+)|[a-z]+/i).each do |part|
|
38
|
+
part_as_float = Float(part) rescue nil
|
39
|
+
if part_as_float
|
40
|
+
parts << part_as_float
|
41
|
+
part_contextualized = nil
|
42
|
+
else
|
43
|
+
if part_contextualized
|
44
|
+
error "Part has already been contextualized with #{part_contextualized}"
|
45
|
+
return nil
|
46
|
+
end
|
47
|
+
|
48
|
+
if parts.empty?
|
49
|
+
parts << 1
|
50
|
+
end
|
51
|
+
|
52
|
+
# part is context
|
53
|
+
multiplier = case part
|
54
|
+
when 's', 'sec', 'seconds' then 1
|
55
|
+
when 'h', 'hours', 'hrs' then 3600
|
56
|
+
when 'm', 'min', 'minutes' then 60
|
57
|
+
when 'd', 'days' then 86_400
|
58
|
+
when 'w', 'wks', 'weeks' then 604_800
|
59
|
+
when 'months', 'month', 'm' then 2_629_743.83
|
60
|
+
when 'years', 'y' then 31_556_926
|
61
|
+
when /\As/ then 1
|
62
|
+
when /\Am/ then 60
|
63
|
+
when /\Ah/ then 86_400
|
64
|
+
when /\Aw/ then 604_800
|
65
|
+
when /\Am/ then 2_629_743.83
|
66
|
+
when /\Ay/ then 31_556_926
|
67
|
+
end
|
68
|
+
|
69
|
+
part_contextualized = part
|
70
|
+
parts << (parts.pop * multiplier)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
if parts.empty?
|
75
|
+
nil
|
76
|
+
else
|
77
|
+
value = parts.inject(from) {|s, p| s += p}
|
78
|
+
value.ceil == value ? value.ceil : value
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require ~'spec_helper'
|
2
|
+
|
3
|
+
describe Spanner do
|
4
|
+
|
5
|
+
it "should return nil for empty strings" do
|
6
|
+
Spanner.parse('').should be_nil
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should assume seconds" do
|
10
|
+
Spanner.parse('1').should == 1
|
11
|
+
end
|
12
|
+
|
13
|
+
#simple
|
14
|
+
{ '.5s' => 0.5, '1s' => 1, '1.5s' => 1.5, '1m' => 60, '1.5m' => 90, '1d' => 86400, '1.7233312d' => 148895.81568 }.each do |input, output|
|
15
|
+
it "should parse #{input} and return #{output}" do
|
16
|
+
Spanner.parse(input).should == output
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
#complex
|
21
|
+
{ '1m23s' => 83 }.each do |input, output|
|
22
|
+
it "should parse #{input} and return #{output}" do
|
23
|
+
Spanner.parse(input).should == output
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should accept time as from option" do
|
28
|
+
now = Time.new
|
29
|
+
Spanner.parse('23s', :from => now).should == now.to_i + 23
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should accept special :now as from option" do
|
33
|
+
Spanner.parse('23s', :from => :now).should == Time.new.to_i + 23
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
data/spec/spec.opts
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require ~'../lib/spanner'
|
metadata
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: spanner
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Joshua Hull
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2010-03-16 00:00:00 -04:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: Natural language time span parsing
|
17
|
+
email: joshbuddy@gmail.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files:
|
23
|
+
- README.rdoc
|
24
|
+
files:
|
25
|
+
- README.rdoc
|
26
|
+
- Rakefile
|
27
|
+
- VERSION
|
28
|
+
- lib/spanner.rb
|
29
|
+
- spec/spanner_spec.rb
|
30
|
+
- spec/spec.opts
|
31
|
+
- spec/spec_helper.rb
|
32
|
+
has_rdoc: true
|
33
|
+
homepage: http://github.com/joshbuddy/spanner
|
34
|
+
licenses: []
|
35
|
+
|
36
|
+
post_install_message:
|
37
|
+
rdoc_options:
|
38
|
+
- --charset=UTF-8
|
39
|
+
require_paths:
|
40
|
+
- lib
|
41
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: "0"
|
46
|
+
version:
|
47
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
48
|
+
requirements:
|
49
|
+
- - ">="
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: "0"
|
52
|
+
version:
|
53
|
+
requirements: []
|
54
|
+
|
55
|
+
rubyforge_project:
|
56
|
+
rubygems_version: 1.3.5
|
57
|
+
signing_key:
|
58
|
+
specification_version: 3
|
59
|
+
summary: Natural language time span parsing
|
60
|
+
test_files:
|
61
|
+
- spec/spanner_spec.rb
|
62
|
+
- spec/spec_helper.rb
|