rubycube 0.2.0

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.
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
+