tataru 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +5 -0
  3. data/bin/console +15 -0
  4. data/bin/setup +8 -0
  5. data/lib/tataru.rb +70 -12
  6. data/lib/tataru/base_resource.rb +49 -0
  7. data/lib/tataru/base_resource_desc.rb +35 -0
  8. data/lib/tataru/compiler.rb +100 -0
  9. data/lib/tataru/create_subroutines.rb +31 -0
  10. data/lib/tataru/delete_subroutines.rb +42 -0
  11. data/lib/tataru/flattener.rb +81 -0
  12. data/lib/tataru/init_hash_compiler.rb +41 -0
  13. data/lib/tataru/instruction.rb +52 -7
  14. data/lib/tataru/instruction_hash.rb +54 -0
  15. data/lib/tataru/instructions/call_instruction.rb +19 -0
  16. data/lib/tataru/instructions/check_create_instruction.rb +27 -0
  17. data/lib/tataru/instructions/check_delete_instruction.rb +20 -0
  18. data/lib/tataru/instructions/check_instruction.rb +26 -0
  19. data/lib/tataru/instructions/check_update_instruction.rb +27 -0
  20. data/lib/tataru/instructions/clear_instruction.rb +12 -0
  21. data/lib/tataru/instructions/compare_instruction.rb +16 -0
  22. data/lib/tataru/instructions/create_instruction.rb +20 -0
  23. data/lib/tataru/instructions/delete_instruction.rb +14 -0
  24. data/lib/tataru/instructions/end_instruction.rb +12 -0
  25. data/lib/tataru/instructions/goto_if_instruction.rb +26 -0
  26. data/lib/tataru/instructions/immediate_mode_instruction.rb +12 -0
  27. data/lib/tataru/instructions/init_instruction.rb +27 -0
  28. data/lib/tataru/instructions/key_instruction.rb +12 -0
  29. data/lib/tataru/instructions/mark_deletable_instruction.rb +13 -0
  30. data/lib/tataru/instructions/read_instruction.rb +28 -0
  31. data/lib/tataru/instructions/rescmp_instruction.rb +34 -0
  32. data/lib/tataru/instructions/resource_instruction.rb +15 -0
  33. data/lib/tataru/instructions/return_instruction.rb +16 -0
  34. data/lib/tataru/instructions/update_instruction.rb +16 -0
  35. data/lib/tataru/instructions/value_instruction.rb +15 -0
  36. data/lib/tataru/instructions/value_rom_instruction.rb +23 -0
  37. data/lib/tataru/instructions/value_update_instruction.rb +18 -0
  38. data/lib/tataru/memory.rb +16 -0
  39. data/lib/tataru/quest.rb +43 -0
  40. data/lib/tataru/representation.rb +22 -0
  41. data/lib/tataru/representations/array_representation.rb +18 -0
  42. data/lib/tataru/representations/hash_representation.rb +20 -0
  43. data/lib/tataru/representations/literal_representation.rb +9 -0
  44. data/lib/tataru/representations/output_representation.rb +19 -0
  45. data/lib/tataru/representations/resource_representation.rb +49 -0
  46. data/lib/tataru/resolver.rb +39 -0
  47. data/lib/tataru/resource_dsl.rb +21 -71
  48. data/lib/tataru/resource_type_pool.rb +22 -0
  49. data/lib/tataru/rom_reader.rb +47 -0
  50. data/lib/tataru/runner.rb +48 -0
  51. data/lib/tataru/sub_planner.rb +41 -0
  52. data/lib/tataru/subroutine_compiler.rb +52 -0
  53. data/lib/tataru/top_dsl.rb +35 -0
  54. data/lib/tataru/update_subroutines.rb +111 -0
  55. data/lib/tataru/version.rb +1 -1
  56. data/spec/compiler_spec.rb +181 -0
  57. data/spec/flattener_spec.rb +88 -0
  58. data/spec/init_hash_compiler_spec.rb +85 -0
  59. data/spec/instruction_hash_spec.rb +63 -0
  60. data/spec/instruction_spec.rb +36 -0
  61. data/spec/instructions/call_instruction_spec.rb +28 -0
  62. data/spec/instructions/check_create_instruction_spec.rb +67 -0
  63. data/spec/instructions/check_delete_instruction_spec.rb +47 -0
  64. data/spec/instructions/check_update_instruction_spec.rb +67 -0
  65. data/spec/instructions/clear_instruction_spec.rb +16 -0
  66. data/spec/instructions/compare_instruction_spec.rb +29 -0
  67. data/spec/instructions/create_instruction_spec.rb +62 -0
  68. data/spec/instructions/delete_instruction_spec.rb +20 -0
  69. data/spec/instructions/end_instruction_spec.rb +15 -0
  70. data/spec/instructions/goto_if_instruction_spec.rb +42 -0
  71. data/spec/instructions/init_instruction_spec.rb +16 -0
  72. data/spec/instructions/key_instruction_spec.rb +15 -0
  73. data/spec/instructions/mark_deletable_instruction_spec.rb +20 -0
  74. data/spec/instructions/read_instruction_spec.rb +34 -0
  75. data/spec/instructions/rescmp_instruction_spec.rb +113 -0
  76. data/spec/instructions/return_instruction_spec.rb +28 -0
  77. data/spec/instructions/update_instruction_spec.rb +39 -0
  78. data/spec/instructions/value_instruction_spec.rb +27 -0
  79. data/spec/instructions/value_rom_instruction_spec.rb +170 -0
  80. data/spec/instructions/value_update_instruction_spec.rb +35 -0
  81. data/spec/quest_spec.rb +9 -0
  82. data/spec/representations/array_representation_spec.rb +29 -0
  83. data/spec/representations/hash_representation_spec.rb +29 -0
  84. data/spec/representations/literal_representation_spec.rb +10 -0
  85. data/spec/representations/output_representation_spec.rb +11 -0
  86. data/spec/representations/resource_representation_spec.rb +50 -0
  87. data/spec/resource_dsl_spec.rb +71 -0
  88. data/spec/runner_spec.rb +99 -0
  89. data/spec/spec_helper.rb +401 -0
  90. data/spec/subroutine_compiler_spec.rb +39 -0
  91. data/spec/taru_spec.rb +616 -0
  92. data/spec/top_dsl_spec.rb +68 -0
  93. data/tataru.gemspec +45 -0
  94. metadata +145 -27
  95. data/Rakefile +0 -8
  96. data/lib/tataru/default_resource_finder.rb +0 -10
  97. data/lib/tataru/execution_step.rb +0 -79
  98. data/lib/tataru/planner.rb +0 -93
  99. data/lib/tataru/requirements.rb +0 -67
  100. data/lib/tataru/requirements_dsl.rb +0 -33
  101. data/lib/tataru/resource.rb +0 -52
  102. data/lib/tataru/state.rb +0 -64
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'tataru'
4
+
5
+ describe Tataru::Flattener do
6
+ it 'flattens literals' do
7
+ f = Tataru::Flattener.new(Tataru::Representations::LiteralRepresentation.new('meow'))
8
+ expect(f.flattened).to eq(
9
+ top: {
10
+ type: :literal,
11
+ value: 'meow'
12
+ }
13
+ )
14
+ end
15
+
16
+ it 'flattens arrays' do
17
+ f = Tataru::Flattener.new(Tataru::Representations::ArrayRepresentation.new(['meow']))
18
+ expect(f.flattened).to eq(
19
+ top: {
20
+ type: :array,
21
+ references: {
22
+ 0 => :top_0
23
+ }
24
+ },
25
+ top_0: {
26
+ type: :literal,
27
+ value: 'meow'
28
+ }
29
+ )
30
+ end
31
+
32
+ it 'flattens hashes' do
33
+ f = Tataru::Flattener.new(Tataru::Representations::HashRepresentation.new({somefield: 'somevalue'}))
34
+ expect(f.flattened).to eq(
35
+ top: {
36
+ type: :hash,
37
+ references: {
38
+ somefield: :top_somefield
39
+ }
40
+ },
41
+ top_somefield: {
42
+ type: :literal,
43
+ value: 'somevalue'
44
+ }
45
+ )
46
+ end
47
+
48
+ it 'flattens resources' do
49
+ f = Tataru::Flattener.new(Tataru::Representations::ResourceRepresentation.new('thing', Tataru::BaseResourceDesc.new, {}))
50
+ expect(f.flattened).to eq(
51
+ top: {
52
+ type: :hash,
53
+ references: {}
54
+ }
55
+ )
56
+ end
57
+
58
+ it 'flattens outputs' do
59
+ f = Tataru::Flattener.new(Tataru::Representations::OutputRepresentation.new('employee', 'age'))
60
+ expect(f.flattened).to eq(
61
+ top: {
62
+ type: :output,
63
+ resource: 'employee',
64
+ output: 'age'
65
+ }
66
+ )
67
+ end
68
+
69
+ it 'flattens resources that have properties' do
70
+ f = Tataru::Flattener.new(Tataru::Representations::ResourceRepresentation.new(
71
+ 'thing',
72
+ Tataru::BaseResourceDesc.new,
73
+ { somefield: Tataru::Representations::LiteralRepresentation.new('somevalue') }
74
+ ))
75
+ expect(f.flattened).to eq(
76
+ top: {
77
+ type: :hash,
78
+ references: {
79
+ somefield: :top_somefield
80
+ }
81
+ },
82
+ top_somefield: {
83
+ type: :literal,
84
+ value: 'somevalue'
85
+ }
86
+ )
87
+ end
88
+ end
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'tataru'
4
+
5
+ describe Tataru::InitHashCompiler do
6
+ it 'resolves references' do
7
+ dsl = Tataru::TopDsl.new(Tataru::ResourceTypePool.new)
8
+ ihc = Tataru::InitHashCompiler.new(dsl)
9
+
10
+ refs = {
11
+ aaa: 'top_thing',
12
+ bbb: 'top'
13
+ }
14
+
15
+ expect(ihc.resolved_references('resname', refs)).to eq(
16
+ aaa: 'resname_thing',
17
+ bbb: 'resname'
18
+ )
19
+ end
20
+
21
+ it 'generates an init hash' do
22
+ dsl = Tataru::TopDsl.new(Tataru::ResourceTypePool.new)
23
+ ihc = Tataru::InitHashCompiler.new(dsl)
24
+
25
+ expect(ihc.generate_init_hash).to eq(rom: {}, remote_ids: {})
26
+ end
27
+
28
+ it 'generates hashes for each resource' do
29
+ dsl = Tataru::TopDsl.new(Tataru::ResourceTypePool.new)
30
+ ihc = Tataru::InitHashCompiler.new(dsl)
31
+
32
+ rr = Tataru::Representations::ResourceRepresentation.new('file', Tataru::BaseResourceDesc.new, {})
33
+ allow(dsl).to receive(:resources) { { 'file1' => rr } }
34
+
35
+ allow_any_instance_of(Tataru::Flattener).to receive(:flattened) do
36
+ {
37
+ 'abc' => {
38
+ type: :teststuff
39
+ }
40
+ }
41
+ end
42
+
43
+ expect(ihc.generate_init_hash).to eq(
44
+ rom: {
45
+ 'abc' => {
46
+ type: :teststuff
47
+ }
48
+ },
49
+ remote_ids: {}
50
+ )
51
+ end
52
+
53
+ it 'generates hashes for each resource with references' do
54
+ dsl = Tataru::TopDsl.new(Tataru::ResourceTypePool.new)
55
+ ihc = Tataru::InitHashCompiler.new(dsl)
56
+
57
+ rr = Tataru::Representations::ResourceRepresentation.new('file', Tataru::BaseResourceDesc.new, {
58
+ aaa: Tataru::Representations::LiteralRepresentation.new('meow')
59
+ })
60
+ allow(dsl).to receive(:resources) { { 'file1' => rr } }
61
+
62
+ allow_any_instance_of(Tataru::Flattener).to receive(:flattened) do
63
+ {
64
+ 'abc' => {
65
+ type: :teststuff,
66
+ references: {
67
+ thing: 'top_stuff'
68
+ }
69
+ }
70
+ }
71
+ end
72
+
73
+ expect(ihc.generate_init_hash).to eq(
74
+ rom: {
75
+ 'abc' => {
76
+ type: :teststuff,
77
+ references: {
78
+ thing: 'file1_stuff'
79
+ }
80
+ }
81
+ },
82
+ remote_ids: {}
83
+ )
84
+ end
85
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'tataru'
4
+
5
+ describe Tataru::InstructionHash do
6
+ it 'can be made' do
7
+ im = Tataru::InstructionHash.new(init: {})
8
+
9
+ expect(im.instruction_list).to be_empty
10
+ end
11
+
12
+ it 'makes init properly' do
13
+ im = Tataru::InstructionHash.new(init: {
14
+ remote_ids: {
15
+ 'thing' => 'abc'
16
+ }
17
+ }, instructions: [:init])
18
+
19
+ expect(im.instruction_list[0]).to be_a(Tataru::Instructions::InitInstruction)
20
+ expect(im.instruction_list[0].remote_ids).to eq('thing' => 'abc')
21
+ end
22
+
23
+ it 'adds instructions' do
24
+ im = Tataru::InstructionHash.new(instructions:[
25
+ :create
26
+ ])
27
+
28
+ expect(Tataru::Instructions::CreateInstruction).to receive(:new).and_call_original
29
+
30
+ expect(im.instruction_list[0]).to be_a(Tataru::Instructions::CreateInstruction)
31
+ end
32
+
33
+ it 'add more instructions' do
34
+ im = Tataru::InstructionHash.new(instructions:[
35
+ :create,
36
+ :delete
37
+ ])
38
+
39
+ expect(Tataru::Instructions::CreateInstruction).to receive(:new).and_call_original
40
+ expect(Tataru::Instructions::DeleteInstruction).to receive(:new).and_call_original
41
+
42
+ expect(im.instruction_list[0]).to be_a(Tataru::Instructions::CreateInstruction)
43
+ expect(im.instruction_list[1]).to be_a(Tataru::Instructions::DeleteInstruction)
44
+ end
45
+
46
+ it 'adds immediate mode instructions' do
47
+ im = Tataru::InstructionHash.new(instructions:[
48
+ { key: 'name' }
49
+ ])
50
+
51
+ expect(Tataru::Instructions::KeyInstruction).to receive(:new).with('name').and_call_original
52
+
53
+ expect(im.instruction_list[0]).to be_a(Tataru::Instructions::KeyInstruction)
54
+ end
55
+
56
+ it 'errors on unknown instruction' do
57
+ im = Tataru::InstructionHash.new(instructions:[
58
+ :unknown
59
+ ])
60
+
61
+ expect { im.instruction_list[0] }.to raise_error "Unknown instruction 'unknown'"
62
+ end
63
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'tataru'
4
+
5
+ describe Tataru::Instruction do
6
+ it 'can be made' do
7
+ Tataru::Instruction.new
8
+ end
9
+
10
+ it 'checks parameters' do
11
+ instr = Class.new(Tataru::Instruction)
12
+ instr.class_eval do
13
+ expects :param1
14
+ end
15
+
16
+ mem = Tataru::Memory.new
17
+ mem.hash[:temp] = {}
18
+
19
+ expect { instr.new.execute(mem) }.to raise_error 'required param param1 not found'
20
+ end
21
+
22
+ it 'allows execution to occur if param exists' do
23
+ instr = Class.new(Tataru::Instruction)
24
+ instr.class_eval do
25
+ expects :param1
26
+ end
27
+
28
+ mem = Tataru::Memory.new
29
+ mem.hash[:temp] = { param1: 'hello' }
30
+
31
+ instruction = instr.new
32
+ expect(instruction).to receive(:run)
33
+
34
+ instruction.execute(mem)
35
+ end
36
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'tataru'
4
+
5
+ describe Tataru::Instructions::CallInstruction do
6
+ it 'call an existing label' do
7
+ mem = Tataru::Memory.new
8
+ instr = Tataru::Instructions::CallInstruction.new('function')
9
+
10
+ mem.hash[:labels] = { 'function' => 10 }
11
+ instr.memory = mem
12
+ instr.run
13
+
14
+ expect(mem.program_counter).to eq 9
15
+ end
16
+
17
+ it 'sets error if no such label' do
18
+ mem = Tataru::Memory.new
19
+ mem.hash[:labels] = { }
20
+ instr = Tataru::Instructions::CallInstruction.new('function')
21
+
22
+ instr.memory = mem
23
+ instr.run
24
+
25
+ expect(mem.program_counter).to eq 0
26
+ expect(mem.error).to eq 'Label not found'
27
+ end
28
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'tataru'
4
+
5
+ describe Tataru::Instructions::CheckCreateInstruction do
6
+ it 'reverses program counter if not completed' do
7
+ mem = Tataru::Memory.new
8
+ instr = Tataru::Instructions::CheckCreateInstruction.new
9
+
10
+ expect(mem.program_counter).to eq 0
11
+
12
+ mem.hash[:temp] = {
13
+ resource_name: 'thing',
14
+ resource_desc: 'Tataru::BaseResourceDesc'
15
+ }
16
+ mem.hash[:remote_ids] = { 'thing' => 'hello' }
17
+ instr.memory = mem
18
+
19
+ allow_any_instance_of(Tataru::BaseResource).to receive(:create_complete?) { false }
20
+ instr.run
21
+
22
+ expect(mem.program_counter).to eq -1
23
+ end
24
+
25
+ it 'sets outputs' do
26
+ mem = Tataru::Memory.new
27
+ instr = Tataru::Instructions::CheckCreateInstruction.new
28
+
29
+ expect(mem.program_counter).to eq 0
30
+
31
+ mem.hash[:temp] = {
32
+ resource_name: 'thing',
33
+ resource_desc: 'Tataru::BaseResourceDesc'
34
+ }
35
+ mem.hash[:remote_ids] = { 'thing' => 'hello' }
36
+ mem.hash[:outputs] = {}
37
+ instr.memory = mem
38
+
39
+ allow_any_instance_of(Tataru::BaseResource).to receive(:create_complete?) { true }
40
+ allow_any_instance_of(Tataru::BaseResource).to receive(:outputs) { { something: 'a'} }
41
+ allow_any_instance_of(Tataru::BaseResourceDesc).to receive(:output_fields) { [:something] }
42
+ instr.run
43
+
44
+ expect(mem.hash[:outputs]['thing']).to eq(something: 'a')
45
+ expect(mem.program_counter).to eq 0
46
+ end
47
+
48
+ it 'throws if output is not a hash' do
49
+ mem = Tataru::Memory.new
50
+ instr = Tataru::Instructions::CheckCreateInstruction.new
51
+
52
+ expect(mem.program_counter).to eq 0
53
+
54
+ mem.hash[:temp] = {
55
+ resource_name: 'thing',
56
+ resource_desc: 'Tataru::BaseResourceDesc'
57
+ }
58
+ mem.hash[:remote_ids] = { 'thing' => 'hello' }
59
+ mem.hash[:outputs] = {}
60
+ instr.memory = mem
61
+
62
+ allow_any_instance_of(Tataru::BaseResource).to receive(:create_complete?) { true }
63
+ allow_any_instance_of(Tataru::BaseResource).to receive(:outputs) { 'a2' }
64
+ allow_any_instance_of(Tataru::BaseResourceDesc).to receive(:output_fields) { [:something] }
65
+ expect { instr.run }.to raise_error "Output for 'thing' is not a hash"
66
+ end
67
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'tataru'
4
+
5
+ describe Tataru::Instructions::CheckDeleteInstruction do
6
+ it 'reverses program counter if not completed' do
7
+ mem = Tataru::Memory.new
8
+ instr = Tataru::Instructions::CheckDeleteInstruction.new
9
+
10
+ expect(mem.program_counter).to eq 0
11
+
12
+ mem.hash[:temp] = {
13
+ resource_name: 'thing',
14
+ resource_desc: 'Tataru::BaseResourceDesc'
15
+ }
16
+ mem.hash[:remote_ids] = { 'thing' => 'hello' }
17
+ instr.memory = mem
18
+
19
+ allow_any_instance_of(Tataru::BaseResource).to receive(:delete_complete?) { false }
20
+ instr.run
21
+
22
+ expect(mem.program_counter).to eq -1
23
+ end
24
+
25
+ it 'sets deleted' do
26
+ mem = Tataru::Memory.new
27
+ instr = Tataru::Instructions::CheckDeleteInstruction.new
28
+
29
+ expect(mem.program_counter).to eq 0
30
+
31
+ mem.hash[:temp] = {
32
+ resource_name: 'thing',
33
+ resource_desc: 'Tataru::BaseResourceDesc'
34
+ }
35
+ mem.hash[:remote_ids] = { 'thing' => 'hello' }
36
+ mem.hash[:deleted] = []
37
+ instr.memory = mem
38
+
39
+ allow_any_instance_of(Tataru::BaseResource).to receive(:delete_complete?) { true }
40
+ allow_any_instance_of(Tataru::BaseResourceDesc).to receive(:needs_remote_id?) { true }
41
+ instr.run
42
+
43
+ expect(mem.hash[:deleted]).to eq ['thing']
44
+ expect(mem.hash[:remote_ids]).to eq({})
45
+ expect(mem.program_counter).to eq 0
46
+ end
47
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'tataru'
4
+
5
+ describe Tataru::Instructions::CheckUpdateInstruction do
6
+ it 'reverses program counter if not completed' do
7
+ mem = Tataru::Memory.new
8
+ instr = Tataru::Instructions::CheckUpdateInstruction.new
9
+
10
+ expect(mem.program_counter).to eq 0
11
+
12
+ mem.hash[:temp] = {
13
+ resource_name: 'thing',
14
+ resource_desc: 'Tataru::BaseResourceDesc'
15
+ }
16
+ mem.hash[:remote_ids] = { 'thing' => 'hello' }
17
+ instr.memory = mem
18
+
19
+ allow_any_instance_of(Tataru::BaseResource).to receive(:update_complete?) { false }
20
+ instr.run
21
+
22
+ expect(mem.program_counter).to eq -1
23
+ end
24
+
25
+ it 'sets outputs' do
26
+ mem = Tataru::Memory.new
27
+ instr = Tataru::Instructions::CheckUpdateInstruction.new
28
+
29
+ expect(mem.program_counter).to eq 0
30
+
31
+ mem.hash[:temp] = {
32
+ resource_name: 'thing',
33
+ resource_desc: 'Tataru::BaseResourceDesc'
34
+ }
35
+ mem.hash[:remote_ids] = { 'thing' => 'hello' }
36
+ mem.hash[:outputs] = {}
37
+ instr.memory = mem
38
+
39
+ allow_any_instance_of(Tataru::BaseResource).to receive(:update_complete?) { true }
40
+ allow_any_instance_of(Tataru::BaseResource).to receive(:outputs) { { something: 'a2'} }
41
+ allow_any_instance_of(Tataru::BaseResourceDesc).to receive(:output_fields) { [:something] }
42
+ instr.run
43
+
44
+ expect(mem.hash[:outputs]['thing']).to eq(something: 'a2')
45
+ expect(mem.program_counter).to eq 0
46
+ end
47
+
48
+ it 'throws if output is not a hash' do
49
+ mem = Tataru::Memory.new
50
+ instr = Tataru::Instructions::CheckUpdateInstruction.new
51
+
52
+ expect(mem.program_counter).to eq 0
53
+
54
+ mem.hash[:temp] = {
55
+ resource_name: 'thing',
56
+ resource_desc: 'Tataru::BaseResourceDesc'
57
+ }
58
+ mem.hash[:remote_ids] = { 'thing' => 'hello' }
59
+ mem.hash[:outputs] = {}
60
+ instr.memory = mem
61
+
62
+ allow_any_instance_of(Tataru::BaseResource).to receive(:update_complete?) { true }
63
+ allow_any_instance_of(Tataru::BaseResource).to receive(:outputs) { 'a2' }
64
+ allow_any_instance_of(Tataru::BaseResourceDesc).to receive(:output_fields) { [:something] }
65
+ expect { instr.run }.to raise_error "Output for 'thing' is not a hash"
66
+ end
67
+ end