tins 0.13.2 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/Rakefile +1 -13
  4. data/VERSION +1 -1
  5. data/examples/add_one.png +0 -0
  6. data/examples/add_one.stm +13 -0
  7. data/examples/bb3.png +0 -0
  8. data/examples/bb3.stm +26 -0
  9. data/examples/bb3_19.stm +26 -0
  10. data/examples/concatenate_compare.mtm +31 -0
  11. data/examples/concatenate_compare.png +0 -0
  12. data/examples/concatenate_compare_19.mtm +31 -0
  13. data/examples/length_difference.mtm +17 -0
  14. data/examples/length_difference.png +0 -0
  15. data/examples/length_difference_19.mtm +17 -0
  16. data/examples/let.rb +62 -0
  17. data/examples/mail.rb +73 -0
  18. data/examples/minsky.rb +145 -0
  19. data/examples/multiply.reg +42 -0
  20. data/examples/null_pattern.rb +52 -0
  21. data/examples/ones_difference-mtm.png +0 -0
  22. data/examples/ones_difference-stm.png +0 -0
  23. data/examples/ones_difference.mtm +12 -0
  24. data/examples/ones_difference.stm +25 -0
  25. data/examples/ones_difference_19.mtm +12 -0
  26. data/examples/ones_difference_19.stm +25 -0
  27. data/examples/prefix-equals-suffix-reversed-with-infix.png +0 -0
  28. data/examples/prefix-equals-suffix-reversed-with-infix.stm +38 -0
  29. data/examples/prefix-equals-suffix-reversed-with-infix_19.stm +38 -0
  30. data/examples/recipe.rb +81 -0
  31. data/examples/recipe2.rb +82 -0
  32. data/examples/recipe_common.rb +97 -0
  33. data/examples/subtract.reg +9 -0
  34. data/examples/turing-graph.rb +17 -0
  35. data/examples/turing.rb +310 -0
  36. data/lib/dslkit.rb +2 -0
  37. data/lib/dslkit/polite.rb +1 -0
  38. data/lib/dslkit/rude.rb +1 -0
  39. data/lib/tins.rb +1 -0
  40. data/lib/tins/dslkit.rb +662 -0
  41. data/lib/tins/thread_local.rb +52 -0
  42. data/lib/tins/version.rb +1 -1
  43. data/lib/tins/xt.rb +1 -0
  44. data/lib/tins/xt/dslkit.rb +23 -0
  45. data/tests/concern_test.rb +24 -25
  46. data/tests/dslkit_test.rb +308 -0
  47. data/tests/dynamic_scope_test.rb +31 -0
  48. data/tests/from_module_test.rb +61 -0
  49. data/tests/scope_test.rb +31 -0
  50. data/tests/test_helper.rb +0 -1
  51. data/tins.gemspec +10 -10
  52. metadata +68 -17
@@ -0,0 +1,9 @@
1
+ # vim: set filetype=ruby:
2
+
3
+ # Subtraction A = A - B, A >= 0 and A >= B
4
+ register.A = 10 # register names have to be uppercase
5
+ register.B = 3
6
+
7
+ label(Sub_B) { decrement B, Stop, Sub_A } # labels have to be uppercase as well
8
+ label(Sub_A) { decrement A, Stop, Sub_B }
9
+ label(Stop) { halt }
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ load File.join(File.dirname(__FILE__), 'turing.rb')
4
+ include Turing
5
+
6
+ filename, *tapes = ARGV
7
+ machine_type =
8
+ case ext = File.extname(filename)
9
+ when '.stm'
10
+ SingleTapeMachine
11
+ when '.mtm'
12
+ MultiTapeMachine
13
+ else
14
+ raise "unknown turing machine suffix: #{ext}, use .stm or .mtm"
15
+ end
16
+ tm = machine_type.new(File.read(filename))
17
+ print tm.to_graphviz
@@ -0,0 +1,310 @@
1
+ require 'term/ansicolor'
2
+ require 'tins'
3
+
4
+ module Turing
5
+ class Tape
6
+ def initialize(*initials)
7
+ @left = []
8
+ @head = 'B'
9
+ @right = []
10
+ c = 0
11
+ first = true
12
+ for initial in initials
13
+ if first
14
+ c += 1
15
+ first = false
16
+ else
17
+ @left.push 'B'
18
+ c += 1
19
+ end
20
+ for s in initial.split(//)
21
+ @left.push s
22
+ c += 1
23
+ end
24
+ end
25
+ c.times { left }
26
+ end
27
+
28
+ def read
29
+ @head
30
+ end
31
+
32
+ def write(symbol)
33
+ @head = symbol
34
+ self
35
+ end
36
+
37
+ def left
38
+ @right.push @head
39
+ @head = @left.pop || 'B'
40
+ self
41
+ end
42
+
43
+ def right
44
+ @left.push @head
45
+ @head = @right.pop || 'B'
46
+ self
47
+ end
48
+
49
+ def clear
50
+ @left.clear
51
+ @right.clear
52
+ @head = 'B'
53
+ self
54
+ end
55
+
56
+ def to_s
57
+ "#{@left.join}#{Term::ANSIColor.red(@head)}#{@right.join.reverse}"
58
+ end
59
+
60
+ alias inspect to_s
61
+ end
62
+
63
+ module States
64
+ class State
65
+ attr_accessor :tape
66
+ end
67
+
68
+ class Cond < State
69
+ def initialize(opts = {})
70
+ @if, @then, @else = opts.values_at :if, :then, :else
71
+ end
72
+
73
+ def execute
74
+ tape.read == @if ? @then : @else
75
+ end
76
+
77
+ def to_s
78
+ "if #@if then #@then else #@else"
79
+ end
80
+
81
+ def to_graphviz(stateno, tapeno = nil)
82
+ %{#{stateno} [ shape=diamond label="#{tapeno && "#{tapeno}: "}#@if" ];
83
+ #{stateno} -> #@then [ taillabel="+" ];
84
+ #{stateno} -> #@else [ taillabel="-" ];
85
+ #{stateno} -> #{stateno} [ label="#{stateno}" weight=4.0 color=transparent ];}
86
+ end
87
+ end
88
+
89
+ class Left < State
90
+ def initialize(opts = {})
91
+ @goto = opts[:goto]
92
+ end
93
+
94
+ def execute
95
+ tape.left
96
+ @goto
97
+ end
98
+
99
+ def to_s
100
+ "left, goto #@goto"
101
+ end
102
+
103
+ def to_graphviz(stateno, tapeno = nil)
104
+ %{#{stateno} [ shape=rect label="#{tapeno && "#{tapeno}: "}L" ];
105
+ #{stateno} -> #@goto;
106
+ #{stateno} -> #{stateno} [ label="#{stateno}" weight=4.0 color=transparent ];}
107
+ end
108
+ end
109
+
110
+ class Right < State
111
+ def initialize(opts = {})
112
+ @goto = opts[:goto]
113
+ end
114
+
115
+ def execute
116
+ tape.right
117
+ @goto
118
+ end
119
+
120
+ def to_s
121
+ "right, goto #@goto"
122
+ end
123
+
124
+ def to_graphviz(stateno, tapeno = nil)
125
+ %{#{stateno} [ shape=rect label="#{tapeno && "#{tapeno}: "}R" ];
126
+ #{stateno} -> #@goto;
127
+ #{stateno} -> #{stateno} [ label="#{stateno}" weight=4.0 color=transparent ];}
128
+ end
129
+ end
130
+
131
+ class Write < State
132
+ def initialize(opts = {})
133
+ @symbol, @goto = opts.values_at :symbol, :goto
134
+ end
135
+
136
+ def execute
137
+ tape.write @symbol
138
+ @goto
139
+ end
140
+
141
+ def to_s
142
+ "write #@symbol, goto #@goto"
143
+ end
144
+
145
+ def to_graphviz(stateno, tapeno = nil)
146
+ %{#{stateno} [ shape=rect label="#{tapeno && "#{tapeno}: "}#@symbol" ];
147
+ #{stateno} -> #@goto;
148
+ #{stateno} -> #{stateno} [ label="#{stateno}" weight=4.0 color=transparent ];}
149
+ end
150
+ end
151
+
152
+ class Halt < State
153
+ def initialize(opts = {})
154
+ end
155
+
156
+ def execute
157
+ -1
158
+ end
159
+
160
+ def to_s
161
+ 'halt'
162
+ end
163
+
164
+ def to_graphviz(stateno, tapeno = nil)
165
+ %{#{stateno} [ shape=rect label="HALT" ];
166
+ #{stateno} -> #{stateno} [ label="#{stateno}" weight=4.0 color=transparent ];}
167
+ end
168
+ end
169
+ end
170
+
171
+ class BaseMachine
172
+ def initialize(program = nil, &block)
173
+ @states = []
174
+ if program
175
+ block_given? and raise "use either program source string or a block"
176
+ interpret program
177
+ else
178
+ instance_eval(&block)
179
+ end
180
+ end
181
+
182
+ def step(*tapes)
183
+ @stepping = true
184
+ run(*tapes)
185
+ end
186
+ end
187
+
188
+ class SingleTapeMachine < BaseMachine
189
+ include Tins::Deflect
190
+ include Tins::Interpreter
191
+
192
+ def initialize(program = nil)
193
+ deflector = Deflector.new do |number, id, name, *args|
194
+ opts = Hash === args.last ? args.pop : {}
195
+ state = States.const_get(name.to_s.capitalize).new(opts)
196
+ @states[number] = state
197
+ end
198
+ deflect_start(Integer, :method_missing, deflector)
199
+ super
200
+ ensure
201
+ deflect_stop(Integer, :method_missing) if deflect?(Integer, :method_missing)
202
+ end
203
+
204
+ def run(*tape)
205
+ @tape = Tape.new(*tape)
206
+ @states.each { |s| s and s.tape = @tape }
207
+ goto_state = -1
208
+ @states.any? { |s| goto_state += 1; s }
209
+ begin
210
+ printf "%3u: %s", goto_state, @tape
211
+ @stepping ? STDIN.gets : puts
212
+ goto_state = @states[goto_state].execute
213
+ end until goto_state < 0
214
+ end
215
+
216
+ def to_s
217
+ result = ''
218
+ @states.each_with_index do |state, i|
219
+ result << "%3u. %s\n" % [ i, state ]
220
+ end
221
+ result
222
+ end
223
+
224
+ def to_graphviz
225
+ result = "digraph {\n"
226
+ start_edge = false
227
+ @states.each_with_index do |state, stateno|
228
+ state or next
229
+ unless start_edge
230
+ result << "start [ fontcolor=transparent color=transparent ];"
231
+ result << "start -> #{stateno};"
232
+ start_edge = true
233
+ end
234
+ result << state.to_graphviz(stateno) << "\n"
235
+ end
236
+ result << "}\n"
237
+ end
238
+ end
239
+
240
+ class MultiTapeMachine < BaseMachine
241
+ include Tins::Deflect
242
+ include Tins::Interpreter
243
+
244
+ def initialize(program = nil)
245
+ deflector = Deflector.new do |number, id, name, *args|
246
+ opts = Hash === args.last ? args.pop : {}
247
+ tape, = *args
248
+ state = States.const_get(name.to_s.capitalize).new(opts)
249
+ @states[number] = [ tape, state ]
250
+ end
251
+ deflect_start(Integer, :method_missing, deflector)
252
+ super
253
+ ensure
254
+ deflect_stop(Integer, :method_missing) if deflect?(Integer, :method_missing)
255
+ end
256
+
257
+ def run(*tapes)
258
+ tapes.unshift ''
259
+ @tapes = tapes.map { |tape| Tape.new(tape) }
260
+ goto_state = -1
261
+ @states.any? { |s| goto_state += 1; s }
262
+ begin
263
+ printf "%3u: %s", goto_state, @tapes * ' '
264
+ @stepping ? STDIN.gets : puts
265
+ tape, state = @states[goto_state]
266
+ state.tape = tape ? @tapes[tape] : nil
267
+ goto_state = state.execute
268
+ end until goto_state < 0
269
+ end
270
+
271
+ def to_s
272
+ result = ''
273
+ @states.each_with_index do |(tape, state), i|
274
+ result << "%3u. %1u: %s\n" % [ i, tape, state ]
275
+ end
276
+ result
277
+ end
278
+
279
+ def to_graphviz
280
+ result = "digraph {\n"
281
+ start_edge = false
282
+ @states.each_with_index do |(tapeno,state), stateno|
283
+ state or next
284
+ unless start_edge
285
+ result << "start [ fontcolor=transparent color=transparent ];"
286
+ result << "start -> #{stateno};"
287
+ start_edge = true
288
+ end
289
+ result << state.to_graphviz(stateno, tapeno) << "\n"
290
+ end
291
+ result << "}\n"
292
+ end
293
+ end
294
+ end
295
+
296
+ if $0 == __FILE__ and ARGV.any?
297
+ include Turing
298
+ filename, *tapes = ARGV
299
+ machine_type =
300
+ case ext = File.extname(filename)
301
+ when '.stm'
302
+ SingleTapeMachine
303
+ when '.mtm'
304
+ MultiTapeMachine
305
+ else
306
+ raise "unknown turing machine suffix: #{ext}, use .stm or .mtm"
307
+ end
308
+ tm = machine_type.new(File.read(filename))
309
+ $DEBUG ? tm.step(*tapes) : tm.run(*tapes)
310
+ end
@@ -0,0 +1,2 @@
1
+ require 'tins/alias'
2
+ require 'tins/dslkit/polite'
@@ -0,0 +1 @@
1
+ require 'tins/dslkit'
@@ -0,0 +1 @@
1
+ require 'tins/xt/dslkit'
@@ -47,6 +47,7 @@ module Tins
47
47
  require 'tins/method_description'
48
48
  require 'tins/annotate'
49
49
  require 'tins/token'
50
+ require 'tins/dslkit'
50
51
  if defined? ::Encoding
51
52
  require 'tins/string_byte_order_mark'
52
53
  end
@@ -0,0 +1,662 @@
1
+ require 'tins'
2
+ require 'thread'
3
+ require 'sync'
4
+
5
+ require 'tins/thread_local'
6
+
7
+ module Tins
8
+ # This module contains some handy methods to deal with eigenclasses. Those
9
+ # are also known as virtual classes, singleton classes, metaclasses, plus all
10
+ # the other names Matz doesn't like enough to actually accept one of the
11
+ # names.
12
+ #
13
+ # The module can be included into other modules/classes to make the methods available.
14
+ module Eigenclass
15
+ if Object.respond_to?(:singleton_class)
16
+ alias eigenclass singleton_class
17
+ else
18
+ # Returns the eigenclass of this object.
19
+ def eigenclass
20
+ class << self; self; end
21
+ end
22
+ end
23
+
24
+ # Evaluates the _block_ in context of the eigenclass of this object.
25
+ def eigenclass_eval(&block)
26
+ eigenclass.instance_eval(&block)
27
+ end
28
+ end
29
+
30
+ module ClassMethod
31
+ include Eigenclass
32
+
33
+ # Define a class method named _name_ using _block_. To be able to take
34
+ # blocks as arguments in the given _block_ Ruby 1.9 is required.
35
+ def class_define_method(name, &block)
36
+ eigenclass_eval { define_method(name, &block) }
37
+ end
38
+
39
+ # Define reader and writer attribute methods for all <i>*ids</i>.
40
+ def class_attr_accessor(*ids)
41
+ eigenclass_eval { attr_accessor(*ids) }
42
+ end
43
+
44
+ # Define reader attribute methods for all <i>*ids</i>.
45
+ def class_attr_reader(*ids)
46
+ eigenclass_eval { attr_reader(*ids) }
47
+ end
48
+
49
+ # Define writer attribute methods for all <i>*ids</i>.
50
+ def class_attr_writer(*ids)
51
+ eigenclass_eval { attr_writer(*ids) }
52
+ end
53
+
54
+ # I boycott attr!
55
+ end
56
+
57
+ module ThreadGlobal
58
+ # Define a thread global variable named _name_ in this module/class. If the
59
+ # value _value_ is given, it is used to initialize the variable.
60
+ def thread_global(name, default_value = nil)
61
+ is_a?(Module) or raise TypeError, "receiver has to be a Module"
62
+
63
+ name = name.to_s
64
+ var_name = "@__#{name}_#{__id__.abs}__"
65
+
66
+ lock = Mutex.new
67
+ modul = self
68
+
69
+ define_method(name) do
70
+ lock.synchronize { modul.instance_variable_get var_name }
71
+ end
72
+
73
+ define_method(name + "=") do |value|
74
+ lock.synchronize { modul.instance_variable_set var_name, value }
75
+ end
76
+
77
+ modul.instance_variable_set var_name, default_value if default_value
78
+ self
79
+ end
80
+
81
+ # Define a thread global variable for the current instance with name
82
+ # _name_. If the value _value_ is given, it is used to initialize the
83
+ # variable.
84
+ def instance_thread_global(name, value = nil)
85
+ sc = class << self
86
+ extend Tins::ThreadGlobal
87
+ self
88
+ end
89
+ sc.thread_global name, value
90
+ self
91
+ end
92
+ end
93
+
94
+ module InstanceExec
95
+ unless (Object.instance_method(:instance_exec) rescue nil)
96
+ class << self
97
+ attr_accessor :pool
98
+ attr_accessor :count
99
+ end
100
+ self.count = 0
101
+ self.pool = []
102
+
103
+ # This is a pure ruby implementation of Ruby 1.9's instance_exec method. It
104
+ # executes _block_ in the context of this object while parsing <i>*args</i> into
105
+ # the block.
106
+ def instance_exec(*args, &block)
107
+ instance = self
108
+ id = instance_exec_fetch_symbol
109
+ InstanceExec.module_eval do
110
+ begin
111
+ define_method id, block
112
+ instance.__send__ id, *args
113
+ ensure
114
+ remove_method id if method_defined?(id)
115
+ end
116
+ end
117
+ ensure
118
+ InstanceExec.pool << id
119
+ end
120
+
121
+ private
122
+
123
+ @@mutex = Mutex.new
124
+
125
+ # Fetch a symbol from a pool in thread save way. If no more symbols are
126
+ # available create a new one, that will be pushed into the pool later.
127
+ def instance_exec_fetch_symbol
128
+ @@mutex.synchronize do
129
+ if InstanceExec.pool.empty?
130
+ InstanceExec.count += 1
131
+ symbol = :"__instance_exec_#{InstanceExec.count}__"
132
+ else
133
+ symbol = InstanceExec.pool.shift
134
+ end
135
+ return symbol
136
+ end
137
+ end
138
+ end
139
+ end
140
+
141
+ module Interpreter
142
+ include InstanceExec
143
+
144
+ # Interpret the string _source_ as a body of a block, while passing
145
+ # <i>*args</i> into the block.
146
+ #
147
+ # A small example explains how the method is supposed to be used and how
148
+ # the <i>*args</i> can be fetched:
149
+ #
150
+ # class A
151
+ # include Tins::Interpreter
152
+ # def c
153
+ # 3
154
+ # end
155
+ # end
156
+ #
157
+ # A.new.interpret('|a,b| a + b + c', 1, 2) # => 6
158
+ #
159
+ # To use a specified binding see #interpret_with_binding.
160
+ def interpret(source, *args)
161
+ interpret_with_binding(source, binding, *args)
162
+ end
163
+
164
+ # Interpret the string _source_ as a body of a block, while passing
165
+ # <i>*args</i> into the block and using _my_binding_ for evaluation.
166
+ #
167
+ # A small example:
168
+ #
169
+ # class A
170
+ # include Tins::Interpreter
171
+ # def c
172
+ # 3
173
+ # end
174
+ # def foo
175
+ # b = 2
176
+ # interpret_with_binding('|a| a + b + c', binding, 1) # => 6
177
+ # end
178
+ # end
179
+ # A.new.foo # => 6
180
+ #
181
+ # See also #interpret.
182
+ def interpret_with_binding(source, my_binding, *args)
183
+ path = '(interpret)'
184
+ if source.respond_to? :to_io
185
+ path = source.path if source.respond_to? :path
186
+ source = source.to_io.read
187
+ end
188
+ block = lambda { |*a| eval("lambda { #{source} }", my_binding, path).call(*a) }
189
+ instance_exec(*args, &block)
190
+ end
191
+ end
192
+
193
+ # This module contains the _constant_ method. For small example of its usage
194
+ # see the documentation of the DSLAccessor module.
195
+ module Constant
196
+ # Create a constant named _name_, that refers to value _value_. _value is
197
+ # frozen, if this is possible. If you want to modify/exchange a value use
198
+ # DSLAccessor#dsl_reader/DSLAccessor#dsl_accessor instead.
199
+ def constant(name, value = name)
200
+ value = value.freeze rescue value
201
+ define_method(name) { value }
202
+ end
203
+ end
204
+
205
+ # The DSLAccessor module contains some methods, that can be used to make
206
+ # simple accessors for a DSL.
207
+ #
208
+ #
209
+ # class CoffeeMaker
210
+ # extend Tins::Constant
211
+ #
212
+ # constant :on
213
+ # constant :off
214
+ #
215
+ # extend Tins::DSLAccessor
216
+ #
217
+ # dsl_accessor(:state) { off } # Note: the off constant from above is used
218
+ #
219
+ # dsl_accessor :allowed_states, :on, :off
220
+ #
221
+ # def process
222
+ # allowed_states.include?(state) or fail "Explode!!!"
223
+ # if state == on
224
+ # puts "Make coffee."
225
+ # else
226
+ # puts "Idle..."
227
+ # end
228
+ # end
229
+ # end
230
+ #
231
+ # cm = CoffeeMaker.new
232
+ # cm.instance_eval do
233
+ # state # => :off
234
+ # state on
235
+ # state # => :on
236
+ # process # => outputs "Make coffee."
237
+ # end
238
+ #
239
+ # Note that Tins::SymbolMaker is an alternative for Tins::Constant in
240
+ # this example. On the other hand SymbolMaker can make debugging more
241
+ # difficult.
242
+ module DSLAccessor
243
+ # This method creates a dsl accessor named _name_. If nothing else is given
244
+ # as argument it defaults to nil. If <i>*default</i> is given as a single
245
+ # value it is used as a default value, if more than one value is given the
246
+ # _default_ array is used as the default value. If no default value but a
247
+ # block _block_ is given as an argument, the block is executed everytime
248
+ # the accessor is read <b>in the context of the current instance</b>.
249
+ #
250
+ # After setting up the accessor, the set or default value can be retrieved
251
+ # by calling the method +name+. To set a value one can call <code>name
252
+ # :foo</code> to set the attribute value to <code>:foo</code> or
253
+ # <code>name(:foo, :bar)</code> to set it to <code>[ :foo, :bar ]</code>.
254
+ def dsl_accessor(name, *default, &block)
255
+ variable = "@#{name}"
256
+ define_method(name) do |*args|
257
+ if args.empty?
258
+ result = instance_variable_get(variable)
259
+ if result.nil?
260
+ result = if default.empty?
261
+ block && instance_eval(&block)
262
+ elsif default.size == 1
263
+ default.first
264
+ else
265
+ default
266
+ end
267
+ instance_variable_set(variable, result)
268
+ result
269
+ else
270
+ result
271
+ end
272
+ else
273
+ instance_variable_set(variable, args.size == 1 ? args.first : args)
274
+ end
275
+ end
276
+ end
277
+
278
+ # This method creates a dsl reader accessor, that behaves exactly like a
279
+ # #dsl_accessor but can only be read not set.
280
+ def dsl_reader(name, *default, &block)
281
+ variable = "@#{name}"
282
+ define_method(name) do |*args|
283
+ if args.empty?
284
+ result = instance_variable_get(variable)
285
+ if result.nil?
286
+ if default.empty?
287
+ block && instance_eval(&block)
288
+ elsif default.size == 1
289
+ default.first
290
+ else
291
+ default
292
+ end
293
+ else
294
+ result
295
+ end
296
+ else
297
+ raise ArgumentError, "wrong number of arguments (#{args.size} for 0)"
298
+ end
299
+ end
300
+ end
301
+ end
302
+
303
+ # This module can be included in another module/class. It generates a symbol
304
+ # for every missing method that was called in the context of this
305
+ # module/class.
306
+ module SymbolMaker
307
+ # Returns a symbol (_id_) for every missing method named _id_.
308
+ def method_missing(id, *args)
309
+ if args.empty?
310
+ id
311
+ else
312
+ super
313
+ end
314
+ end
315
+ end
316
+
317
+ # This module can be used to extend another module/class. It generates
318
+ # symbols for every missing constant under the namespace of this
319
+ # module/class.
320
+ module ConstantMaker
321
+ # Returns a symbol (_id_) for every missing constant named _id_.
322
+ def const_missing(id)
323
+ id
324
+ end
325
+ end
326
+
327
+ module BlankSlate
328
+ # Creates an anonymous blank slate class, that only responds to the methods
329
+ # <i>*ids</i>. ids can be Symbols, Strings, and Regexps that have to match
330
+ # the method name with #===.
331
+ def self.with(*ids)
332
+ opts = Hash === ids.last ? ids.pop : {}
333
+ ids = ids.map { |id| Regexp === id ? id : id.to_s }
334
+ klass = opts[:superclass] ? Class.new(opts[:superclass]) : Class.new
335
+ klass.instance_eval do
336
+ instance_methods.each do |m|
337
+ m = m.to_s
338
+ undef_method m unless m =~ /^(__|object_id)/ or ids.any? { |i| i === m }
339
+ end
340
+ end
341
+ klass
342
+ end
343
+ end
344
+
345
+ # See examples/recipe.rb and examples/recipe2.rb how this works at the
346
+ # moment.
347
+ module Deflect
348
+ # The basic Deflect exception
349
+ class DeflectError < StandardError; end
350
+
351
+ class << self
352
+ extend Tins::ThreadLocal
353
+
354
+ # A thread local variable, that holds a DeflectorCollection instance for
355
+ # the current thread.
356
+ thread_local :deflecting
357
+ end
358
+
359
+ # A deflector is called with a _class_, a method _id_, and its
360
+ # <i>*args</i>.
361
+ class Deflector < Proc; end
362
+
363
+ # This class implements a collection of deflectors, to make them available
364
+ # by emulating Ruby's message dispatch.
365
+ class DeflectorCollection
366
+ def initialize
367
+ @classes = {}
368
+ end
369
+
370
+ # Add a new deflector _deflector_ for class _klass_ and method name _id_,
371
+ # and return self.
372
+ #
373
+ def add(klass, id, deflector)
374
+ k = @classes[klass]
375
+ k = @classes[klass] = {} unless k
376
+ k[id.to_s] = deflector
377
+ self
378
+ end
379
+
380
+ # Return true if messages are deflected for class _klass_ and method name
381
+ # _id_, otherwise return false.
382
+ def member?(klass, id)
383
+ !!(k = @classes[klass] and k.key?(id.to_s))
384
+ end
385
+
386
+ # Delete the deflecotor class _klass_ and method name _id_. Returns the
387
+ # deflector if any was found, otherwise returns true.
388
+ def delete(klass, id)
389
+ if k = @classes[klass]
390
+ d = k.delete id.to_s
391
+ @classes.delete klass if k.empty?
392
+ d
393
+ end
394
+ end
395
+
396
+ # Try to find a deflector for class _klass_ and method _id_ and return
397
+ # it. If none was found, return nil instead.
398
+ def find(klass, id)
399
+ klass.ancestors.find do |k|
400
+ if d = @classes[k] and d = d[id.to_s]
401
+ return d
402
+ end
403
+ end
404
+ end
405
+ end
406
+
407
+ @@sync = Sync.new
408
+
409
+ # Start deflecting method calls named _id_ to the _from_ class using the
410
+ # Deflector instance deflector.
411
+ def deflect_start(from, id, deflector)
412
+ @@sync.synchronize do
413
+ Deflect.deflecting ||= DeflectorCollection.new
414
+ Deflect.deflecting.member?(from, id) and
415
+ raise DeflectError, "#{from}##{id} is already deflected"
416
+ Deflect.deflecting.add(from, id, deflector)
417
+ from.class_eval do
418
+ define_method(id) do |*args|
419
+ if Deflect.deflecting and d = Deflect.deflecting.find(self.class, id)
420
+ d.call(self, id, *args)
421
+ else
422
+ super(*args)
423
+ end
424
+ end
425
+ end
426
+ end
427
+ end
428
+
429
+ # Return true if method _id_ is deflected from class _from_, otherwise
430
+ # return false.
431
+ def self.deflect?(from, id)
432
+ Deflect.deflecting && Deflect.deflecting.member?(from, id)
433
+ end
434
+
435
+ # Return true if method _id_ is deflected from class _from_, otherwise
436
+ # return false.
437
+ def deflect?(from, id)
438
+ Deflect.deflect?(from, id)
439
+ end
440
+
441
+ # Start deflecting method calls named _id_ to the _from_ class using the
442
+ # Deflector instance deflector. After that yield to the given block and
443
+ # stop deflecting again.
444
+ def deflect(from, id, deflector)
445
+ @@sync.synchronize do
446
+ begin
447
+ deflect_start(from, id, deflector)
448
+ yield
449
+ ensure
450
+ deflect_stop(from, id)
451
+ end
452
+ end
453
+ end
454
+
455
+ # Stop deflection method calls named _id_ to class _from_.
456
+ def deflect_stop(from, id)
457
+ @@sync.synchronize do
458
+ Deflect.deflecting.delete(from, id) or
459
+ raise DeflectError, "#{from}##{id} is not deflected from"
460
+ from.instance_eval { remove_method id }
461
+ end
462
+ end
463
+ end
464
+
465
+ # This module can be included into modules/classes to make the delegate
466
+ # method available.
467
+ module Delegate
468
+ # A method to easily delegate methods to an object, stored in an
469
+ # instance variable or returned by a method call.
470
+ #
471
+ # It's used like this:
472
+ # class A
473
+ # delegate :method_here, :@obj, :method_there
474
+ # end
475
+ # or:
476
+ # class A
477
+ # delegate :method_here, :method_call, :method_there
478
+ # end
479
+ #
480
+ # _other_method_name_ defaults to method_name, if it wasn't given.
481
+ def delegate(method_name, obj, other_method_name = method_name)
482
+ raise ArgumentError, "obj wasn't defined" unless obj
483
+ =begin
484
+ 1.9 only:
485
+ define_method(method_name) do |*args, &block|
486
+ instance_variable_get(obj).__send__(other_method_name, *args, &block)
487
+ end
488
+ =end
489
+ obj = obj.to_s
490
+ if obj[0] == ?@
491
+ class_eval <<-EOS
492
+ def #{method_name}(*args, &block)
493
+ instance_variable_get('#{obj}').__send__(
494
+ '#{other_method_name}', *args, &block)
495
+ end
496
+ EOS
497
+ else
498
+ class_eval <<-EOS
499
+ def #{method_name}(*args, &block)
500
+ __send__('#{obj}').__send__(
501
+ '#{other_method_name}', *args, &block)
502
+ end
503
+ EOS
504
+ end
505
+ end
506
+ end
507
+
508
+ # This module includes the block_self module_function.
509
+ module BlockSelf
510
+ module_function
511
+
512
+ # This method returns the receiver _self_ of the context in which _block_
513
+ # was created.
514
+ def block_self(&block)
515
+ eval 'self', block.__send__(:binding)
516
+ end
517
+ end
518
+
519
+ # This module contains a configurable method missing delegator and can be
520
+ # mixed into a module/class.
521
+ module MethodMissingDelegator
522
+
523
+ # Including this module in your classes makes an _initialize_ method
524
+ # available, whose first argument is used as method_missing_delegator
525
+ # attribute. If a superior _initialize_ method was defined it is called
526
+ # with all arguments but the first.
527
+ module DelegatorModule
528
+ include Tins::MethodMissingDelegator
529
+
530
+ def initialize(delegator, *a, &b)
531
+ self.method_missing_delegator = delegator
532
+ super(*a, &b) if defined? super
533
+ end
534
+ end
535
+
536
+ # This class includes DelegatorModule and can be used as a superclass
537
+ # instead of including DelegatorModule.
538
+ class DelegatorClass
539
+ include DelegatorModule
540
+ end
541
+
542
+ # This object will be the receiver of all missing method calls, if it has a
543
+ # value other than nil.
544
+ attr_accessor :method_missing_delegator
545
+
546
+ # Delegates all missing method calls to _method_missing_delegator_ if this
547
+ # attribute has been set. Otherwise it will call super.
548
+ def method_missing(id, *a, &b)
549
+ unless method_missing_delegator.nil?
550
+ method_missing_delegator.__send__(id, *a, &b)
551
+ else
552
+ super
553
+ end
554
+ end
555
+ end
556
+
557
+ module ParameterizedModule
558
+ # Pass _args_ and _block_ to configure the module and then return it after
559
+ # calling the parameterize method has been called with these arguments. The
560
+ # _parameterize_ method should return a configured module.
561
+ def parameterize_for(*args, &block)
562
+ respond_to?(:parameterize) ? parameterize(*args, &block) : self
563
+ end
564
+ end
565
+
566
+ module FromModule
567
+ include ParameterizedModule
568
+
569
+ alias from parameterize_for
570
+
571
+ def parameterize(opts = {})
572
+ modul = opts[:module] or raise ArgumentError, 'option :module is required'
573
+ import_methods = Array(opts[:methods])
574
+ result = modul.dup
575
+ remove_methods = modul.instance_methods.map(&:to_sym) - import_methods.map(&:to_sym)
576
+ remove_methods.each do |m|
577
+ begin
578
+ result.__send__ :remove_method, m
579
+ rescue NameError
580
+ end
581
+ end
582
+ result
583
+ end
584
+ end
585
+
586
+ module Scope
587
+ def scope_push(scope_frame, name = :default)
588
+ scope_get(name).push scope_frame
589
+ self
590
+ end
591
+
592
+ def scope_pop(name = :default)
593
+ scope_get(name).pop
594
+ scope_get(name).empty? and Thread.current[name] = nil
595
+ self
596
+ end
597
+
598
+ def scope_top(name = :default)
599
+ scope_get(name).last
600
+ end
601
+
602
+ def scope_reverse(name = :default, &block)
603
+ scope_get(name).reverse_each(&block)
604
+ end
605
+
606
+ def scope_block(scope_frame, name = :default)
607
+ scope_push(scope_frame, name)
608
+ yield
609
+ self
610
+ ensure
611
+ scope_pop(name)
612
+ end
613
+
614
+ def scope_get(name = :default)
615
+ Thread.current[name] ||= []
616
+ end
617
+
618
+ def scope(name = :default)
619
+ scope_get(name).dup
620
+ end
621
+ end
622
+
623
+ module DynamicScope
624
+ class Context < Hash
625
+ def [](name)
626
+ super name.to_sym
627
+ end
628
+
629
+ def []=(name, value)
630
+ super name.to_sym, value
631
+ end
632
+ end
633
+
634
+ include Scope
635
+
636
+ attr_accessor :dynamic_scope_name
637
+
638
+ def dynamic_defined?(id)
639
+ self.dynamic_scope_name ||= :variables
640
+ scope_reverse(dynamic_scope_name) { |c| c.key?(id) and return true }
641
+ false
642
+ end
643
+
644
+ def dynamic_scope(&block)
645
+ self.dynamic_scope_name ||= :variables
646
+ scope_block(Context.new, dynamic_scope_name, &block)
647
+ end
648
+
649
+ def method_missing(id, *args)
650
+ self.dynamic_scope_name ||= :variables
651
+ if args.empty? and scope_reverse(dynamic_scope_name) { |c| c.key?(id) and return c[id] }
652
+ super
653
+ elsif args.size == 1 and id.to_s =~ /(.*?)=\Z/
654
+ c = scope_top(dynamic_scope_name) or super
655
+ c[$1] = args.first
656
+ else
657
+ super
658
+ end
659
+ end
660
+ end
661
+ end
662
+ DSLKit = Tins