tins 0.13.2 → 1.0.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 (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