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