heuristics 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/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in heuristics.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Peter Haza
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,75 @@
1
+ # Heuristics
2
+
3
+ This gem allows you to define a set of conditions and test values against them.
4
+ A typical simple example can look like this:
5
+
6
+ require 'chronic' # Excellent date lib to recognise dates
7
+ Heuristics.define(:field_tester) do
8
+ assume_default :integer
9
+
10
+ assume(:string) { condition { value.instance_of? String } }
11
+ assume(:hash) { condition { value.instance_of? Hash } }
12
+ assume(:date) { conditino { Chronic.parse(value) != nil } }
13
+ end
14
+
15
+ Then you can use it like this
16
+
17
+ # Returns :string
18
+ Heuristics.test(:field_tester, 'abc')
19
+
20
+ # Returns :date
21
+ Heuristics.test(:field_tester, '23.09.1985')
22
+
23
+ # Falls back to :integer per assume_default. None of the other assumptions returned trus
24
+ Heuristics.test(:field_tester, [])
25
+
26
+
27
+
28
+ ## Installation
29
+
30
+ Add this line to your application's Gemfile:
31
+
32
+ gem 'heuristics'
33
+
34
+ And then execute:
35
+
36
+ $ bundle
37
+
38
+ Or install it yourself as:
39
+
40
+ $ gem install heuristics
41
+
42
+ ## Usage
43
+
44
+ # Creates a new heuristic named :field_tester
45
+ Heuristics.define(:field_tester) do
46
+
47
+ # Default value to return if no assumptions match
48
+ assume_default :integer
49
+
50
+ # An assumption that will return :string if all conditions return true
51
+ assume(:string) { condition { value.instance_of? String } }
52
+
53
+ # Assummption with multiple conditions. Returns :hash_with_values
54
+ # if all conditions return true
55
+ assume(:hash_with_values) do
56
+ condition { value.instance_of? Hash }
57
+ condition { value.keys.size > 0 }
58
+ }
59
+
60
+ # An assumption that will return :date if all conditions return true
61
+ assume(:date) { conditino { Chronic.parse(value) != nil } }
62
+ end
63
+
64
+
65
+ # Test a value against heuristic named :field_tester
66
+ # Returns :hash_with_values in this case
67
+ Heuristics.test(:field_tester, {a: 1})
68
+
69
+ ## Contributing
70
+
71
+ 1. Fork it
72
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
73
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
74
+ 4. Push to the branch (`git push origin my-new-feature`)
75
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+ require 'rake/testtask'
4
+ Rake::TestTask.new do |t|
5
+ t.libs << 'lib/heuristics'
6
+ t.test_files = FileList['test/lib/heuristics/*_test.rb']
7
+ t.verbose = false
8
+ end
9
+ task :default => :test
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'heuristics/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "heuristics"
8
+ gem.version = Heuristics::VERSION
9
+ gem.authors = ["Peter Haza"]
10
+ gem.email = ["peter.haza@gmail.com"]
11
+ gem.description = %q{This gem allows you to define a set of conditions and test values against them.}
12
+ gem.summary = %q{This gem allows you to define a set of conditions and test values against them.}
13
+ gem.homepage = ""
14
+
15
+ gem.add_development_dependency "rake"
16
+ gem.add_development_dependency "chronic"
17
+ gem.add_dependency "docile"
18
+
19
+ gem.files = `git ls-files`.split($/)
20
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
21
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
22
+ gem.require_paths = ["lib"]
23
+
24
+ end
@@ -0,0 +1,18 @@
1
+ module Heuristics
2
+ class Builder
3
+ attr_reader :tests, :default
4
+
5
+ def initialize
6
+ @tests = {}
7
+ end
8
+
9
+ def assume(type, &block)
10
+ raise "An assumption with the name '#{type}' already exists" unless @tests[type].nil?
11
+ @tests[type] = Docile.dsl_eval(ConditionEvaluator.new, &block)
12
+ end
13
+
14
+ def assume_default(type)
15
+ @default = type
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,20 @@
1
+ require 'ostruct'
2
+
3
+ module Heuristics
4
+ class ConditionEvaluator
5
+
6
+ def initialize
7
+ @conditions = []
8
+ end
9
+
10
+ def condition(&block)
11
+ @conditions << Proc.new(&block)
12
+ end
13
+
14
+ def check(value)
15
+ context = OpenStruct.new(value: value)
16
+
17
+ @conditions.map{|cond| context.instance_eval(&cond) rescue false}.reject{|v| v}.length == 0
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,11 @@
1
+ module Heuristics
2
+ class Tester
3
+ def initialize(builder)
4
+ @builder = builder
5
+ end
6
+
7
+ def test(value)
8
+ @builder.tests.map{|k, e| e.check(value) ? k : nil }.reject(&:nil?).first || @builder.default
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,3 @@
1
+ module Heuristics
2
+ VERSION = "0.0.1"
3
+ end
data/lib/heuristics.rb ADDED
@@ -0,0 +1,25 @@
1
+ require 'docile'
2
+ require 'heuristics/builder'
3
+ require 'heuristics/condition_evaluator'
4
+ require 'heuristics/tester'
5
+ require 'heuristics/version'
6
+
7
+ module Heuristics
8
+ class << self
9
+ @@testers = {}
10
+
11
+ def define(name, &block)
12
+ raise "A heuristic with the name '#{name}' already exists" unless @@testers[name].nil?
13
+ @@testers[name] = Tester.new(Docile.dsl_eval(Builder.new, &block))
14
+ end
15
+
16
+ def test(name, value)
17
+ unless @@testers.key? name
18
+ raise "Heuristic named #{name} hasn't been defined."
19
+ else
20
+ @@testers[name].test(value)
21
+ end
22
+ end
23
+
24
+ end
25
+ end
@@ -0,0 +1,13 @@
1
+ require_relative '../../test_helper'
2
+
3
+ describe Heuristics::Builder do
4
+ it 'should respond to #tests' do
5
+ Heuristics::Builder.new.must_respond_to :tests
6
+ end
7
+
8
+ it 'must allow setting a default assumption' do
9
+ b = Heuristics::Builder.new
10
+ b.assume_default :string
11
+ b.default.must_equal :string
12
+ end
13
+ end
@@ -0,0 +1,77 @@
1
+ require_relative '../../test_helper'
2
+
3
+ describe Heuristics do
4
+ before do
5
+ # @default = Heuristics.define do
6
+ #
7
+ # assume_default :string
8
+ #
9
+ # assume :integer do
10
+ # condition { value =~ /\A\d+\Z/ }
11
+ # end
12
+ #
13
+ # assume :email do
14
+ # condition { value =~ /\A.*@.*\Z/}
15
+ # end
16
+ #
17
+ # assume :date_of_birth do
18
+ # require 'chronic'
19
+ #
20
+ # condition { Chronic.parse(value) }
21
+ # end
22
+ #
23
+ # assume :phone_number do
24
+ # condition { value.starts_with? '+353' } and
25
+ # condition { value.scan(/\d/).length > 5 }
26
+ # end
27
+ # end
28
+ end
29
+
30
+ it 'should allow to use external libs for testing' do
31
+ require 'chronic'
32
+ Heuristics.define(:external_lib_test) { assume(:date) { condition { Chronic.parse(value) != nil } } }
33
+ Heuristics.test(:external_lib_test, '23.09.85').must_equal :date
34
+ end
35
+
36
+ it 'should return default if no assumptions are true' do
37
+ Heuristics.define(:default_test) { assume_default :string; assume(:email) { condition { false } } }
38
+ Heuristics.test(:default_test, 1).must_equal :string
39
+ end
40
+
41
+ it 'should return nil if no default is set' do
42
+ Heuristics.define(:default_test2) { assume(:nothing) { condition { false } } }
43
+ Heuristics.test(:default_test2, 1).must_be :nil?
44
+ end
45
+
46
+ it 'should return the first true assumption' do
47
+ Heuristics.define(:assumption_test) do
48
+ assume_default :integer
49
+ assume(:string) { condition { value.instance_of? String } }
50
+ assume(:hash) { condition { value.instance_of? String } }
51
+ end
52
+ Heuristics.test(:assumption_test, 'abc').must_equal :string
53
+ end
54
+
55
+ it 'should support complex types' do
56
+ Heuristics.define(:complex_test) { assume_default :integer; assume(:test) { condition { value[:hepp] } } }
57
+ Heuristics.test(:complex_test, []).must_equal :integer
58
+ end
59
+
60
+ it 'should raise an exception if trying to create a heuristic with a name that already exists ' do
61
+ proc {
62
+ Heuristics.define(:duplicate_heuristic_test) { assume_default :integer }
63
+ Heuristics.define(:duplicate_heuristic_test) { assume_default :integer }
64
+ }.must_raise RuntimeError
65
+ end
66
+
67
+ it 'should raise an exception if trying to create an assumption with a name that already exists' do
68
+ proc {
69
+ Heuristics.define(:duplicate_assumption_test) do
70
+ assume_default :integer
71
+ assume(:test) { condition { true } }
72
+ assume(:test) { condition { true } }
73
+ end
74
+ }.must_raise RuntimeError
75
+ end
76
+
77
+ end
@@ -0,0 +1,7 @@
1
+ require_relative '../../test_helper'
2
+
3
+ describe Heuristics do
4
+ it "must be defined" do
5
+ Heuristics::VERSION.wont_be_nil
6
+ end
7
+ end
@@ -0,0 +1,3 @@
1
+ require 'minitest/autorun'
2
+ require 'minitest/pride'
3
+ require File.expand_path('../../lib/heuristics', __FILE__)
metadata ADDED
@@ -0,0 +1,115 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: heuristics
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.0.1
6
+ platform: ruby
7
+ authors:
8
+ - Peter Haza
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-02-22 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ version_requirements: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ! '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ none: false
21
+ name: rake
22
+ type: :development
23
+ prerelease: false
24
+ requirement: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - ! '>='
27
+ - !ruby/object:Gem::Version
28
+ version: '0'
29
+ none: false
30
+ - !ruby/object:Gem::Dependency
31
+ version_requirements: !ruby/object:Gem::Requirement
32
+ requirements:
33
+ - - ! '>='
34
+ - !ruby/object:Gem::Version
35
+ version: '0'
36
+ none: false
37
+ name: chronic
38
+ type: :development
39
+ prerelease: false
40
+ requirement: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - ! '>='
43
+ - !ruby/object:Gem::Version
44
+ version: '0'
45
+ none: false
46
+ - !ruby/object:Gem::Dependency
47
+ version_requirements: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ! '>='
50
+ - !ruby/object:Gem::Version
51
+ version: '0'
52
+ none: false
53
+ name: docile
54
+ type: :runtime
55
+ prerelease: false
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ! '>='
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ none: false
62
+ description: This gem allows you to define a set of conditions and test values against
63
+ them.
64
+ email:
65
+ - peter.haza@gmail.com
66
+ executables: []
67
+ extensions: []
68
+ extra_rdoc_files: []
69
+ files:
70
+ - .gitignore
71
+ - Gemfile
72
+ - LICENSE.txt
73
+ - README.md
74
+ - Rakefile
75
+ - heuristics.gemspec
76
+ - lib/heuristics.rb
77
+ - lib/heuristics/builder.rb
78
+ - lib/heuristics/condition_evaluator.rb
79
+ - lib/heuristics/tester.rb
80
+ - lib/heuristics/version.rb
81
+ - test/lib/heuristics/builder_test.rb
82
+ - test/lib/heuristics/functional_test.rb
83
+ - test/lib/heuristics/version_test.rb
84
+ - test/test_helper.rb
85
+ homepage: ''
86
+ licenses: []
87
+ post_install_message:
88
+ rdoc_options: []
89
+ require_paths:
90
+ - lib
91
+ required_ruby_version: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ! '>='
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ none: false
97
+ required_rubygems_version: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ none: false
103
+ requirements: []
104
+ rubyforge_project:
105
+ rubygems_version: 1.8.23
106
+ signing_key:
107
+ specification_version: 3
108
+ summary: This gem allows you to define a set of conditions and test values against
109
+ them.
110
+ test_files:
111
+ - test/lib/heuristics/builder_test.rb
112
+ - test/lib/heuristics/functional_test.rb
113
+ - test/lib/heuristics/version_test.rb
114
+ - test/test_helper.rb
115
+ has_rdoc: