typelib 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/Rakefile ADDED
@@ -0,0 +1,74 @@
1
+ # $Id$
2
+ $:.push 'lib'
3
+ require 'rake/testtask'
4
+ require 'rake/packagetask'
5
+ require 'rake/gempackagetask'
6
+ require 'rdoc/task'
7
+ require 'fileutils'
8
+ require 'typelib'
9
+
10
+ include FileUtils::Verbose
11
+
12
+ task :default => [ :test, :dist ]
13
+
14
+ task :fixperms do
15
+ chmod(0755, Dir['bin/*'])
16
+ end
17
+
18
+ #
19
+ # Tests
20
+ #
21
+
22
+ Rake::TestTask.new do |t|
23
+ t.libs << 'lib'
24
+ t.test_files = FileList['test/test*.rb']
25
+ t.verbose = true
26
+ end
27
+
28
+ #
29
+ # Distribution
30
+ #
31
+
32
+ task :dist => [:fixperms, :repackage, :gem, :rdoc]
33
+ task :distclean => [:clobber_package, :clobber_rdoc]
34
+ task :clean => [:distclean]
35
+
36
+ #
37
+ # Documentation
38
+ #
39
+
40
+ RDoc::Task.new do |rd|
41
+ rd.rdoc_dir = "rdoc"
42
+ rd.main = "README.rdoc"
43
+ rd.rdoc_files.include("README.rdoc")
44
+ rd.rdoc_files.include("./lib/**/*.rb")
45
+ rd.options = %w(-a)
46
+ end
47
+
48
+ #
49
+ # Packaging
50
+ #
51
+
52
+ spec = Gem::Specification.new do |s|
53
+ s.name = "typelib"
54
+ s.version = TypeLib::VERSION
55
+ s.author = "Erik Hollensbe"
56
+ s.email = "erik@hollensbe.org"
57
+ s.summary = "An on-demand arbitrary check and conversion library that won't destroy your data."
58
+
59
+ s.files = Dir["Rakefile"] + Dir["README"] + Dir["lib/**/*"] + Dir['test/**/*']
60
+
61
+ s.has_rdoc = true
62
+ end
63
+
64
+ Rake::GemPackageTask.new(spec) do |s|
65
+ end
66
+
67
+ Rake::PackageTask.new(spec.name, spec.version) do |p|
68
+ p.need_tar_gz = true
69
+ p.need_zip = true
70
+ p.package_files.include("./bin/**/*")
71
+ p.package_files.include("./Rakefile")
72
+ p.package_files.include("./lib/**/*.rb")
73
+ p.package_files.include("README")
74
+ end
data/lib/typelib.rb ADDED
@@ -0,0 +1,73 @@
1
+ require 'delegate'
2
+
3
+ # Typelib is a way of checking and converting data. It operates as a "filter
4
+ # chain" system which allows it to gradually normalize disparate data into a
5
+ # common type. Each chain is optionally a part of a list which allows it to
6
+ # provide several paths in a single external execution.
7
+ #
8
+ # The library is arguably very simple and therefore has simple requirements and
9
+ # needs. This is intentional.
10
+ #
11
+ # Please see TypeLib::Filter and TypeLib::FilterList for more information.
12
+ #
13
+ module TypeLib
14
+
15
+ VERSION = "0.0.1"
16
+
17
+ # A FilterList is a ... list of filters. It includes all the methods that
18
+ # Array contains, plus an additional method -- execute. See
19
+ # TypeLib::Filter.
20
+ class FilterList < DelegateClass(Array)
21
+ # Create a new FilterList. An array of TypeLib::Filter objects is
22
+ # accepted for construction.
23
+ def initialize(ary=[])
24
+ @filters = ary
25
+ super(@filters)
26
+ end
27
+
28
+ # Execute the checks in this list against +obj+, passing in +addl+
29
+ # if any additional arguments are provided. If the check passes, the
30
+ # conversion is run and the chain supplied to the constructor is
31
+ # followed. If no checks pass, the original item is returned.
32
+ def execute(obj, *addl)
33
+ ret = obj
34
+ @filters.each do |filter|
35
+ if filter.check(obj, *addl)
36
+ ret = filter.convert(obj, *addl)
37
+ break
38
+ end
39
+ end
40
+ return ret
41
+ end
42
+ end
43
+
44
+ # TypeLib::Filter is a way of checking, then optionally converting data
45
+ # based on whether or not the check passes. At that point, an additional
46
+ # TypeLib::FilterList may be provided which indicates that more checks need
47
+ # to be followed with the new data.
48
+ class Filter
49
+ attr_reader :check_proc, :convert_proc, :filters
50
+
51
+ # A TypeLib::Filter consists of a check procedure, a conversion procedure,
52
+ # and a TypeLib::FilterList which may be empty. Checks return boolean
53
+ # which indicates whether or not the conversion will be attempted.
54
+ def initialize(check, convert, filters=FilterList.new)
55
+ @check_proc = check
56
+ @convert_proc = convert
57
+ @filters = filters
58
+ end
59
+
60
+ # Check this object against the filter. If additional data is supplied,
61
+ # it will be provided to the Filter#check_proc.
62
+ def check(obj, *addl)
63
+ check_proc.call(obj, *addl)
64
+ end
65
+
66
+ # Convert this object unconditionally. If additional data is supplied,
67
+ # it will be provided to the Filter#convert_proc.
68
+ def convert(obj, *addl)
69
+ ret = convert_proc.call(obj, *addl)
70
+ filters.execute(ret, *addl)
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,66 @@
1
+ require 'bigdecimal'
2
+ require 'date'
3
+ require 'typelib'
4
+
5
+ module TypeLib
6
+ # Canned checks are just that -- already written for you.
7
+ module Canned
8
+ #
9
+ # build_strptime_filter works with DateTime objects and a format that
10
+ # you provide. It will build and return a new TypeLib::Filter that can
11
+ # check strings for the format you provide and if they pass, convert
12
+ # them to a DateTime object.
13
+ #
14
+ # Due to limitations in the DateTime system itself, you must include
15
+ # zone information -- if not, you will get a value that is offset to
16
+ # GMT, which is not what DateTime.now and pals will return. An
17
+ # ArgumentError will be raised if you do not supply this in the filter.
18
+ #
19
+ def build_strptime_filter(format)
20
+ if format !~ /%z/
21
+ raise ArgumentError, "format must include %z due to DateTime fail"
22
+ end
23
+
24
+ check = proc do |obj|
25
+ (DateTime.strptime(obj, format).strftime(format) == obj) rescue false
26
+ end
27
+
28
+ convert = proc { |obj| DateTime.strptime(obj, format) }
29
+
30
+ return Filter.new(check, convert)
31
+ end
32
+
33
+ module_function :build_strptime_filter
34
+
35
+ # Canned Checks.
36
+ module Checks
37
+ STR_IS_INT = proc { |obj| obj.kind_of?(String) and obj =~ /^\d+$/ }
38
+ STR_IS_DEC = proc { |obj| obj.kind_of?(String) and obj =~ /^[\d.]+$/ }
39
+
40
+ IS_NUMERIC = proc { |obj| obj.kind_of?(Numeric) }
41
+ IS_INTEGER = proc { |obj| obj.kind_of?(Integer) }
42
+ end
43
+
44
+ # Canned Conversions.
45
+ module Conversions
46
+ TO_INTEGER = proc { |obj| obj.to_i }
47
+ TO_FLOAT = proc { |obj| obj.to_f }
48
+ TO_STRING = proc { |obj| obj.to_s }
49
+ TO_BINARY = proc { |obj| obj.to_s(2) }
50
+ TO_HEX = proc { |obj| obj.to_s(16) }
51
+
52
+ STR_TO_BIGDECIMAL = proc { |obj| BigDecimal.new(obj.to_s) }
53
+ end
54
+
55
+ # Fully canned filters.
56
+ module Filters
57
+ STR_TO_INT = Filter.new(Checks::STR_IS_INT, Conversions::TO_INTEGER)
58
+ STR_TO_FLOAT = Filter.new(Checks::STR_IS_DEC, Conversions::TO_FLOAT)
59
+ STR_TO_DEC = Filter.new(Checks::STR_IS_DEC, Conversions::STR_TO_BIGDECIMAL)
60
+
61
+ NUM_TO_STR = Filter.new(Checks::IS_NUMERIC, Conversions::TO_STRING)
62
+ INT_TO_BIN = Filter.new(Checks::IS_INTEGER, Conversions::TO_BINARY)
63
+ INT_TO_HEX = Filter.new(Checks::IS_INTEGER, Conversions::TO_HEX)
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,90 @@
1
+ require 'rubygems'
2
+ gem 'test-unit'
3
+ require 'test/unit'
4
+
5
+ $:.unshift 'lib'
6
+ require 'typelib'
7
+
8
+ class TestBasic < Test::Unit::TestCase
9
+ def test_01_classes
10
+ assert(TypeLib)
11
+ assert(TypeLib::FilterList)
12
+ assert(TypeLib::Filter)
13
+ end
14
+
15
+ def test_02_object_properties
16
+ filters = TypeLib::FilterList.new
17
+
18
+ assert_respond_to(filters, :[])
19
+ assert_respond_to(filters, :<<)
20
+ assert_respond_to(filters, :execute)
21
+
22
+ check = proc { }
23
+ convert = proc { }
24
+ filter = TypeLib::Filter.new(check, convert)
25
+
26
+ assert_respond_to(filter, :check_proc)
27
+ assert_respond_to(filter, :convert_proc)
28
+ assert_respond_to(filter, :filters)
29
+ assert_respond_to(filter, :check)
30
+ assert_respond_to(filter, :convert)
31
+
32
+ assert_kind_of(Proc, filter.check_proc)
33
+ assert_kind_of(Proc, filter.convert_proc)
34
+ assert_kind_of(TypeLib::FilterList, filter.filters)
35
+ end
36
+
37
+ def test_03_basic_conversions
38
+ filters = TypeLib::FilterList.new
39
+ check = proc { |obj| obj.kind_of?(Integer) }
40
+ convert = proc { |obj| obj }
41
+
42
+ check2 = proc { |obj| obj.kind_of?(String) and obj =~ /^\d+$/ }
43
+ convert2 = proc { |obj| Integer(obj) }
44
+
45
+ filters << TypeLib::Filter.new(check, convert)
46
+ filters << TypeLib::Filter.new(check2, convert2)
47
+
48
+ assert_equal(2, filters.count)
49
+ assert_equal(1, filters.execute(1))
50
+ assert_equal(1, filters.execute("1"))
51
+
52
+ 10.times do
53
+ x = rand(250000).to_i
54
+ assert_equal(x, filters.execute(x))
55
+ assert_equal(x, filters.execute(x.to_s))
56
+ end
57
+
58
+ assert_equal("1.25", filters.execute("1.25"))
59
+
60
+ filters << TypeLib::Filter.new(proc { true }, proc { |obj| Integer(obj) })
61
+ assert_raises(ArgumentError) { filters.execute("1.25") }
62
+ end
63
+
64
+ def test_04_args
65
+ check = proc { |obj, *addl| addl[0] }
66
+ convert = proc { |obj, *addl| addl[0] }
67
+ filter = TypeLib::Filter.new(check, convert)
68
+
69
+ assert(filter.check(true, true))
70
+ assert(!filter.check(true, false))
71
+ assert_equal("fart", filter.convert(true, "fart"))
72
+ end
73
+
74
+ def test_05_chains
75
+ filters = TypeLib::FilterList.new
76
+
77
+ check = proc { |obj| obj.kind_of?(Integer) }
78
+ convert = proc { |obj| obj.to_s }
79
+
80
+ check2 = proc { |obj| obj.kind_of?(String) and obj =~ /^\d+$/ }
81
+ convert2 = proc { |obj| obj.to_f }
82
+
83
+ filters << TypeLib::Filter.new(check, convert, TypeLib::FilterList.new([TypeLib::Filter.new(check2, convert2)]))
84
+ filters << TypeLib::Filter.new(check2, convert2)
85
+
86
+ assert_equal(1.0, filters.execute(1))
87
+ assert_kind_of(Float, filters.execute(1))
88
+ assert_equal(1.0, filters.execute("1"))
89
+ end
90
+ end
@@ -0,0 +1,56 @@
1
+ require 'rubygems'
2
+ gem 'test-unit'
3
+ require 'test/unit'
4
+
5
+ $:.unshift 'lib'
6
+ require 'typelib'
7
+ require 'typelib/canned'
8
+
9
+ class TestCanned < Test::Unit::TestCase
10
+ include TypeLib::Canned
11
+ include TypeLib::Canned::Filters
12
+
13
+ def create_filterlist(arg)
14
+ filters = TypeLib::FilterList.new
15
+ filters << arg
16
+ return filters
17
+ end
18
+
19
+ def test_01_string_to_numeric
20
+ filters = create_filterlist(STR_TO_INT)
21
+ assert_equal(1, filters.execute("1"))
22
+ assert_kind_of(Integer, filters.execute("1"))
23
+
24
+ filters = create_filterlist(STR_TO_FLOAT)
25
+ assert_equal(1.0, filters.execute("1"))
26
+ assert_kind_of(Float, filters.execute("1"))
27
+
28
+ filters = create_filterlist(STR_TO_DEC)
29
+ assert_equal(BigDecimal.new("1.0"), filters.execute("1"))
30
+ assert_kind_of(BigDecimal, filters.execute("1"))
31
+ end
32
+
33
+ def test_02_numeric_to_string
34
+ filters = create_filterlist(NUM_TO_STR)
35
+ assert_equal("1", filters.execute(1))
36
+ assert_kind_of(String, filters.execute(1))
37
+
38
+ filters = create_filterlist(INT_TO_BIN)
39
+ assert_equal("10", filters.execute(2))
40
+ assert_kind_of(String, filters.execute(2))
41
+
42
+ filters = create_filterlist(INT_TO_HEX)
43
+ assert_equal("a", filters.execute(10))
44
+ assert_kind_of(String, filters.execute(10))
45
+
46
+ end
47
+
48
+ def test_03_datetime
49
+ filters = create_filterlist(build_strptime_filter("%H:%M:%S%z"))
50
+ time = DateTime.now
51
+ newtime = filters.execute(time.strftime("%H:%M:%S%z"))
52
+ assert_equal(time.to_s, newtime.to_s)
53
+
54
+ assert_raises(ArgumentError.new("format must include %z due to DateTime fail")) { build_strptime_filter("%H:%M:%S") }
55
+ end
56
+ end
metadata ADDED
@@ -0,0 +1,66 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: typelib
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 1
9
+ version: 0.0.1
10
+ platform: ruby
11
+ authors:
12
+ - Erik Hollensbe
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-05-17 00:00:00 -04:00
18
+ default_executable:
19
+ dependencies: []
20
+
21
+ description:
22
+ email: erik@hollensbe.org
23
+ executables: []
24
+
25
+ extensions: []
26
+
27
+ extra_rdoc_files: []
28
+
29
+ files:
30
+ - Rakefile
31
+ - lib/typelib/canned.rb
32
+ - lib/typelib.rb
33
+ - test/test_01_basic.rb
34
+ - test/test_02_canned.rb
35
+ has_rdoc: true
36
+ homepage:
37
+ licenses: []
38
+
39
+ post_install_message:
40
+ rdoc_options: []
41
+
42
+ require_paths:
43
+ - lib
44
+ required_ruby_version: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ segments:
49
+ - 0
50
+ version: "0"
51
+ required_rubygems_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ segments:
56
+ - 0
57
+ version: "0"
58
+ requirements: []
59
+
60
+ rubyforge_project:
61
+ rubygems_version: 1.3.6
62
+ signing_key:
63
+ specification_version: 3
64
+ summary: An on-demand arbitrary check and conversion library that won't destroy your data.
65
+ test_files: []
66
+