spanner 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,7 @@
1
+ --colour
2
+ --format
3
+ specdoc
4
+ --loadby
5
+ mtime
6
+ --reverse
7
+ --backtrace
@@ -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