typestrict 0.0.4

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/History.txt ADDED
@@ -0,0 +1,26 @@
1
+ === 0.0.4 / 2010-08-03
2
+
3
+ * 1 major enhancement
4
+
5
+ * Renamed package to typestrict
6
+
7
+
8
+ === 0.0.3 / 2010-08-03
9
+
10
+ * 1 major enhancement
11
+
12
+ * Introduced TypeError and verbose output - now displays list of errors caught and raises single Strict::TypeError
13
+
14
+ === 0.0.2 / 2010-08-03
15
+
16
+ * 1 major enhancement
17
+
18
+ * Gemify
19
+
20
+
21
+ === 0.0.1 / 2010-08-02
22
+
23
+ * 1 major enhancement
24
+
25
+ * Birthday!
26
+
data/Manifest.txt ADDED
@@ -0,0 +1,8 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README
4
+ README.txt
5
+ Rakefile
6
+ bin/typestrict
7
+ lib/typestrict.rb
8
+ test/test_strict.rb
data/README ADDED
@@ -0,0 +1,89 @@
1
+ strict is a rudimentary static typesystem for ruby objects - it includes easy validation of individual ruby objects and Hash sets.
2
+
3
+ For an example of validation:
4
+
5
+ require 'typestrict'
6
+
7
+ class Rectangle
8
+ include Strict
9
+
10
+ def initialize(p)
11
+
12
+ validate_map!({
13
+ :width => :natural_number,
14
+ :height => :natural_number,
15
+ :float => :boolean
16
+ }, p)
17
+
18
+ @width = p[:width]
19
+ @height = p[:height]
20
+ @float = p[:float]
21
+ end
22
+
23
+ def set_dimensions(dimensions)
24
+ validate_map!({
25
+ :width => :natural_number,
26
+ :height => :natural_number
27
+ }, dimensions) #checks for the presence + validity of all declared parameters
28
+
29
+ @height = dimensions[:height]
30
+ @width = dimensions[:width]
31
+ end
32
+
33
+ def scale_height(scale_coefficent)
34
+ enforce!(:float, scale_coefficent)
35
+ return scale_coefficent*@height #alays valid
36
+ end
37
+ end
38
+
39
+ Strict defines a number of 'super-types', currently which are:
40
+ :natural_number
41
+ :float
42
+ :boolean
43
+ :string_array
44
+ :numeric_array
45
+ :float_array
46
+ :integer_array
47
+ :hex_color_string
48
+
49
+ Strict simply implements an extendable static type-checker over dynamic ruby objects. For an example of validation with troublesome datasets:
50
+
51
+ #enforce!(:string_array, ["one", "two", 3, "four"])
52
+ #./strict.rb:3:in `enforce_primitive!': 3 must be of type String (RuntimeError)
53
+
54
+ type_matrix = {
55
+ :fruit => [:apple, :orange, :banana],
56
+ :height => :natural_number,
57
+ :width => :float,
58
+ :broken => :boolean
59
+ }
60
+
61
+ invalid_data = {
62
+ :fruit => :pear,
63
+ :height => -1,
64
+ :width => 100,
65
+ :broken => 1}
66
+
67
+
68
+ validate_map!(type_matrix, invalid_data)
69
+ #./strict.rb:27:in `enforce_weak_primitives!': 1 must be of one of the types of [TrueClass, FalseClass] (RuntimeError)
70
+ #./strict.rb:3:in `enforce_primitive!': 10 must be of type Float (RuntimeError)
71
+ #./strict.rb:38:in `enforce!': pear should take on a value within [:apple, :orange, :banana] (RuntimeError)
72
+ #./strict.rb:53:in `enforce!': -1 must be > 0 (RuntimeError)
73
+
74
+ A valid dataset for this type_matrix would be:
75
+
76
+ valid_data = {
77
+ :fruit => :apple,
78
+ :height => 200,
79
+ :width => 100.011,
80
+ :broken => true
81
+ }
82
+
83
+ validate_map!(type_matrix, invalid_data) # => all checks pass
84
+
85
+ TODO
86
+ ----
87
+ Implement generic handler for super-types
88
+ Performance analysis
89
+ Implement low-level?
data/README.txt ADDED
@@ -0,0 +1,72 @@
1
+ = strict
2
+
3
+ * http://www.raeez.com/strict
4
+
5
+ == DESCRIPTION:
6
+
7
+ Strict implements a traditional static type-system over dynamic ruby objects. Strict is designed to help debug simple type errors.
8
+
9
+ == FEATURES/PROBLEMS:
10
+
11
+ Implements a Static typesystem over Object
12
+ Support for ruby Primitives (strict subsets, weak subsets etc.)
13
+ Support for user-defined types
14
+
15
+ == SYNOPSIS:
16
+
17
+ def add_nums(a, b)
18
+ enforce!(:numeric, a)
19
+ enforce!(:numeric, b)
20
+ return a + b
21
+ end
22
+
23
+ def launch_rocket(params)
24
+ enforce_map!({
25
+ :destination => [:moon, :mars, :saturn, :jupiter, :iss],
26
+ :rocket_type => [:appollo, :space_x],
27
+ :fuel_litres => :numeric,
28
+ :mission_id => :string}, params)
29
+ ... #launch the rocket
30
+ end
31
+
32
+ == REQUIREMENTS:
33
+
34
+ none
35
+
36
+ == INSTALL:
37
+
38
+ sudo gem install strict
39
+
40
+ == DEVELOPERS:
41
+
42
+ After checking out the source, run:
43
+
44
+ $ rake
45
+
46
+ This task will install any missing dependencies, run the tests/specs,
47
+ and generate the RDoc.
48
+
49
+ == LICENSE:
50
+
51
+ (The MIT License)
52
+
53
+ Copyright (c) 2010 FIX
54
+
55
+ Permission is hereby granted, free of charge, to any person obtaining
56
+ a copy of this software and associated documentation files (the
57
+ 'Software'), to deal in the Software without restriction, including
58
+ without limitation the rights to use, copy, modify, merge, publish,
59
+ distribute, sublicense, and/or sell copies of the Software, and to
60
+ permit persons to whom the Software is furnished to do so, subject to
61
+ the following conditions:
62
+
63
+ The above copyright notice and this permission notice shall be
64
+ included in all copies or substantial portions of the Software.
65
+
66
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
67
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
68
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
69
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
70
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
71
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
72
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,16 @@
1
+ require 'rubygems'
2
+ require 'hoe'
3
+ require 'lib/typestrict'
4
+
5
+ Hoe.spec 'Strict' do
6
+ self.name = 'typestrict'
7
+ self.version = Strict::VERSION
8
+ self.author = 'Raeez Lorgat'
9
+ self.description = 'Static typesystem for ruby'
10
+ self.email = 'raeez@mit.edu'
11
+ self.summary = 'Strict implements a static typesystem over dynamic ruby objects'
12
+ self.url = 'http://www.raeez.com/strict'
13
+ end
14
+
15
+ desc "Release and publish documentation"
16
+ task :repubdoc => [:release, :publish_docs]
data/bin/typestrict ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ abort "you need to write me"
data/lib/typestrict.rb ADDED
@@ -0,0 +1,159 @@
1
+ module Strict
2
+ VERSION = '0.0.4'
3
+
4
+ class TypeError < Exception
5
+ attr :errors
6
+ def initialize(error_list)
7
+ enforce!(:string_array, error_list, 'lib/strict')
8
+ @errors = error_list
9
+ end
10
+
11
+ def inspect
12
+ msg = "TypeError#{@errors.size > 1 ? "s" : ""} caught:\n"
13
+ @errors.each do |e|
14
+ msg += "#{e}\n"
15
+ end
16
+ return msg
17
+ end
18
+ end
19
+
20
+ @@errors = []
21
+ @@raise_exception = true
22
+
23
+ def setmode_raise!
24
+ @@errors = []
25
+ @@raise_exception = true
26
+ end
27
+
28
+ def setmode_catch!
29
+ @@errors = []
30
+ @@raise_exception = false
31
+ end
32
+
33
+ def catch_error error
34
+ @@errors << error
35
+ t = TypeError.new([error])
36
+ raise(t, t.inspect, caller) if @@raise_exception
37
+ end
38
+
39
+ def raise_hell!
40
+ t = TypeError.new(@@errors)
41
+ raise(t, t.inspect, caller) if @@errors.size > 0
42
+ end
43
+
44
+ def header(context, data)
45
+ "#{context}; #{data.class.inspect}::#{data.inspect}"
46
+ end
47
+
48
+ def enforce_primitive!(type, data, context="Value")
49
+ catch_error "#{header(context, data)} must be of type #{type}" unless (data.is_a? type and type.is_a? Class)
50
+ return data
51
+ end
52
+
53
+ def enforce_strict_primitives!(types, data, context="Value")
54
+ enforce_primitive!(Array, types, 'lib/strict')
55
+ types.each {|type| enforce_primitive!(Object, type, 'lib/strict')}
56
+ types.each do |type|
57
+ enforce_primitive!(type, data, context)
58
+ end
59
+ return data
60
+ end
61
+
62
+ def enforce_weak_primitives!(types, data, context="Value")
63
+ enforce_primitive!(Array, types, 'lib/strict')
64
+ types.each {|type| enforce_primitive!(Object, type, 'lib/strict')}
65
+
66
+ match_found = false
67
+ types.each do |type|
68
+ begin
69
+ s = @@errors.size
70
+ enforce_primitive!(type, data, context)
71
+ match_found = true unless s != @@errors.size
72
+ rescue
73
+ next
74
+ end
75
+ end
76
+ catch_error "#{header(context, data)} must be of one of the types of #{types.inspect}" unless match_found
77
+ return data
78
+ end
79
+
80
+ def enforce_non_nil!(obj, context="Value")
81
+ catch_error "#{context}: Object is nil!" if obj.nil?
82
+ return obj
83
+ end
84
+
85
+ def enforce!(supertype, data, context="Value") #write a more generic handler, for new types?
86
+ enforce_non_nil!(data, context)
87
+
88
+ if supertype.is_a? Array #enumeration of values
89
+ catch_error "#{header(context, data)} should take on a value within #{supertype.inspect}" unless supertype.include? data
90
+
91
+ else if supertype.is_a? Symbol #distinct type
92
+ case supertype
93
+ when :string
94
+ enforce_primitive!(String, data, context)
95
+ catch_error "#{header(context, data)} can't be empty string" unless (data.size > 0)
96
+
97
+ when :hex_color
98
+ enforce!(:string, data, context)
99
+ catch_error "#{header(context, data)} must be six characters long" unless (data.size == 6)
100
+ data.upcase.each_byte {|c| catch_error "#{header(context, data)} must contain only hexadecimal characters" unless ((48 <= c and c <= 57) or (65 <= c and c <= 70))}
101
+
102
+ when :natural_number
103
+ enforce_primitive!(Fixnum, data, context)
104
+ catch_error "#{header(context, data)} must be > 0" unless (data > 0)
105
+
106
+ when :integer
107
+ enforce_primitive!(Fixnum, data, context)
108
+
109
+ when :numeric
110
+ enforce_primitive!(Numeric, data, context)
111
+
112
+ when :boolean
113
+ enforce_weak_primitives!([TrueClass, FalseClass], data, context)
114
+
115
+ when :float
116
+ enforce_primitive!(Float, data, context)
117
+
118
+ when :string_array
119
+ enforce_primitive!(Array, data, context)
120
+ data.each {|item| enforce_primitive!(String, item, context)}
121
+
122
+ when :numeric_array
123
+ enforce_primitive!(Array, data, context)
124
+ data.each {|item| enforce_primitive!(Numeric, item, context)}
125
+
126
+ when :float_array
127
+ enforce_primitive!(Array, data, context)
128
+ data.each {|item| enforce_primitive!(Float, item, context)}
129
+
130
+ when :integer_array
131
+ enforce_primitive!(Array, data, context)
132
+ data.each {|item| enforce_primitive!(Fixnum, item, context)}
133
+
134
+ else
135
+ catch_error "undefined symbol-supertype encountered: #{supertype.inspect}"
136
+ end
137
+ end
138
+ end
139
+ return data
140
+ end
141
+
142
+ def enforce_map!(matrix, map)
143
+ enforce_primitive!(Hash, matrix, "lib/strict")
144
+ enforce_primitive!(Hash, map, "lib/strict")
145
+
146
+ setmode_catch!
147
+
148
+ matrix.each do |param, type|
149
+ if map.has_key? param
150
+ enforce!(type, map[param], "map[#{param.inspect}]")
151
+ else
152
+ catch_error "map: missing required param: #{param.inspect}"
153
+ end
154
+ end
155
+ raise_hell!
156
+ setmode_raise!
157
+ return map
158
+ end
159
+ end
@@ -0,0 +1,8 @@
1
+ require "test/unit"
2
+ require "typestrict"
3
+
4
+ class TestStrict < Test::Unit::TestCase
5
+ def test_sanity
6
+ puts "no tests implemented!"
7
+ end
8
+ end
metadata ADDED
@@ -0,0 +1,108 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: typestrict
3
+ version: !ruby/object:Gem::Version
4
+ hash: 23
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 4
10
+ version: 0.0.4
11
+ platform: ruby
12
+ authors:
13
+ - Raeez Lorgat
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-08-03 00:00:00 -04:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: rubyforge
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 7
30
+ segments:
31
+ - 2
32
+ - 0
33
+ - 4
34
+ version: 2.0.4
35
+ type: :development
36
+ version_requirements: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ name: hoe
39
+ prerelease: false
40
+ requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ hash: 21
46
+ segments:
47
+ - 2
48
+ - 6
49
+ - 1
50
+ version: 2.6.1
51
+ type: :development
52
+ version_requirements: *id002
53
+ description: Static typesystem for ruby
54
+ email: raeez@mit.edu
55
+ executables:
56
+ - typestrict
57
+ extensions: []
58
+
59
+ extra_rdoc_files:
60
+ - History.txt
61
+ - Manifest.txt
62
+ - README.txt
63
+ files:
64
+ - History.txt
65
+ - Manifest.txt
66
+ - README
67
+ - README.txt
68
+ - Rakefile
69
+ - bin/typestrict
70
+ - lib/typestrict.rb
71
+ - test/test_strict.rb
72
+ has_rdoc: true
73
+ homepage: http://www.raeez.com/strict
74
+ licenses: []
75
+
76
+ post_install_message:
77
+ rdoc_options:
78
+ - --main
79
+ - README.txt
80
+ require_paths:
81
+ - lib
82
+ required_ruby_version: !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ hash: 3
88
+ segments:
89
+ - 0
90
+ version: "0"
91
+ required_rubygems_version: !ruby/object:Gem::Requirement
92
+ none: false
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ hash: 3
97
+ segments:
98
+ - 0
99
+ version: "0"
100
+ requirements: []
101
+
102
+ rubyforge_project: strict
103
+ rubygems_version: 1.3.7
104
+ signing_key:
105
+ specification_version: 3
106
+ summary: Strict implements a static typesystem over dynamic ruby objects
107
+ test_files:
108
+ - test/test_strict.rb