platypus 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/HISTORY ADDED
@@ -0,0 +1,19 @@
1
+ = Change History
2
+
3
+ # == History
4
+ #
5
+ # * 2006-06-06 3v1l_d4y:
6
+ # * Removed transformation options.
7
+ # * Removed StringIO typecast. It is not required by default.
8
+ # * Added TypeCastException for better error reporting while coding.
9
+ #
10
+
11
+ == 1.0.0 // 2009-07-07
12
+
13
+ This is the initial stand-alone release of TypeCast,
14
+ spun-off from Ruby Facets.
15
+
16
+ * 1 Major Enhancement
17
+
18
+ * Happy Birthday!
19
+
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License
2
+
3
+ Copyright (c) 2010 Thomas Sawyer
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
22
+
@@ -0,0 +1,6 @@
1
+ = Developer's Notes
2
+
3
+ == 2010-05-27 | Alias for +Overloadable#overload+
4
+
5
+ I am not sure if I like +con+, +sig+ or +over+ better as a short alias for +overload+. I thought of +con+ becuase of it's double meaning as Latin for 'with' and as an abbreviation for 'conditional'. But it seems esoteric in practice, perhaps b/c it would be too general a term if we were programming in Spanish. While +over+ probably makes the most sense in terms of being an abbreviated form of +overload+, +sig+ is the same number of characters as +def+ and conveys some additional semantics which applies to method overloading --the *signature*.
6
+
data/PROFILE ADDED
@@ -0,0 +1,24 @@
1
+ ---
2
+ title : Platypus
3
+ suite : rubyworks
4
+ summary: Type Casting System
5
+ license: MIT
6
+ created: 2004-01-01
7
+
8
+ copyright:
9
+ Copyright (c) 2004 Thomas Sawyer
10
+
11
+ authors:
12
+ - Thomas Sawyer
13
+ - Jonas Pfenniger
14
+
15
+ description:
16
+ Provides a complete double-dispatch type conversion system,
17
+ method overloadability and psuedo-classes.
18
+
19
+ resources:
20
+ homepage: http://rubyworks.github.com/platypus
21
+ development: http://github.com/rubyworks/platypus
22
+ repository: http://rubyworks.github.com/platypus.git
23
+ forum: http://googlegroups/group/rubyworks-mailinglist
24
+
data/README ADDED
@@ -0,0 +1,94 @@
1
+ = Platypus
2
+ _ ___
3
+ / \ / \
4
+ \. |: cc|
5
+ (.|:,---,
6
+ (.|: \ c|
7
+ (. y-'
8
+ \ _ /
9
+ m m
10
+
11
+ * home: http://rubyworks.github.com/platypus
12
+ * work: http://github.com/rubyworks/platypus
13
+
14
+
15
+ == DESCRIPTION
16
+
17
+ Platypus provides a generalized type conversion system,
18
+ method overloading and psuedo-classes.
19
+
20
+
21
+ == RELEASE NOTES
22
+
23
+ Please see HISTORY file.
24
+
25
+
26
+ == SYNOPSIS
27
+
28
+ === Type Conversion
29
+
30
+ "1234".to(Float) => 1234.0 (Float)
31
+
32
+ Time.from("6:30") => 1234.0 (Time)
33
+
34
+ === Method Overloading
35
+
36
+ To overload a method use the #overload method to define
37
+ new functionality based on a specified type interface.
38
+
39
+ class X
40
+ include Overloadable
41
+
42
+ def x
43
+ "hello"
44
+ end
45
+
46
+ sig Integer
47
+
48
+ def x(i)
49
+ i
50
+ end
51
+
52
+ sig String, String
53
+
54
+ def x(s1, s2)
55
+ [s1, s2]
56
+ end
57
+ end
58
+
59
+ === Psuedo-Classes
60
+
61
+ class KiloType < Type
62
+ x % 1000 == 0
63
+ end
64
+
65
+ KiloType === 1000
66
+ KiloType === 2000
67
+
68
+
69
+ == INSTALLATION
70
+
71
+ To install with RubyGems simply open a console and type:
72
+
73
+ $ gem install typecast
74
+
75
+ Site installation can be achieved with Setup.rb (gem install setup),
76
+ then download the tarball package and type:
77
+
78
+ $ tar -xvzf typecast-1.0.0.tgz
79
+ $ cd typecast-1.0.0
80
+ $ sudo setup.rb all
81
+
82
+ Windows users use 'ruby setup.rb all'.
83
+
84
+
85
+ == COPYING
86
+
87
+ Copyright (c) 2010 Thomas Sawyer
88
+
89
+ This program is ditributed unser the terms of the Ruby license.
90
+
91
+ See LICENSE or COPYING file for details.
92
+
93
+ #--
94
+ *-* mode: rdoc *-*
data/REQUIRE ADDED
@@ -0,0 +1,6 @@
1
+ development:
2
+ - syckle
3
+
4
+ development/test:
5
+ - qed
6
+
data/VERSION ADDED
@@ -0,0 +1,5 @@
1
+ name : platypus
2
+ major: 1
3
+ minor: 0
4
+ patch: 0
5
+ date : 2010-05-27
@@ -0,0 +1,4 @@
1
+ require 'platypus/typecast'
2
+ require 'platypus/overload'
3
+ require 'platypus/type'
4
+
@@ -0,0 +1,8 @@
1
+ module Kernel
2
+
3
+ def case?(*matchers)
4
+ matchers.all?{ |m| m === self }
5
+ end unless method_defined?(:case?)
6
+
7
+ end
8
+
@@ -0,0 +1,98 @@
1
+ # The Obverloadable mixin allows you to easily
2
+ # overload methods based on method signitures.
3
+ #
4
+ module Overloadable
5
+
6
+ #
7
+ def self.append_features(base)
8
+ if Module==base
9
+ super(base)
10
+ else
11
+ base.extend(self)
12
+ end
13
+ end
14
+
15
+ # Setup an overload state.
16
+ def overload(*signature)
17
+ (@overload_stack ||= []) << signature
18
+ end
19
+
20
+ # Short alias for +overload+.
21
+ alias_method :sig, :overload
22
+
23
+ #
24
+ def method_added(name)
25
+ return if $skip
26
+
27
+ @overload_stack ||= []
28
+ @overload_method ||= {}
29
+
30
+ signature = @overload_stack.pop
31
+
32
+ if !method_defined?("#{name}:origin")
33
+ $skip = true
34
+ if signature
35
+ define_method("#{name}:origin"){|*a| raise ArgumentError }
36
+ else
37
+ alias_method("#{name}:origin", name)
38
+ end
39
+ $skip = false
40
+ end
41
+
42
+ if signature
43
+ @overload_module ||= Module.new
44
+
45
+ include @overload_module
46
+
47
+ signature = Signature[*signature]
48
+ @overload_method[name] ||= []
49
+ @overload_method[name] << signature
50
+
51
+ signame = "#{name}:#{signature.key}"
52
+
53
+ alias_method(signame, name)
54
+
55
+ sigs = @overload_method[name]
56
+ $skip = true
57
+ define_method(name) do |*args|
58
+ #sigs.sort.each do |sig|
59
+ s = sigs.find{ |sig| sig.match?(args) }
60
+ if s
61
+ __send__("#{name}:#{s.key}", *args)
62
+ else
63
+ __send__("#{name}:origin", *args)
64
+ end
65
+ end
66
+ $skip = false
67
+ end
68
+
69
+ end
70
+
71
+ #
72
+ class Signature < Array
73
+ def key
74
+ hash #Marshal.dump(self)
75
+ end
76
+
77
+ #
78
+ def match?(args)
79
+ return false unless size == args.size
80
+ size.times do |i|
81
+ return false unless self[i] === args[i]
82
+ end
83
+ true
84
+ end
85
+
86
+ #
87
+ def <=>(other)
88
+ cmp = (size <=> other.size)
89
+ return cmp if cmp && cmp != 0
90
+ size.times do |i|
91
+ cmp = (self[i] <=> other[i])
92
+ return cmp if cmp && cmp != 0
93
+ end
94
+ 0
95
+ end
96
+ end
97
+
98
+ end
@@ -0,0 +1,153 @@
1
+ require 'platypus/core_ext'
2
+
3
+ # Base class for Euphoria-like Types.
4
+ #
5
+ # class KiloType < Type
6
+ # condition do |x|
7
+ # x.case? Integer
8
+ # x.kind_of?(Integer)
9
+ # x.respond_to?(:succ)
10
+ # x > 1000
11
+ # end
12
+ # end
13
+ #
14
+ # Becuase the +x.something?+ is so common, TypeCast provides
15
+ # special "magic-dot" method to make defining these
16
+ # conditions more concise.
17
+ #
18
+ # class KiloType < Type
19
+ # x.case? Integer
20
+ # x.kind_of?(Integer)
21
+ # x.respond_to?(:succ)
22
+ # x > 1000
23
+ # end
24
+ #
25
+ # While TypeCasts are not actual types in the sense they
26
+ # are not actual classes. They can be used for conversion
27
+ # by defining a "from_{class}" class method. In doing so
28
+ # you should make sure the result of the conversion conforms
29
+ # to the typecast. You can use the TypeCast.validate method
30
+ # to make that a bit easier. For instance:
31
+ #
32
+ # class KiloType
33
+ # def from_string(str)
34
+ # validate(str.to_i)
35
+ # end
36
+ # end
37
+ #
38
+ # TypeCast also provides a helper DSL method that handles
39
+ # this for you.
40
+ #
41
+ # class KiloType
42
+ # conversion String do |str|
43
+ # str.to_i
44
+ # end
45
+ # end
46
+ #
47
+ # This will define a method equivalent to the prior example.
48
+
49
+ class Type
50
+
51
+ # Activeate/Deactivate type-checking globally (NOT USED YET).
52
+ def self.check(on_or_off=nil)
53
+ @check = on_or_off unless on_or_off.nil?
54
+ @check
55
+ end
56
+
57
+ def self.validate(obj)
58
+ raise TypeError unless self === obj
59
+ return obj
60
+ end
61
+
62
+ #
63
+ def self.condition(&block)
64
+ @index ||= index
65
+ @index = @index.succ
66
+ define_method("condition_#{@index}", &block)
67
+ #@conditions << block
68
+ end
69
+
70
+ #
71
+ def self.conversion(klass, &block)
72
+ name = klass.name.downcase.gsub('::', '_')
73
+ (class << self; self; end).class_eval do
74
+ define_method("from_#{name}") do |from|
75
+ r = block.call(from)
76
+ validate(r)
77
+ end
78
+ end
79
+ end
80
+
81
+ #
82
+ def self.x
83
+ @x ||= Conditions.new(self)
84
+ end
85
+
86
+ #
87
+ class Conditions
88
+ instance_methods.each{ |x| private x unless x.to_s =~ /^__/ }
89
+
90
+ def initialize(type)
91
+ @type = type
92
+ end
93
+
94
+ #def __conditions__
95
+ # @__conditions__ ||= []
96
+ #end
97
+
98
+ def method_missing(s, *a, &b)
99
+ @type.condition do |x|
100
+ x.__send__(s, *a, &b)
101
+ end
102
+ #__conditions__ << [s, a, b]
103
+ end
104
+ end
105
+
106
+ #
107
+ #def initialize(*matchers, &validate)
108
+ # @matchers = matchers
109
+ # @validate = validate
110
+ #end
111
+
112
+ #
113
+ def self.index
114
+ methods = instance_methods.select{ |m| m.to_s =~ /^condition_/ }
115
+ indexes = methods.map{ |m| m.split('_')[1].to_i }
116
+ indexes.max || 0
117
+ end
118
+
119
+ def self.conditions
120
+ #x.__conditions__ + (@conditions || []) + (defined?(super) ? super : [])
121
+ instance_methods.select{ |m| m.to_s =~ /^condition_/ }
122
+ end
123
+
124
+ #
125
+ def self.===(obj)
126
+ #conditions.all? do |s, a, b|
127
+ # obj.__send__(s, *a, &b)
128
+ #end
129
+ instance = new
130
+ conditions.all? do |method|
131
+ instance.__send__(method, obj)
132
+ end
133
+ end
134
+
135
+ #
136
+ #def match?(argument)
137
+ # @matchers.all? do |matcher|
138
+ # case matcher
139
+ # when Symbol
140
+ # argument.send(:respond_to?, matcher) }
141
+ # else
142
+ # matcher === argument
143
+ # end
144
+ # end
145
+ #end
146
+
147
+ #
148
+ #def valid?(obj)
149
+ # @validate[obj] if @validate
150
+ #end
151
+
152
+ end
153
+
@@ -0,0 +1,57 @@
1
+ require 'set'
2
+
3
+ class Class
4
+
5
+ def typecasts
6
+ @typecasts ||= {}
7
+ end
8
+
9
+ # Define a type cast.
10
+ def typecast(target_class, *specifics, &block)
11
+ set = (specifics.empty? ? nil : Set.new(specifics))
12
+ typecasts[target_class] ||= {}
13
+ typecasts[target_class][set] = block
14
+ end
15
+
16
+ # Convert +source+ to an instance of the class.
17
+ def from(source, specifics={})
18
+ set = (specifics.empty? ? nil : Set.new(specifics.keys))
19
+ base = ancestors.find{ |anc| source.class.typecasts.key?(anc) }
20
+ if base
21
+ cast = source.class.typecasts[base]
22
+ if block = cast[set]
23
+ if block.arity == 1
24
+ return block.call(source)
25
+ else
26
+ return block.call(source, specifics)
27
+ end
28
+ end
29
+ end
30
+ raise TypeError
31
+ end
32
+
33
+ end
34
+
35
+
36
+ class Object
37
+ # Convert an object to an instance of given +target_class+.
38
+ def to(target_class, specifics={})
39
+ target_class.from(self, specifics)
40
+ end
41
+ end
42
+
43
+
44
+ class String #:nodoc:
45
+ typecast Integer do |string|
46
+ Integer(string)
47
+ end
48
+ end
49
+
50
+ class Time #:nodoc:
51
+ # This method will require the 'time.rb' Time extensions.
52
+ typecast String do
53
+ require 'time'
54
+ parse(string)
55
+ end
56
+ end
57
+
@@ -0,0 +1,173 @@
1
+ require 'platypus/overload'
2
+
3
+ #
4
+ class TC_Overload_01 < Test::Unit::TestCase
5
+
6
+ class X
7
+ include Overloadable
8
+
9
+ def x
10
+ "hello"
11
+ end
12
+
13
+ overload Array
14
+
15
+ def x(x)
16
+ [Array, x]
17
+ end
18
+
19
+ overload Symbol
20
+
21
+ def x(x)
22
+ [Symbol, x]
23
+ end
24
+ end
25
+
26
+ def setup
27
+ @x = X.new
28
+ end
29
+
30
+ def test_x
31
+ assert_equal( "hello", @x.x )
32
+ end
33
+
34
+ def test_a
35
+ assert_equal( [Array, [1]], @x.x([1]) )
36
+ end
37
+
38
+ def test_s
39
+ assert_equal( [Symbol, :a], @x.x(:a) )
40
+ end
41
+
42
+ end
43
+
44
+ #
45
+ class TC_Overload_02 < Test::Unit::TestCase
46
+
47
+ class X
48
+ include Overloadable
49
+
50
+ def x
51
+ "hello"
52
+ end
53
+
54
+ overload Integer
55
+
56
+ def x(i)
57
+ i
58
+ end
59
+
60
+ overload String, String
61
+
62
+ def x(s1, s2)
63
+ [s1, s2]
64
+ end
65
+
66
+ end
67
+
68
+ def setup
69
+ @x = X.new
70
+ end
71
+
72
+ def test_x
73
+ assert_equal( "hello", @x.x )
74
+ end
75
+
76
+ def test_i
77
+ assert_equal( 1, @x.x(1) )
78
+ end
79
+
80
+ def test_s
81
+ assert_equal( ["a","b"], @x.x("a","b") )
82
+ end
83
+
84
+ end
85
+
86
+ #
87
+ class TC_Overload_03 < Test::Unit::TestCase
88
+
89
+ class SubArray < Array
90
+ end
91
+
92
+ class SubSubArray < SubArray
93
+ end
94
+
95
+ class X
96
+ include Overloadable
97
+
98
+ def x
99
+ "hello"
100
+ end
101
+
102
+ overload Integer
103
+
104
+ def x(i)
105
+ i
106
+ end
107
+
108
+ overload Symbol
109
+
110
+ def x(s)
111
+ s
112
+ end
113
+
114
+ overload String, String
115
+
116
+ def x(s1, s2)
117
+ [s1, s2]
118
+ end
119
+
120
+ overload Symbol, String
121
+
122
+ def x(s1, s2)
123
+ [s1, s2]
124
+ end
125
+
126
+ overload Array
127
+
128
+ def x(a)
129
+ "array"
130
+ end
131
+
132
+ end
133
+
134
+ def setup
135
+ @x = X.new
136
+ end
137
+
138
+ def test_x
139
+ assert_equal( "hello", @x.x )
140
+ end
141
+
142
+ def test_i
143
+ assert_equal( 1, @x.x(1) )
144
+ end
145
+
146
+ def test_strings
147
+ assert_equal( ["a","b"], @x.x("a","b") )
148
+ end
149
+
150
+ def test_symbol_string
151
+ assert_equal( [:a,"b"], @x.x(:a,"b") )
152
+ end
153
+
154
+ def test_sym
155
+ assert_equal( :sym, @x.x(:sym) )
156
+ end
157
+
158
+ def test_subarray
159
+ assert_equal("array", @x.x([]))
160
+ assert_equal("array", @x.x(SubArray.new))
161
+ assert_equal("array", @x.x(SubSubArray.new))
162
+ end
163
+
164
+ #def test_raise
165
+ # assert_raise ArgumentError do
166
+ # X.module_eval do
167
+ # overload 42
168
+ # end
169
+ # end
170
+ #end
171
+
172
+ end
173
+
metadata ADDED
@@ -0,0 +1,78 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: platypus
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 1
7
+ - 0
8
+ - 0
9
+ version: 1.0.0
10
+ platform: ruby
11
+ authors:
12
+ - Thomas Sawyer
13
+ - Jonas Pfenniger
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-05-27 00:00:00 -04:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description: Provides a complete double-dispatch type conversion system, method overloadability and psuedo-classes.
23
+ email:
24
+ executables: []
25
+
26
+ extensions: []
27
+
28
+ extra_rdoc_files:
29
+ - README
30
+ files:
31
+ - lib/platypus/core_ext.rb
32
+ - lib/platypus/overload.rb
33
+ - lib/platypus/type.rb
34
+ - lib/platypus/typecast.rb
35
+ - lib/platypus.rb
36
+ - test/test_overload.rb
37
+ - PROFILE
38
+ - LICENSE
39
+ - README
40
+ - HISTORY
41
+ - NOTES.rdoc
42
+ - REQUIRE
43
+ - VERSION
44
+ has_rdoc: true
45
+ homepage: http://rubyworks.github.com/platypus
46
+ licenses: []
47
+
48
+ post_install_message:
49
+ rdoc_options:
50
+ - --title
51
+ - Platypus API
52
+ - --main
53
+ - README
54
+ require_paths:
55
+ - lib
56
+ required_ruby_version: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ segments:
61
+ - 0
62
+ version: "0"
63
+ required_rubygems_version: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ segments:
68
+ - 0
69
+ version: "0"
70
+ requirements: []
71
+
72
+ rubyforge_project: platypus
73
+ rubygems_version: 1.3.6
74
+ signing_key:
75
+ specification_version: 3
76
+ summary: Type Casting System
77
+ test_files:
78
+ - test/test_overload.rb