tins 0.13.2 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/Rakefile +1 -13
- data/VERSION +1 -1
- data/examples/add_one.png +0 -0
- data/examples/add_one.stm +13 -0
- data/examples/bb3.png +0 -0
- data/examples/bb3.stm +26 -0
- data/examples/bb3_19.stm +26 -0
- data/examples/concatenate_compare.mtm +31 -0
- data/examples/concatenate_compare.png +0 -0
- data/examples/concatenate_compare_19.mtm +31 -0
- data/examples/length_difference.mtm +17 -0
- data/examples/length_difference.png +0 -0
- data/examples/length_difference_19.mtm +17 -0
- data/examples/let.rb +62 -0
- data/examples/mail.rb +73 -0
- data/examples/minsky.rb +145 -0
- data/examples/multiply.reg +42 -0
- data/examples/null_pattern.rb +52 -0
- data/examples/ones_difference-mtm.png +0 -0
- data/examples/ones_difference-stm.png +0 -0
- data/examples/ones_difference.mtm +12 -0
- data/examples/ones_difference.stm +25 -0
- data/examples/ones_difference_19.mtm +12 -0
- data/examples/ones_difference_19.stm +25 -0
- data/examples/prefix-equals-suffix-reversed-with-infix.png +0 -0
- data/examples/prefix-equals-suffix-reversed-with-infix.stm +38 -0
- data/examples/prefix-equals-suffix-reversed-with-infix_19.stm +38 -0
- data/examples/recipe.rb +81 -0
- data/examples/recipe2.rb +82 -0
- data/examples/recipe_common.rb +97 -0
- data/examples/subtract.reg +9 -0
- data/examples/turing-graph.rb +17 -0
- data/examples/turing.rb +310 -0
- data/lib/dslkit.rb +2 -0
- data/lib/dslkit/polite.rb +1 -0
- data/lib/dslkit/rude.rb +1 -0
- data/lib/tins.rb +1 -0
- data/lib/tins/dslkit.rb +662 -0
- data/lib/tins/thread_local.rb +52 -0
- data/lib/tins/version.rb +1 -1
- data/lib/tins/xt.rb +1 -0
- data/lib/tins/xt/dslkit.rb +23 -0
- data/tests/concern_test.rb +24 -25
- data/tests/dslkit_test.rb +308 -0
- data/tests/dynamic_scope_test.rb +31 -0
- data/tests/from_module_test.rb +61 -0
- data/tests/scope_test.rb +31 -0
- data/tests/test_helper.rb +0 -1
- data/tins.gemspec +10 -10
- metadata +68 -17
@@ -0,0 +1,42 @@
|
|
1
|
+
# vim: set filetype=ruby:
|
2
|
+
|
3
|
+
# Multiplication: C = A * B, A >= 0 and B >= 0
|
4
|
+
register.A = 3
|
5
|
+
register.B = 7
|
6
|
+
|
7
|
+
# Save A in T
|
8
|
+
label(SaveA) { decrement A, MoveS1, IncT }
|
9
|
+
label(IncT) { increment T, IncS1 }
|
10
|
+
label(IncS1) { increment S, SaveA }
|
11
|
+
|
12
|
+
label(MoveS1) { decrement S, SaveB, IncA1 }
|
13
|
+
label(IncA1) { increment A, MoveS1 }
|
14
|
+
|
15
|
+
# Save B in U
|
16
|
+
label(SaveB) { decrement B, MoveS2, IncU }
|
17
|
+
label(IncU) { increment U, IncS2 }
|
18
|
+
label(IncS2) { increment S, SaveB }
|
19
|
+
|
20
|
+
label(MoveS2) { decrement S, Mul, IncB1 }
|
21
|
+
label(IncB1) { increment B, MoveS2 }
|
22
|
+
|
23
|
+
# Multiply A * B
|
24
|
+
label(Mul) { decrement A, MoveT, DecB }
|
25
|
+
|
26
|
+
# Add B to C
|
27
|
+
label(DecB) { decrement B, MoveU, IncC }
|
28
|
+
label(IncC) { increment C, DecB }
|
29
|
+
|
30
|
+
# Move U back to B
|
31
|
+
label(MoveU) { decrement U, SaveB, IncB2 }
|
32
|
+
label(IncB2) { increment B, MoveU }
|
33
|
+
|
34
|
+
# Move T back to A
|
35
|
+
label(MoveT) { decrement T, ClearU, IncA2 }
|
36
|
+
label(IncA2) { increment A, MoveT }
|
37
|
+
|
38
|
+
# Clear U
|
39
|
+
label(ClearU) { decrement U, Stop, ClearU }
|
40
|
+
|
41
|
+
# Stop
|
42
|
+
label(Stop) { halt }
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'tins'
|
2
|
+
$:.unshift 'examples'
|
3
|
+
|
4
|
+
include Tins::Deflect
|
5
|
+
|
6
|
+
puts "Example 1"
|
7
|
+
deflect(NilClass, :method_missing, Deflector.new { nil }) do
|
8
|
+
begin
|
9
|
+
p "foo".bar.baz
|
10
|
+
rescue NoMethodError
|
11
|
+
p "caught 1"
|
12
|
+
end
|
13
|
+
p nil.bar.baz
|
14
|
+
t = Thread.new do
|
15
|
+
begin
|
16
|
+
p nil.bar.baz
|
17
|
+
rescue NoMethodError
|
18
|
+
p "caught 2"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
t.join if t.alive?
|
22
|
+
p nil.bar.baz
|
23
|
+
end
|
24
|
+
begin
|
25
|
+
p nil.bar.baz
|
26
|
+
rescue NoMethodError
|
27
|
+
p "caught 3"
|
28
|
+
end
|
29
|
+
|
30
|
+
puts "-" * 70, "Example 2"
|
31
|
+
deflect_start(NilClass, :method_missing, Deflector.new { nil })
|
32
|
+
begin
|
33
|
+
p "foo".bar.baz
|
34
|
+
rescue NoMethodError
|
35
|
+
p "caught 1"
|
36
|
+
end
|
37
|
+
p nil.bar.baz
|
38
|
+
t = Thread.new do
|
39
|
+
begin
|
40
|
+
p nil.bar.baz
|
41
|
+
rescue NoMethodError
|
42
|
+
p "caught 2"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
t.join if t.alive?
|
46
|
+
p nil.bar.baz
|
47
|
+
deflect_stop(NilClass, :method_missing)
|
48
|
+
begin
|
49
|
+
p nil.bar.baz
|
50
|
+
rescue NoMethodError
|
51
|
+
p "caught 3"
|
52
|
+
end
|
Binary file
|
Binary file
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# vim: ts=2 sw=2 et ft=ruby
|
2
|
+
# Call with two strings of ones on the tape:
|
3
|
+
# $ turing.rb ones_difference.mtm 1111 11
|
4
|
+
|
5
|
+
0. right 1, :goto => 1
|
6
|
+
1. right 2, :goto => 2
|
7
|
+
2. cond 1, :if => '1', :then => 3, :else => 4
|
8
|
+
3. cond 2, :if => '1', :then => 0, :else => 5
|
9
|
+
4. cond 2, :if => '1', :then => 5, :else => 7
|
10
|
+
5. write 0, :symbol => '1', :goto => 6
|
11
|
+
6. left 0, :goto => 0
|
12
|
+
7. halt
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# vim: ts=2 sw=2 et ft=ruby
|
2
|
+
# Call with two strings of ones on the tape:
|
3
|
+
# $ turing.rb ones_difference.stm 1111 11
|
4
|
+
|
5
|
+
1. right :goto => 2
|
6
|
+
2. cond :if => '1', :then => 1, :else => 3
|
7
|
+
3. right :goto => 4
|
8
|
+
4. cond :if => '1', :then => 5, :else => 17
|
9
|
+
5. right :goto => 6
|
10
|
+
6. cond :if => '1', :then => 5, :else => 7
|
11
|
+
7. left :goto => 8
|
12
|
+
8. write :symbol => 'B', :goto => 9
|
13
|
+
9. left :goto => 10
|
14
|
+
10. cond :if => '1', :then => 9, :else => 11
|
15
|
+
11. left :goto => 12
|
16
|
+
12. cond :if => '1', :then => 11, :else => 13
|
17
|
+
13. right :goto => 14
|
18
|
+
14. cond :if => '1', :then => 15, :else => 16
|
19
|
+
15. write :symbol => 'B', :goto => 1
|
20
|
+
16. write :symbol => '1', :goto => 18
|
21
|
+
17. left :goto => 20
|
22
|
+
18. right :goto => 19
|
23
|
+
19. cond :if => '1', :then => 18, :else => 21
|
24
|
+
20. halt
|
25
|
+
21. halt
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# vim: ts=2 sw=2 et ft=ruby
|
2
|
+
# Call with two strings of ones on the tape:
|
3
|
+
# $ turing.rb ones_difference.mtm 1111 11
|
4
|
+
|
5
|
+
0. right 1, goto: 1
|
6
|
+
1. right 2, goto: 2
|
7
|
+
2. cond 1, if: '1', then: 3, else: 4
|
8
|
+
3. cond 2, if: '1', then: 0, else: 5
|
9
|
+
4. cond 2, if: '1', then: 5, else: 7
|
10
|
+
5. write 0, symbol: '1', goto: 6
|
11
|
+
6. left 0, goto: 0
|
12
|
+
7. halt
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# vim: ts=2 sw=2 et ft=ruby
|
2
|
+
# Call with two strings of ones on the tape:
|
3
|
+
# $ turing.rb ones_difference.stm 1111 11
|
4
|
+
|
5
|
+
1. right goto: 2
|
6
|
+
2. cond if: '1', then: 1, else: 3
|
7
|
+
3. right goto: 4
|
8
|
+
4. cond if: '1', then: 5, else: 17
|
9
|
+
5. right goto: 6
|
10
|
+
6. cond if: '1', then: 5, else: 7
|
11
|
+
7. left goto: 8
|
12
|
+
8. write symbol: 'B', goto: 9
|
13
|
+
9. left goto: 10
|
14
|
+
10. cond if: '1', then: 9, else: 11
|
15
|
+
11. left goto: 12
|
16
|
+
12. cond if: '1', then: 11, else: 13
|
17
|
+
13. right goto: 14
|
18
|
+
14. cond if: '1', then: 15, else: 16
|
19
|
+
15. write symbol: 'B', goto: 1
|
20
|
+
16. write symbol: '1', goto: 18
|
21
|
+
17. left goto: 20
|
22
|
+
18. right goto: 19
|
23
|
+
19. cond if: '1', then: 18, else: 21
|
24
|
+
20. halt
|
25
|
+
21. halt
|
Binary file
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# vim: ts=2 sw=2 et ft=ruby
|
2
|
+
# Call with a binary number on the tape:
|
3
|
+
# $ turing.rb prefix-equals-suffix-reversed-with-infix.stm 100101001
|
4
|
+
|
5
|
+
0. right :goto => 1
|
6
|
+
1. cond :if => '0', :then => 2, :else => 13
|
7
|
+
2. write :symbol => 'O', :goto => 3
|
8
|
+
3. right :goto => 4
|
9
|
+
4. cond :if => 'B', :then => 21, :else => 5
|
10
|
+
5. right :goto => 6
|
11
|
+
6. cond :if => 'B', :then => 7, :else => 5
|
12
|
+
7. left :goto => 8
|
13
|
+
8. cond :if => '0', :then => 9, :else => 21
|
14
|
+
9. write :symbol => 'B', :goto => 10
|
15
|
+
10. left :goto => 11
|
16
|
+
11. cond :if => '0', :then => 10, :else => 12
|
17
|
+
12. cond :if => '1', :then => 10, :else => 0
|
18
|
+
13. cond :if => '1', :then => 14, :else => 31
|
19
|
+
14. write :symbol => 'I', :goto => 15
|
20
|
+
15. right :goto => 16
|
21
|
+
16. cond :if => 'B', :then => 21, :else => 17
|
22
|
+
17. right :goto => 18
|
23
|
+
18. cond :if => 'B', :then => 19, :else => 17
|
24
|
+
19. left :goto => 20
|
25
|
+
20. cond :if => '1', :then => 9, :else => 21
|
26
|
+
21. write :symbol => 'B', :goto => 22
|
27
|
+
22. left :goto => 23
|
28
|
+
23. cond :if => '0', :then => 21, :else => 24
|
29
|
+
24. cond :if => '1', :then => 21, :else => 25
|
30
|
+
25. write :symbol => 'B', :goto => 26
|
31
|
+
26. left :goto => 27
|
32
|
+
27. cond :if => 'O', :then => 29, :else => 28
|
33
|
+
28. cond :if => 'I', :then => 30, :else => 32
|
34
|
+
29. write :symbol => '0', :goto => 26
|
35
|
+
30. write :symbol => '1', :goto => 26
|
36
|
+
31. halt
|
37
|
+
32. right :goto => 33
|
38
|
+
33. cond :if => 'B', :then => 31, :else => 32
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# vim: ts=2 sw=2 et ft=ruby
|
2
|
+
# Call with a binary number on the tape:
|
3
|
+
# $ turing.rb prefix-equals-suffix-reversed-with-infix.stm 100101001
|
4
|
+
|
5
|
+
0. right goto: 1
|
6
|
+
1. cond if: '0', then: 2, else: 13
|
7
|
+
2. write symbol: 'O', goto: 3
|
8
|
+
3. right goto: 4
|
9
|
+
4. cond if: 'B', then: 21, else: 5
|
10
|
+
5. right goto: 6
|
11
|
+
6. cond if: 'B', then: 7, else: 5
|
12
|
+
7. left goto: 8
|
13
|
+
8. cond if: '0', then: 9, else: 21
|
14
|
+
9. write symbol: 'B', goto: 10
|
15
|
+
10. left goto: 11
|
16
|
+
11. cond if: '0', then: 10, else: 12
|
17
|
+
12. cond if: '1', then: 10, else: 0
|
18
|
+
13. cond if: '1', then: 14, else: 31
|
19
|
+
14. write symbol: 'I', goto: 15
|
20
|
+
15. right goto: 16
|
21
|
+
16. cond if: 'B', then: 21, else: 17
|
22
|
+
17. right goto: 18
|
23
|
+
18. cond if: 'B', then: 19, else: 17
|
24
|
+
19. left goto: 20
|
25
|
+
20. cond if: '1', then: 9, else: 21
|
26
|
+
21. write symbol: 'B', goto: 22
|
27
|
+
22. left goto: 23
|
28
|
+
23. cond if: '0', then: 21, else: 24
|
29
|
+
24. cond if: '1', then: 21, else: 25
|
30
|
+
25. write symbol: 'B', goto: 26
|
31
|
+
26. left goto: 27
|
32
|
+
27. cond if: 'O', then: 29, else: 28
|
33
|
+
28. cond if: 'I', then: 30, else: 32
|
34
|
+
29. write symbol: '0', goto: 26
|
35
|
+
30. write symbol: '1', goto: 26
|
36
|
+
31. halt
|
37
|
+
32. right goto: 33
|
38
|
+
33. cond if: 'B', then: 31, else: 32
|
data/examples/recipe.rb
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'tins/xt'
|
4
|
+
$:.unshift 'examples'
|
5
|
+
require 'recipe_common'
|
6
|
+
|
7
|
+
class Cup < Unit; end
|
8
|
+
class Teaspoon < Unit; end
|
9
|
+
class Tablespoon < Unit; end
|
10
|
+
|
11
|
+
class Flour < Ingredient; end
|
12
|
+
class Bakingpowder < Ingredient; end
|
13
|
+
class Salt < Ingredient; end
|
14
|
+
class Egg < Ingredient; end
|
15
|
+
class Milk < Ingredient; end
|
16
|
+
class Butter < Ingredient; end
|
17
|
+
|
18
|
+
class Recipe < Tins::BlankSlate.with(:respond_to?, :instance_exec, :inspect, /^deflect/)
|
19
|
+
include Tins::Deflect
|
20
|
+
|
21
|
+
def initialize(&block)
|
22
|
+
@ingredients = []
|
23
|
+
deflector = Deflector.new do |number, id, name|
|
24
|
+
if unit = Unit.unit(name, number)
|
25
|
+
unit
|
26
|
+
else
|
27
|
+
ingredient = Ingredient.ingredient(name, number)
|
28
|
+
@ingredients << ingredient
|
29
|
+
end
|
30
|
+
end
|
31
|
+
deflect(Numeric, :method_missing, deflector) do
|
32
|
+
instance_exec(&block)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
attr_reader :ingredients
|
37
|
+
|
38
|
+
def to_a
|
39
|
+
@ingredients
|
40
|
+
end
|
41
|
+
alias to_ary to_a
|
42
|
+
|
43
|
+
def to_s
|
44
|
+
to_a * "\n"
|
45
|
+
end
|
46
|
+
alias inspect to_s
|
47
|
+
|
48
|
+
def method_missing(name, *args)
|
49
|
+
name = name.to_s.gsub(/_/, '').capitalize
|
50
|
+
@ingredients << Object.const_get(name).new(*args)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
class RecipeInterpreter
|
55
|
+
def recipe(&block)
|
56
|
+
Recipe.new(&block)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
recipe_source = <<EOT
|
61
|
+
pancakes = recipe do
|
62
|
+
flour 2.cups
|
63
|
+
baking_powder 2.5.teaspoons
|
64
|
+
salt 0.5.teaspoon
|
65
|
+
egg 1
|
66
|
+
milk 1.5.cups
|
67
|
+
butter 2.tablespoons
|
68
|
+
end
|
69
|
+
EOT
|
70
|
+
|
71
|
+
pancakes = RecipeInterpreter.new.interpret(recipe_source)
|
72
|
+
puts pancakes
|
73
|
+
|
74
|
+
puts
|
75
|
+
|
76
|
+
def recipe(&block)
|
77
|
+
Recipe.new(&block)
|
78
|
+
end
|
79
|
+
|
80
|
+
pancakes = eval recipe_source
|
81
|
+
puts pancakes
|
data/examples/recipe2.rb
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'tins/xt'
|
4
|
+
$:.unshift 'examples'
|
5
|
+
require 'recipe_common'
|
6
|
+
|
7
|
+
class Cup < Unit; end
|
8
|
+
class Teaspoon < Unit; end
|
9
|
+
class Tablespoon < Unit; end
|
10
|
+
|
11
|
+
class Flour < Ingredient; end
|
12
|
+
class Bakingpowder < Ingredient; end
|
13
|
+
class Salt < Ingredient; end
|
14
|
+
class Egg < Ingredient; end
|
15
|
+
class Milk < Ingredient; end
|
16
|
+
class Butter < Ingredient; end
|
17
|
+
|
18
|
+
class Recipe < Tins::BlankSlate.with(:respond_to?, :instance_exec, :inspect, /^deflect/)
|
19
|
+
include Tins::Deflect
|
20
|
+
|
21
|
+
def initialize(&block)
|
22
|
+
@ingredients = []
|
23
|
+
deflector = Deflector.new do |number, id, name|
|
24
|
+
if unit = Unit.unit(name, number)
|
25
|
+
unit
|
26
|
+
else
|
27
|
+
ingredient = Ingredient.ingredient(name, number)
|
28
|
+
@ingredients << ingredient
|
29
|
+
end
|
30
|
+
end
|
31
|
+
deflect_start(Numeric, :method_missing, deflector)
|
32
|
+
deflector2 = Deflector.new do |unit, id, name|
|
33
|
+
ingredient = Ingredient.ingredient(name, unit)
|
34
|
+
@ingredients << ingredient
|
35
|
+
end
|
36
|
+
deflect_start(Unit, :method_missing, deflector2)
|
37
|
+
instance_exec(&block)
|
38
|
+
ensure
|
39
|
+
deflect_stop(Numeric, :method_missing) if deflect?(Numeric, :method_missing)
|
40
|
+
deflect_stop(Unit, :method_missing) if deflect?(Unit, :method_missing)
|
41
|
+
end
|
42
|
+
|
43
|
+
attr_reader :ingredients
|
44
|
+
|
45
|
+
def to_a
|
46
|
+
@ingredients
|
47
|
+
end
|
48
|
+
alias to_ary to_a
|
49
|
+
|
50
|
+
def to_s
|
51
|
+
to_a * "\n"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
class RecipeInterpreter
|
56
|
+
def recipe(&block)
|
57
|
+
Recipe.new(&block)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
recipe_source = <<EOT
|
62
|
+
pancakes = recipe do
|
63
|
+
2.cups. flour
|
64
|
+
2.5.teaspoons. baking_powder
|
65
|
+
0.5.teaspoon. salt
|
66
|
+
1. egg
|
67
|
+
1.5.cups. milk
|
68
|
+
2.tablespoons. butter
|
69
|
+
end
|
70
|
+
EOT
|
71
|
+
|
72
|
+
pancakes = RecipeInterpreter.new.interpret(recipe_source)
|
73
|
+
puts pancakes
|
74
|
+
|
75
|
+
puts
|
76
|
+
|
77
|
+
def recipe(&block)
|
78
|
+
Recipe.new(&block)
|
79
|
+
end
|
80
|
+
|
81
|
+
pancakes = eval recipe_source
|
82
|
+
puts pancakes
|
@@ -0,0 +1,97 @@
|
|
1
|
+
class Ingredient
|
2
|
+
class << self
|
3
|
+
def inherited(klass)
|
4
|
+
ingredients << klass
|
5
|
+
end
|
6
|
+
|
7
|
+
attr_accessor :ingredients
|
8
|
+
|
9
|
+
def ingredient(name, amount)
|
10
|
+
name = name.to_s.gsub(/_/, '').capitalize
|
11
|
+
if klass = ingredients.find { |n| n.to_s == name }
|
12
|
+
klass.new(amount)
|
13
|
+
else
|
14
|
+
raise "unknown ingredient #{name}"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
self.ingredients = []
|
19
|
+
|
20
|
+
def initialize(amount = 1)
|
21
|
+
@amount = amount
|
22
|
+
end
|
23
|
+
|
24
|
+
def name
|
25
|
+
self.class.name.downcase
|
26
|
+
end
|
27
|
+
|
28
|
+
attr_reader :amount
|
29
|
+
|
30
|
+
def to_s
|
31
|
+
"#@amount #{name}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class Unit
|
36
|
+
class << self
|
37
|
+
def inherited(klass)
|
38
|
+
units << klass
|
39
|
+
end
|
40
|
+
|
41
|
+
attr_accessor :units
|
42
|
+
|
43
|
+
def unit(name, amount)
|
44
|
+
name = name.to_s.gsub(/s$/, '').capitalize
|
45
|
+
if klass = units.find { |n| n.to_s == name }
|
46
|
+
klass.new(amount)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
self.units = []
|
51
|
+
|
52
|
+
def initialize(n = 1)
|
53
|
+
@n = n
|
54
|
+
end
|
55
|
+
|
56
|
+
def name
|
57
|
+
self.class.name.downcase
|
58
|
+
end
|
59
|
+
|
60
|
+
attr_reader :n
|
61
|
+
|
62
|
+
def to_s
|
63
|
+
"#@n #{name}#{@n > 1 ? 's' : ''}"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
class Unit
|
68
|
+
class << self
|
69
|
+
def inherited(klass)
|
70
|
+
units << klass
|
71
|
+
end
|
72
|
+
|
73
|
+
attr_accessor :units
|
74
|
+
|
75
|
+
def unit(name, amount)
|
76
|
+
name = name.to_s.gsub(/s$/, '').capitalize
|
77
|
+
if klass = units.find { |n| n.to_s == name }
|
78
|
+
klass.new(amount)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
self.units = []
|
83
|
+
|
84
|
+
def initialize(n = 1)
|
85
|
+
@n = n
|
86
|
+
end
|
87
|
+
|
88
|
+
def name
|
89
|
+
self.class.name.downcase
|
90
|
+
end
|
91
|
+
|
92
|
+
attr_reader :n
|
93
|
+
|
94
|
+
def to_s
|
95
|
+
"#@n #{name}#{@n > 1 ? 's' : ''}"
|
96
|
+
end
|
97
|
+
end
|