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 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