rubycube 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGES +30 -0
  3. data/MANIFEST +12 -0
  4. data/README.md +69 -0
  5. data/Rakefile +50 -0
  6. data/certs/djberg96_pub.pem +21 -0
  7. data/cube.gemspec +23 -0
  8. data/doc/Bar.html +102 -0
  9. data/doc/CHANGES.html +159 -0
  10. data/doc/Foo.html +184 -0
  11. data/doc/Interface.html +278 -0
  12. data/doc/Interface/MethodMissing.html +104 -0
  13. data/doc/Interface/PrivateVisibleMethodMissing.html +102 -0
  14. data/doc/Interface/PublicVisibleMethodMissing.html +102 -0
  15. data/doc/MANIFEST.html +114 -0
  16. data/doc/Module.html +153 -0
  17. data/doc/MyClass.html +234 -0
  18. data/doc/MyInterface.html +106 -0
  19. data/doc/MySubInterface.html +110 -0
  20. data/doc/Object.html +299 -0
  21. data/doc/README.html +222 -0
  22. data/doc/Rakefile.html +148 -0
  23. data/doc/TC_Interface.html +438 -0
  24. data/doc/certs/djberg96_pub_pem.html +110 -0
  25. data/doc/created.rid +13 -0
  26. data/doc/css/fonts.css +167 -0
  27. data/doc/css/rdoc.css +590 -0
  28. data/doc/fonts/Lato-Light.ttf +0 -0
  29. data/doc/fonts/Lato-LightItalic.ttf +0 -0
  30. data/doc/fonts/Lato-Regular.ttf +0 -0
  31. data/doc/fonts/Lato-RegularItalic.ttf +0 -0
  32. data/doc/fonts/SourceCodePro-Bold.ttf +0 -0
  33. data/doc/fonts/SourceCodePro-Regular.ttf +0 -0
  34. data/doc/images/add.png +0 -0
  35. data/doc/images/arrow_up.png +0 -0
  36. data/doc/images/brick.png +0 -0
  37. data/doc/images/brick_link.png +0 -0
  38. data/doc/images/bug.png +0 -0
  39. data/doc/images/bullet_black.png +0 -0
  40. data/doc/images/bullet_toggle_minus.png +0 -0
  41. data/doc/images/bullet_toggle_plus.png +0 -0
  42. data/doc/images/date.png +0 -0
  43. data/doc/images/delete.png +0 -0
  44. data/doc/images/find.png +0 -0
  45. data/doc/images/loadingAnimation.gif +0 -0
  46. data/doc/images/macFFBgHack.png +0 -0
  47. data/doc/images/package.png +0 -0
  48. data/doc/images/page_green.png +0 -0
  49. data/doc/images/page_white_text.png +0 -0
  50. data/doc/images/page_white_width.png +0 -0
  51. data/doc/images/plugin.png +0 -0
  52. data/doc/images/ruby.png +0 -0
  53. data/doc/images/tag_blue.png +0 -0
  54. data/doc/images/tag_green.png +0 -0
  55. data/doc/images/transparent.png +0 -0
  56. data/doc/images/wrench.png +0 -0
  57. data/doc/images/wrench_orange.png +0 -0
  58. data/doc/images/zoom.png +0 -0
  59. data/doc/index.html +121 -0
  60. data/doc/interface_gemspec.html +121 -0
  61. data/doc/js/darkfish.js +161 -0
  62. data/doc/js/jquery.js +4 -0
  63. data/doc/js/navigation.js +142 -0
  64. data/doc/js/navigation.js.gz +0 -0
  65. data/doc/js/search.js +109 -0
  66. data/doc/js/search_index.js +1 -0
  67. data/doc/js/search_index.js.gz +0 -0
  68. data/doc/js/searcher.js +228 -0
  69. data/doc/js/searcher.js.gz +0 -0
  70. data/doc/table_of_contents.html +227 -0
  71. data/examples/demo.rb +96 -0
  72. data/lib/cube.rb +2 -0
  73. data/lib/cube/interfaces.rb +266 -0
  74. data/lib/cube/traits.rb +75 -0
  75. data/test/test_interface.rb +111 -0
  76. metadata +151 -0
Binary file
@@ -0,0 +1,227 @@
1
+ <!DOCTYPE html>
2
+
3
+ <html>
4
+ <head>
5
+ <meta charset="UTF-8">
6
+
7
+ <title>Table of Contents - RDoc Documentation</title>
8
+
9
+ <script type="text/javascript">
10
+ var rdoc_rel_prefix = "./";
11
+ </script>
12
+
13
+ <script src="./js/jquery.js"></script>
14
+ <script src="./js/darkfish.js"></script>
15
+
16
+ <link href="./css/fonts.css" rel="stylesheet">
17
+ <link href="./css/rdoc.css" rel="stylesheet">
18
+
19
+
20
+
21
+ <body id="top" class="table-of-contents">
22
+ <main role="main">
23
+ <h1 class="class">Table of Contents - RDoc Documentation</h1>
24
+
25
+ <h2 id="pages">Pages</h2>
26
+ <ul>
27
+ <li class="file">
28
+ <a href="CHANGES.html">CHANGES</a>
29
+
30
+ <ul>
31
+ <li><a href="CHANGES.html#label-1.0.4+-+8-Jan-2016">1.0.4 - 8-Jan-2016</a>
32
+ <li><a href="CHANGES.html#label-1.0.3+-+12-Oct-2014">1.0.3 - 12-Oct-2014</a>
33
+ <li><a href="CHANGES.html#label-1.0.2+-+7-Oct-2009">1.0.2 - 7-Oct-2009</a>
34
+ <li><a href="CHANGES.html#label-1.0.1+-+29-Jul-2009">1.0.1 - 29-Jul-2009</a>
35
+ <li><a href="CHANGES.html#label-1.0.0+-+5-Jun-2005">1.0.0 - 5-Jun-2005</a>
36
+ <li><a href="CHANGES.html#label-0.1.0+-+9-May-2004">0.1.0 - 9-May-2004</a>
37
+ </ul>
38
+ </li>
39
+ <li class="file">
40
+ <a href="MANIFEST.html">MANIFEST</a>
41
+ </li>
42
+ <li class="file">
43
+ <a href="README.html">README</a>
44
+
45
+ <ul>
46
+ <li><a href="README.html#label-Description">Description</a>
47
+ <li><a href="README.html#label-Installation">Installation</a>
48
+ <li><a href="README.html#label-Synopsis">Synopsis</a>
49
+ <li><a href="README.html#label-General+Notes">General Notes</a>
50
+ <li><a href="README.html#label-Runtime+performance+of+check+methods">Runtime performance of check methods</a>
51
+ <li><a href="README.html#label-Developer-27s+Notes">Developer&#39;s Notes</a>
52
+ <li><a href="README.html#label-Acknowledgements">Acknowledgements</a>
53
+ <li><a href="README.html#label-Copyright">Copyright</a>
54
+ <li><a href="README.html#label-Warranty">Warranty</a>
55
+ <li><a href="README.html#label-License">License</a>
56
+ <li><a href="README.html#label-Author">Author</a>
57
+ </ul>
58
+ </li>
59
+ <li class="file">
60
+ <a href="Rakefile.html">Rakefile</a>
61
+ </li>
62
+ <li class="file">
63
+ <a href="certs/djberg96_pub_pem.html">djberg96_pub.pem</a>
64
+ </li>
65
+ <li class="file">
66
+ <a href="interface_gemspec.html">interface.gemspec</a>
67
+ </li>
68
+
69
+ </ul>
70
+
71
+ <h2 id="classes">Classes and Modules</h2>
72
+ <ul>
73
+ <li class="class">
74
+ <a href="Bar.html">Bar</a>
75
+ </li>
76
+ <li class="class">
77
+ <a href="Foo.html">Foo</a>
78
+ </li>
79
+ <li class="module">
80
+ <a href="Interface.html">Interface</a>
81
+ </li>
82
+ <li class="class">
83
+ <a href="Interface/MethodMissing.html">Interface::MethodMissing</a>
84
+ </li>
85
+ <li class="class">
86
+ <a href="Interface/PrivateVisibleMethodMissing.html">Interface::PrivateVisibleMethodMissing</a>
87
+ </li>
88
+ <li class="class">
89
+ <a href="Interface/PublicVisibleMethodMissing.html">Interface::PublicVisibleMethodMissing</a>
90
+ </li>
91
+ <li class="class">
92
+ <a href="Module.html">Module</a>
93
+ </li>
94
+ <li class="class">
95
+ <a href="MyClass.html">MyClass</a>
96
+ </li>
97
+ <li class="module">
98
+ <a href="MyInterface.html">MyInterface</a>
99
+ </li>
100
+ <li class="module">
101
+ <a href="MySubInterface.html">MySubInterface</a>
102
+ </li>
103
+ <li class="class">
104
+ <a href="Object.html">Object</a>
105
+ </li>
106
+ <li class="class">
107
+ <a href="TC_Interface.html">TC_Interface</a>
108
+ </li>
109
+ </ul>
110
+
111
+ <h2 id="methods">Methods</h2>
112
+ <ul>
113
+
114
+ <li class="method">
115
+ <a href="TC_Interface.html#method-c-startup">::startup</a>
116
+ &mdash;
117
+ <span class="container">TC_Interface</span>
118
+
119
+ <li class="method">
120
+ <a href="Foo.html#method-i-bar">#bar</a>
121
+ &mdash;
122
+ <span class="container">Foo</span>
123
+
124
+ <li class="method">
125
+ <a href="MyClass.html#method-i-bar">#bar</a>
126
+ &mdash;
127
+ <span class="container">MyClass</span>
128
+
129
+ <li class="method">
130
+ <a href="MyClass.html#method-i-baz">#baz</a>
131
+ &mdash;
132
+ <span class="container">MyClass</span>
133
+
134
+ <li class="method">
135
+ <a href="Object.html#method-i-check_class">#check_class</a>
136
+ &mdash;
137
+ <span class="container">Object</span>
138
+
139
+ <li class="method">
140
+ <a href="Object.html#method-i-check_interface">#check_interface</a>
141
+ &mdash;
142
+ <span class="container">Object</span>
143
+
144
+ <li class="method">
145
+ <a href="TC_Interface.html#method-i-checker_method">#checker_method</a>
146
+ &mdash;
147
+ <span class="container">TC_Interface</span>
148
+
149
+ <li class="method">
150
+ <a href="Foo.html#method-i-foo">#foo</a>
151
+ &mdash;
152
+ <span class="container">Foo</span>
153
+
154
+ <li class="method">
155
+ <a href="MyClass.html#method-i-foo">#foo</a>
156
+ &mdash;
157
+ <span class="container">MyClass</span>
158
+
159
+ <li class="method">
160
+ <a href="Module.html#method-i-implements-3F">#implements?</a>
161
+ &mdash;
162
+ <span class="container">Module</span>
163
+
164
+ <li class="method">
165
+ <a href="Object.html#method-i-interface">#interface</a>
166
+ &mdash;
167
+ <span class="container">Object</span>
168
+
169
+ <li class="method">
170
+ <a href="Interface.html#method-i-private_visible">#private_visible</a>
171
+ &mdash;
172
+ <span class="container">Interface</span>
173
+
174
+ <li class="method">
175
+ <a href="Interface.html#method-i-public_visible">#public_visible</a>
176
+ &mdash;
177
+ <span class="container">Interface</span>
178
+
179
+ <li class="method">
180
+ <a href="Interface.html#method-i-required_public_methods">#required_public_methods</a>
181
+ &mdash;
182
+ <span class="container">Interface</span>
183
+
184
+ <li class="method">
185
+ <a href="TC_Interface.html#method-i-test_alpha_interface_requirements_met">#test_alpha_interface_requirements_met</a>
186
+ &mdash;
187
+ <span class="container">TC_Interface</span>
188
+
189
+ <li class="method">
190
+ <a href="TC_Interface.html#method-i-test_gamma_interface_requirements_met">#test_gamma_interface_requirements_met</a>
191
+ &mdash;
192
+ <span class="container">TC_Interface</span>
193
+
194
+ <li class="method">
195
+ <a href="TC_Interface.html#method-i-test_interface_requirements_not_met">#test_interface_requirements_not_met</a>
196
+ &mdash;
197
+ <span class="container">TC_Interface</span>
198
+
199
+ <li class="method">
200
+ <a href="TC_Interface.html#method-i-test_method_check">#test_method_check</a>
201
+ &mdash;
202
+ <span class="container">TC_Interface</span>
203
+
204
+ <li class="method">
205
+ <a href="TC_Interface.html#method-i-test_sub_interface_requirements_not_met">#test_sub_interface_requirements_not_met</a>
206
+ &mdash;
207
+ <span class="container">TC_Interface</span>
208
+
209
+ <li class="method">
210
+ <a href="TC_Interface.html#method-i-test_version">#test_version</a>
211
+ &mdash;
212
+ <span class="container">TC_Interface</span>
213
+
214
+ <li class="method">
215
+ <a href="Interface.html#method-i-unrequired_methods">#unrequired_methods</a>
216
+ &mdash;
217
+ <span class="container">Interface</span>
218
+ </ul>
219
+ </main>
220
+
221
+
222
+ <footer id="validator-badges" role="contentinfo">
223
+ <p><a href="http://validator.w3.org/check/referer">Validate</a>
224
+ <p>Generated by <a href="http://docs.seattlerb.org/rdoc/">RDoc</a> 4.2.0.
225
+ <p>Based on <a href="http://deveiate.org/projects/Darkfish-RDoc/">Darkfish</a> by <a href="http://deveiate.org">Michael Granger</a>.
226
+ </footer>
227
+
@@ -0,0 +1,96 @@
1
+ # run as `RUBY_INTERFACE_TYPECHECK= 1 ruby examples/demo.rb`
2
+ require_relative '../lib/cube'
3
+
4
+ Adder = interface {
5
+ # sum is a method that takes an array of Integer and returns an Integer
6
+ proto(:sum, [Integer]) { Integer }
7
+ }
8
+
9
+ Calculator = interface {
10
+ # interfaces can be composed
11
+ extends Adder
12
+ # method fact takes an Integer and returns an Integer
13
+ proto(:fact, Integer) { Integer }
14
+ # method pos takes an array of Integers, an Integer, and returns either Integer or nil
15
+ proto(:pos, [Integer], Integer) { [Integer, NilClass].to_set }
16
+ }
17
+
18
+ class SimpleCalc
19
+ def fact(n)
20
+ (2..n).reduce(1) { |m, e| m * e }
21
+ end
22
+
23
+ def sum(a)
24
+ a.reduce(0, &:+)
25
+ end
26
+
27
+ def pos(arr, i)
28
+ arr.index(i)
29
+ end
30
+
31
+ # implements Calculator #, runtime_checks: false # default is true
32
+ end
33
+
34
+ c = SimpleCalc.as_interface(Calculator).new
35
+ # If SimpleCalc does not have `implements Calculator`, but its methods match the interface
36
+ # you can "cast" it to Calculator - `SimpleCalc.as_interface(Calculator).new`
37
+ # This is useful for casting classes that you did not write
38
+ p c.sum([1, 2])
39
+ p c.pos([1, 2, 3], 4)
40
+
41
+ AdvancedCalculator = interface {
42
+ extend Calculator
43
+ proto(:product, Integer, Integer) { Integer }
44
+ }
45
+
46
+ module AdvancedCalcT
47
+ extend Cube::Trait
48
+ def product(a, b)
49
+ ret = 0
50
+ a.times { ret = sum([ret, b]) }
51
+ ret
52
+ end
53
+
54
+ def sum; end # A class method always takes precedence, no conflict here
55
+ def foo; end # this conflicts with DummyCalcT. Needs to be aliased (see below)
56
+ def bar; end # this conflicts with DummyCalcT. Needs to be aliased (see below)
57
+
58
+ requires_interface Adder # Note that this will give an error if SimpleCalc#sum is removed
59
+ # even if this trait itself has a `sum` method
60
+ end
61
+
62
+ module DummyCalcT
63
+ extend Cube::Trait
64
+ def sum; end # this method conflicts with AdvancedCalcT, but SimpleCalc#sum takes precedence
65
+ def foo; end
66
+ def bar; end
67
+ class << self
68
+ def included(_)
69
+ $stderr.puts "Works like a regular module as well"
70
+ end
71
+ end
72
+ end
73
+
74
+ # This is how we compose behaviours
75
+ # AdvancedCalc is a class which mixes traits AdvancedCalcT and DummyCalcT
76
+ # into SimpleCalc and implements the interface AdvancedCalculator
77
+ # To avoid conflicts, alias methods in AdvancedCalcT (otherwise error will be raised)
78
+ # One can also suppress methods in DummyCalcT
79
+ AdvancedCalc = SimpleCalc.with_trait(AdvancedCalcT,
80
+ aliases: { foo: :adfoo, bar: :adbar })
81
+ .with_trait(DummyCalcT, suppresses: [:foo, :bar])
82
+ .as_interface(AdvancedCalculator)
83
+
84
+ sc = AdvancedCalc.new
85
+ p sc.product(3, 2)
86
+
87
+
88
+ # Benchmarks. Run with RUBY_INTERFACE_TYPECHECK=0 and 1 to compare
89
+
90
+ t1 = Time.now
91
+ 1_000_000.times do
92
+ c.fact(50)
93
+ end
94
+ t2 = Time.now
95
+
96
+ p t2 - t1
@@ -0,0 +1,2 @@
1
+ require_relative 'cube/interfaces'
2
+ require_relative 'cube/traits'
@@ -0,0 +1,266 @@
1
+ require 'securerandom'
2
+ require 'set'
3
+ # A module for implementing Java style interfaces in Ruby. For more information
4
+ # about Java interfaces, please see:
5
+ #
6
+ # http://java.sun.com/docs/books/tutorial/java/concepts/interface.html
7
+ #
8
+ module Cube
9
+ module Interface
10
+ # The version of the interface library.
11
+ Interface::VERSION = '0.2.0'
12
+
13
+ # Raised if a class or instance does not meet the interface requirements.
14
+ class MethodMissing < RuntimeError; end
15
+ class PrivateVisibleMethodMissing < MethodMissing; end
16
+ class PublicVisibleMethodMissing < MethodMissing; end
17
+ class MethodArityError < RuntimeError; end
18
+ class TypeMismatchError < RuntimeError; end
19
+
20
+ alias :extends :extend
21
+
22
+ private
23
+
24
+ def convert_to_lambda &block
25
+ obj = Object.new
26
+ obj.define_singleton_method(:_, &block)
27
+ return obj.method(:_).to_proc
28
+ end
29
+
30
+ def extend_object(obj)
31
+ return append_features(obj) if Interface === obj
32
+ append_features(class << obj; self end)
33
+ included(obj)
34
+ end
35
+
36
+ def append_features(mod)
37
+ return super if Interface === mod
38
+
39
+ # Is this a sub-interface?
40
+ inherited = (self.ancestors-[self]).select{ |x| Interface === x }
41
+ inherited_ids = inherited.map{ |x| x.instance_variable_get('@ids') }
42
+
43
+ # Store required method ids
44
+ inherited_specs = map_spec(inherited_ids.flatten)
45
+ specs = @ids.merge(inherited_specs)
46
+ ids = @ids.keys + map_spec(inherited_ids.flatten).keys
47
+ @unreq ||= []
48
+
49
+
50
+ # Iterate over the methods, minus the unrequired methods, and raise
51
+ # an error if the method has not been defined.
52
+ mod_public_instance_methods = mod.public_instance_methods(true)
53
+ (ids - @unreq).uniq.each do |id|
54
+ id = id.to_s if RUBY_VERSION.to_f < 1.9
55
+ unless mod_public_instance_methods.include?(id)
56
+ raise Interface::PublicVisibleMethodMissing, "#{mod}: #{self}##{id}"
57
+ end
58
+ spec = specs[id]
59
+ if spec.is_a?(Hash) && spec.key?(:in) && spec[:in].is_a?(Array)
60
+ replace_check_method(mod, id, spec[:in], spec[:out])
61
+ end
62
+ end
63
+
64
+ inherited_private_ids = inherited.map{ |x| x.instance_variable_get('@private_ids') }
65
+ # Store required method ids
66
+ private_ids = @private_ids.keys + map_spec(inherited_private_ids.flatten).keys
67
+
68
+ # Iterate over the methods, minus the unrequired methods, and raise
69
+ # an error if the method has not been defined.
70
+ mod_all_methods = mod.instance_methods(true) + mod.private_instance_methods(true)
71
+
72
+ (private_ids - @unreq).uniq.each do |id|
73
+ id = id.to_s if RUBY_VERSION.to_f < 1.9
74
+ unless mod_all_methods.include?(id)
75
+ raise Interface::PrivateVisibleMethodMissing, "#{mod}: #{self}##{id}"
76
+ end
77
+ end
78
+
79
+ super mod
80
+ end
81
+
82
+ def replace_check_method(mod, id, inchecks, outcheck)
83
+ orig_method = mod.instance_method(id)
84
+
85
+ unless mod.instance_variable_defined?("@__interface_arity_skip") \
86
+ && mod.instance_variable_get("@__interface_arity_skip")
87
+ orig_arity = orig_method.parameters.size
88
+ check_arity = inchecks.size
89
+ if orig_arity != check_arity
90
+ raise Interface::MethodArityError,
91
+ "#{mod}: #{self}##{id} arity mismatch: #{orig_arity} instead of #{check_arity}"
92
+ end
93
+ end
94
+
95
+ unless ENV['RUBY_CUBE_TYPECHECK'].to_i > 0 \
96
+ && mod.instance_variable_defined?("@__interface_runtime_check") \
97
+ && mod.instance_variable_get("@__interface_runtime_check")
98
+ return
99
+ end
100
+ iface = self
101
+ mod.class_exec do
102
+ ns_meth_name = "#{id}_#{SecureRandom.hex(3)}".to_sym
103
+ alias_method ns_meth_name, id
104
+ define_method(id) do |*args|
105
+ args.each_index do |i|
106
+ v, t = args[i], inchecks[i]
107
+ begin
108
+ check_type(t, v)
109
+ rescue Interface::TypeMismatchError => e
110
+ raise Interface::TypeMismatchError,
111
+ "#{mod}: #{iface}##{id} (arg: #{i}): #{e.message}"
112
+ end
113
+ end
114
+ ret = send(ns_meth_name, *args)
115
+ begin
116
+ check_type(outcheck, ret) if outcheck
117
+ rescue Interface::TypeMismatchError => e
118
+ raise Interface::TypeMismatchError,
119
+ "#{mod}: #{iface}##{id} (return): #{e.message}"
120
+ end
121
+ ret
122
+ end
123
+ end
124
+ end
125
+
126
+ # def verify_arity(mod, meth)
127
+ # arity = mod.instance_method(meth).arity
128
+ # unless arity == @ids[meth]
129
+ # raise Interface::MethodArityError, "#{mod}: #{self}##{meth}=#{arity}. Should be #{@ids[meth]}"
130
+ # end
131
+ # end
132
+
133
+ def map_spec(ids)
134
+ ids.reduce({}) do |res, m|
135
+ if m.is_a?(Hash)
136
+ res.merge(m)
137
+ elsif m.is_a?(Symbol) || m.is_a?(String)
138
+ res.merge({ m.to_sym => nil })
139
+ end
140
+ end
141
+ end
142
+
143
+ def validate_spec(spec)
144
+ [*spec].each do |t|
145
+ if t.is_a?(Array)
146
+ unless t.first.is_a?(Module)
147
+ raise ArgumentError, "#{t} does not contain a Module or Interface"
148
+ end
149
+ elsif !t.is_a?(Module)
150
+ raise ArgumentError, "#{t} is not a Module or Interface"
151
+ end
152
+ end
153
+ end
154
+
155
+ public
156
+ # Accepts an array of method names that define the interface. When this
157
+ # module is included/implemented, those method names must have already been
158
+ # defined.
159
+ #
160
+ def required_public_methods
161
+ @ids.keys
162
+ end
163
+
164
+ def proto(meth, *args)
165
+ out_spec = yield if block_given?
166
+ validate_spec(args)
167
+ validate_spec(out_spec) if out_spec
168
+ @ids.merge!({ meth.to_sym => { in: args, out: out_spec }})
169
+ end
170
+
171
+ def public_visible(*ids)
172
+ unless ids.all? { |id| id.is_a?(Symbol) || id.is_a?(String) }
173
+ raise ArgumentError, "Arguments should be strings or symbols"
174
+ end
175
+ spec = map_spec(ids)
176
+ @ids.merge!(spec)
177
+ end
178
+
179
+ def private_visible(*ids)
180
+ unless ids.all? { |id| id.is_a?(Symbol) || id.is_a?(String) }
181
+ raise ArgumentError, "Arguments should be strings or symbols"
182
+ end
183
+ spec = map_spec(ids)
184
+ @private_ids.merge!(spec)
185
+ end
186
+ # Accepts an array of method names that are removed as a requirement for
187
+ # implementation. Presumably you would use this in a sub-interface where
188
+ # you only wanted a partial implementation of an existing interface.
189
+ #
190
+ def unrequired_methods(*ids)
191
+ @unreq ||= []
192
+ @unreq += ids
193
+ end
194
+
195
+ def shell
196
+ ids = @ids
197
+ unreq = @unreq
198
+ cls = Class.new(Object) do
199
+ (ids.keys - unreq).each do |m|
200
+ define_method(m) { |*args| }
201
+ end
202
+ end
203
+ cls.send(:shell_implements, self)
204
+ end
205
+ end
206
+ end
207
+
208
+ class Object
209
+ def interface(&block)
210
+ mod = Module.new
211
+ mod.extend(Cube::Interface)
212
+ mod.instance_variable_set('@ids', {})
213
+ mod.instance_variable_set('@private_ids', {})
214
+ mod.instance_eval(&block)
215
+ mod
216
+ end
217
+
218
+ if ENV['RUBY_CUBE_TYPECHECK'].to_i > 0
219
+ def check_type(t, v)
220
+ if t.is_a?(Set)
221
+ unless t.any? { |tp| check_type(tp, v) rescue false }
222
+ raise Cube::Interface::TypeMismatchError,
223
+ "#{v.inspect} is not any of #{tp.to_a}" unless v.is_a?(tp)
224
+ end
225
+ return
226
+ end
227
+ if t.is_a? Array
228
+ raise Cube::Interface::TypeMismatchError,
229
+ "#{v} is not an Array" unless v.is_a? Array
230
+ check_type(t.first, v.first)
231
+ check_type(t.first, v.last)
232
+ return
233
+ end
234
+ raise Cube::Interface::TypeMismatchError, "#{v.inspect} is not type #{t}" unless v.is_a? t
235
+ true
236
+ end
237
+ else
238
+ def check_type(*_); end
239
+ end
240
+ end
241
+
242
+ class Module
243
+ def implements(mod, runtime_checks: true)
244
+ unless is_a? Class
245
+ raise "Non-Class modules should not implement interfaces"
246
+ end
247
+ instance_variable_set(:@__interface_runtime_check, true) if runtime_checks
248
+ include(mod)
249
+ end
250
+
251
+ def as_interface(iface, runtime_checks: true)
252
+ clone.implements(iface, runtime_checks: runtime_checks)
253
+ end
254
+
255
+ def assert_implements(iface)
256
+ clone.implements(iface, false)
257
+ end
258
+
259
+ def shell_implements(mod)
260
+ instance_variable_set(:@__interface_runtime_check, false)
261
+ instance_variable_set(:@__interface_arity_skip, true)
262
+ include(mod)
263
+ end
264
+ end
265
+
266
+