debugger-xml 0.0.1

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 (73) hide show
  1. data/.gitignore +20 -0
  2. data/Gemfile +4 -0
  3. data/LICENSE.txt +22 -0
  4. data/README.md +24 -0
  5. data/Rakefile +10 -0
  6. data/bin/rdebug-ide +90 -0
  7. data/debugger-xml.gemspec +25 -0
  8. data/lib/debugger/printers/texts/xml.yml +121 -0
  9. data/lib/debugger/printers/xml.rb +195 -0
  10. data/lib/debugger/xml.rb +3 -0
  11. data/lib/debugger/xml/extensions/commands/edit.rb +13 -0
  12. data/lib/debugger/xml/extensions/commands/frame.rb +14 -0
  13. data/lib/debugger/xml/extensions/commands/help.rb +13 -0
  14. data/lib/debugger/xml/extensions/commands/info.rb +13 -0
  15. data/lib/debugger/xml/extensions/commands/irb.rb +13 -0
  16. data/lib/debugger/xml/extensions/commands/kill.rb +13 -0
  17. data/lib/debugger/xml/extensions/commands/tmate.rb +13 -0
  18. data/lib/debugger/xml/extensions/commands/trace.rb +13 -0
  19. data/lib/debugger/xml/extensions/commands/variables.rb +16 -0
  20. data/lib/debugger/xml/extensions/debugger.rb +28 -0
  21. data/lib/debugger/xml/extensions/processor.rb +9 -0
  22. data/lib/debugger/xml/ide_processor.rb +149 -0
  23. data/lib/debugger/xml/interface.rb +65 -0
  24. data/lib/debugger/xml/version.rb +5 -0
  25. data/test/breakpoints_test.rb +190 -0
  26. data/test/conditions_test.rb +32 -0
  27. data/test/continue_test.rb +12 -0
  28. data/test/display_test.rb +25 -0
  29. data/test/edit_test.rb +12 -0
  30. data/test/eval_test.rb +20 -0
  31. data/test/examples/breakpoint1.rb +15 -0
  32. data/test/examples/breakpoint2.rb +7 -0
  33. data/test/examples/conditions.rb +4 -0
  34. data/test/examples/continue.rb +4 -0
  35. data/test/examples/display.rb +5 -0
  36. data/test/examples/edit.rb +3 -0
  37. data/test/examples/eval.rb +4 -0
  38. data/test/examples/frame.rb +31 -0
  39. data/test/examples/help.rb +2 -0
  40. data/test/examples/info.rb +48 -0
  41. data/test/examples/irb.rb +6 -0
  42. data/test/examples/jump.rb +14 -0
  43. data/test/examples/kill.rb +2 -0
  44. data/test/examples/method.rb +15 -0
  45. data/test/examples/reload.rb +6 -0
  46. data/test/examples/restart.rb +6 -0
  47. data/test/examples/set.rb +3 -0
  48. data/test/examples/stepping.rb +21 -0
  49. data/test/examples/thread.rb +32 -0
  50. data/test/examples/tmate.rb +10 -0
  51. data/test/examples/trace.rb +7 -0
  52. data/test/examples/variables.rb +26 -0
  53. data/test/examples/variables_xml.rb +31 -0
  54. data/test/frame_test.rb +29 -0
  55. data/test/help_test.rb +13 -0
  56. data/test/ide_control_command_processor_test.rb +62 -0
  57. data/test/ide_processor_test.rb +118 -0
  58. data/test/info_test.rb +12 -0
  59. data/test/irb_test.rb +12 -0
  60. data/test/jump_test.rb +22 -0
  61. data/test/kill_test.rb +13 -0
  62. data/test/method_test.rb +36 -0
  63. data/test/printers/xml_test.rb +193 -0
  64. data/test/reload_test.rb +14 -0
  65. data/test/restart_test.rb +50 -0
  66. data/test/set_test.rb +12 -0
  67. data/test/stepping_test.rb +15 -0
  68. data/test/test_helper.rb +6 -0
  69. data/test/thread_test.rb +20 -0
  70. data/test/tmate_test.rb +15 -0
  71. data/test/trace_test.rb +12 -0
  72. data/test/variables_test.rb +84 -0
  73. metadata +253 -0
@@ -0,0 +1,29 @@
1
+ require_relative 'test_helper'
2
+
3
+ describe "Frame Command" do
4
+ include TestDsl
5
+ temporary_change_method_value(Debugger, :printer, Printers::Xml.new)
6
+
7
+ it "must print current stack frame when without arguments" do
8
+ enter 'break 25', 'cont', 'up', 'frame'
9
+ debug_file('frame')
10
+ check_output_includes %{<frame no="0" file="#{fullpath('frame')}" line="25" current="false"/>}
11
+ end
12
+
13
+ describe "full path settings" do
14
+ temporary_change_hash_value(Debugger::Command.settings, :full_path, false)
15
+
16
+ it "must display current backtrace with full path = true" do
17
+ enter 'set fullpath', 'break 25', 'cont', 'where'
18
+ debug_file('frame')
19
+ check_output_includes(Regexp.new(
20
+ "<frames>" +
21
+ %{<frame no="0" file="#{fullpath('frame')}" line="25" current="true"/>} +
22
+ %{<frame no="1" file="#{fullpath('frame')}" line="21" current="false"/>} +
23
+ %{<frame no="2" file="#{fullpath('frame')}" line="17" current="false"/>} +
24
+ %{<frame no="3" file="#{fullpath('frame')}" line="14" current="false"/>.*} +
25
+ "</frames>",
26
+ Regexp::MULTILINE))
27
+ end
28
+ end
29
+ end
data/test/help_test.rb ADDED
@@ -0,0 +1,13 @@
1
+ require_relative 'test_helper'
2
+
3
+ describe "Help Command" do
4
+ include TestDsl
5
+ temporary_change_method_value(Debugger, :printer, Printers::Xml.new)
6
+
7
+ it "must be unsupported for XML printer" do
8
+ enter 'help'
9
+ debug_file 'help'
10
+ check_output_includes "<error>Unsupported command 'help'</error>", interface.error_queue
11
+ end
12
+
13
+ end
@@ -0,0 +1,62 @@
1
+ require_relative 'test_helper'
2
+ require 'debugger/xml/ide_processor'
3
+
4
+ describe Debugger::Xml::IdeControlCommandProcessor do
5
+ include TestDsl
6
+
7
+ let(:klass) { Debugger::Xml::IdeControlCommandProcessor }
8
+ let(:interface) { Debugger.handler.interface }
9
+ let(:file) { fullpath('jump') }
10
+ let(:context) { stub(frame_binding: stub, stop_reason: nil, thread: stub, thnum: 1, stack_size: 2, dead?: false) }
11
+ subject { klass.new(interface) }
12
+ temporary_change_method_value(Debugger, :handler, Debugger::Xml::IdeProcessor.new(TestInterface.new))
13
+
14
+ before do
15
+ Thread.stubs(:stop)
16
+ Debugger.handler.instance_variable_set("@context", context)
17
+ Debugger.handler.instance_variable_set("@file", file)
18
+ Debugger.handler.instance_variable_set("@line", 30)
19
+ end
20
+
21
+ it "must process a control command" do
22
+ Debugger::AddBreakpoint.any_instance.expects(:execute).with()
23
+ enter 'break 5'
24
+ subject.process_commands
25
+ end
26
+
27
+ it "must process several commands, separated by ;" do
28
+ Debugger::AddBreakpoint.any_instance.expects(:execute).with()
29
+ Debugger::DeleteBreakpointCommand.any_instance.expects(:execute).with()
30
+ enter 'break 5; delete 1'
31
+ subject.process_commands
32
+ end
33
+
34
+ it "must show error if there is no such command" do
35
+ enter 'bla'
36
+ subject.process_commands
37
+ check_output_includes "Unknown command: bla"
38
+ end
39
+
40
+ it "must show error if context is dead" do
41
+ context.expects(:dead?).returns(true)
42
+ enter 'step'
43
+ subject.process_commands
44
+ check_output_includes "Command is unavailable"
45
+ end
46
+
47
+ it "must show error if no suspended thread" do
48
+ Debugger.handler.stubs(:at_line?).returns(false)
49
+ enter 'step'
50
+ subject.process_commands
51
+ check_output_includes(
52
+ "There is no thread suspended at the time and therefore no context to execute 'step'",
53
+ interface.error_queue)
54
+ end
55
+
56
+ it "must run stopped thread after stepping command" do
57
+ context.expects(:step).with(1, false)
58
+ context.thread.expects(:run)
59
+ enter 'step'
60
+ subject.process_commands
61
+ end
62
+ end
@@ -0,0 +1,118 @@
1
+ require_relative 'test_helper'
2
+ require 'debugger/xml/ide_processor'
3
+
4
+ describe Debugger::Xml::IdeProcessor do
5
+ include TestDsl
6
+
7
+ before { Thread.stubs(:stop) }
8
+
9
+ let(:klass) { Debugger::Xml::IdeProcessor }
10
+ let(:interface) { TestInterface.new }
11
+ let(:breakpoint) { stub }
12
+ let(:context) { stub(thread: nil, stop_reason: nil, thnum: 1, stack_size: 2) }
13
+ let(:file) { fullpath('jump') }
14
+ subject { klass.new(interface) }
15
+
16
+ describe "#at_breakpoint" do
17
+ it "must assign breakpoint to instance variable" do
18
+ subject.at_breakpoint(context, breakpoint)
19
+ subject.instance_variable_get("@last_breakpoint").must_equal breakpoint
20
+ end
21
+
22
+ it "must raise error if @last_breakpoint is already assigned" do
23
+ subject.instance_variable_set("@last_breakpoint", breakpoint)
24
+ subject.at_breakpoint(context, breakpoint)
25
+ check_output_includes /INTERNAL ERROR!!!/
26
+ end
27
+
28
+ it "must not print anything" do
29
+ subject.at_breakpoint(context, breakpoint)
30
+ interface.must_be_empty
31
+ end
32
+ end
33
+
34
+ describe "#at_line" do
35
+ describe "print current position" do
36
+ it "must print if context is nil" do
37
+ subject.at_line(nil, file, 30)
38
+ check_output_includes "#{file}:30"
39
+ end
40
+
41
+ it "must print in xml" do
42
+ temporary_change_method_value(Debugger, :printer, Printers::Xml.new) do
43
+ subject.at_line(nil, file, 30)
44
+ check_output_includes %{<suspended file="#{file}" line="30" threadId="" frames=""/>}
45
+ end
46
+ end
47
+
48
+ it "must print if stop reason is :step" do
49
+ context.stubs(:stop_reason).returns(:step)
50
+ subject.at_line(context, file, 30)
51
+ check_output_includes "#{file}:30"
52
+ end
53
+
54
+ it "must clear instance variables after resuming thread" do
55
+ subject.instance_variable_set("@line", 10)
56
+ subject.at_line(context, file, 30)
57
+ subject.instance_variable_get("@line").must_be_nil
58
+ end
59
+
60
+ describe "print breakpoint after at_breakpoint" do
61
+ before do
62
+ Debugger.stubs(:breakpoints).returns([breakpoint])
63
+ Debugger.stubs(:current_context).returns(stub(thnum: 1))
64
+ subject.instance_variable_set("@last_breakpoint", breakpoint)
65
+ end
66
+
67
+ it "must print in plain text" do
68
+ subject.at_line(context, file, 30)
69
+ check_output_includes "Breakpoint 1 at #{file}:30"
70
+ end
71
+
72
+ it "must print in xml" do
73
+ temporary_change_method_value(Debugger, :printer, Printers::Xml.new) do
74
+ subject.at_line(context, file, 30)
75
+ check_output_includes %{<breakpoint file="#{file}" line="30" threadId="1"/>}
76
+ end
77
+ end
78
+ end
79
+
80
+ it "must show error if current thread is DebugThread" do
81
+ context.stubs(:thread).returns(Debugger::DebugThread.new {})
82
+ subject.at_line(context, file, 30)
83
+ check_output_includes /DebuggerThread are not supposed to be traced/
84
+ end
85
+ end
86
+ end
87
+
88
+ describe "#at_line?" do
89
+ it "returns false if #at_line was not called yet" do
90
+ subject.at_line?.must_equal false
91
+ end
92
+
93
+ it "returns true if #at_line was called already" do
94
+ subject.instance_variable_set("@line", 10)
95
+ subject.at_line?.must_equal true
96
+ end
97
+ end
98
+
99
+ describe "#at_return?" do
100
+ before { context.stubs(:stop_frame=).with(-1) }
101
+
102
+ it "sets stop_frame to -1" do
103
+ context.expects(:stop_frame=).with(-1)
104
+ subject.at_return(context, file, 30)
105
+ end
106
+
107
+ it "prints current file and line" do
108
+ subject.at_return(context, file, 30)
109
+ check_output_includes "#{file}:30"
110
+ end
111
+
112
+ it "stops the thread" do
113
+ Thread.expects(:stop)
114
+ subject.at_return(context, file, 30)
115
+ end
116
+ end
117
+
118
+ end
data/test/info_test.rb ADDED
@@ -0,0 +1,12 @@
1
+ require_relative 'test_helper'
2
+
3
+ describe "Info Command" do
4
+ include TestDsl
5
+ temporary_change_method_value(Debugger, :printer, Printers::Xml.new)
6
+
7
+ it "must be unsupported for XML printer" do
8
+ enter 'info line'
9
+ debug_file 'info'
10
+ check_output_includes "<error>Unsupported command 'info'</error>", interface.error_queue
11
+ end
12
+ end
data/test/irb_test.rb ADDED
@@ -0,0 +1,12 @@
1
+ require_relative 'test_helper'
2
+
3
+ describe "Irb Command" do
4
+ include TestDsl
5
+ temporary_change_method_value(Debugger, :printer, Printers::Xml.new)
6
+
7
+ it "must be unsupported for XML printer" do
8
+ enter 'irb'
9
+ debug_file 'irb'
10
+ check_output_includes "<error>Unsupported command 'irb'</error>", interface.error_queue
11
+ end
12
+ end
data/test/jump_test.rb ADDED
@@ -0,0 +1,22 @@
1
+ require_relative 'test_helper'
2
+
3
+ describe "Jump Command" do
4
+ include TestDsl
5
+ temporary_change_method_value(Debugger, :printer, Printers::Xml.new)
6
+
7
+ describe "successful" do
8
+ describe "jumping to the same line" do
9
+ it "must show show the same position" do
10
+ enter 'break 6', 'cont', "jump 6 #{fullpath('jump')}"
11
+ debug_file('jump')
12
+ check_output_includes /<suspended file="#{fullpath('jump')}" line="6" threadId="\d+" frames="\d+"\/>/
13
+ end
14
+ end
15
+
16
+ it "must show message after jump" do
17
+ enter 'break 6', 'cont', "jump 8 #{fullpath('jump')}"
18
+ debug_file('jump')
19
+ check_output_includes /<suspended file="#{fullpath('jump')}" line="8" threadId="\d+" frames="\d+"\/>/
20
+ end
21
+ end
22
+ end
data/test/kill_test.rb ADDED
@@ -0,0 +1,13 @@
1
+ require_relative 'test_helper'
2
+
3
+ describe "Kill Command" do
4
+ include TestDsl
5
+ temporary_change_method_value(Debugger, :printer, Printers::Xml.new)
6
+
7
+ it "must be unsupported for XML printer" do
8
+ enter 'kill'
9
+ debug_file 'kill'
10
+ check_output_includes "<error>Unsupported command 'kill'</error>", interface.error_queue
11
+ end
12
+
13
+ end
@@ -0,0 +1,36 @@
1
+ require_relative 'test_helper'
2
+
3
+ describe "Method Command" do
4
+ include TestDsl
5
+ temporary_change_method_value(Debugger, :printer, Printers::Xml.new)
6
+
7
+ describe "show instance method of a class" do
8
+ it "must show using full command name" do
9
+ enter 'break 15', 'cont', 'm MethodEx'
10
+ debug_file 'method'
11
+ check_output_includes '<methods><method name="bla"/></methods>'
12
+ end
13
+ end
14
+
15
+ describe "show methods of an object" do
16
+ it "must show using full command name" do
17
+ enter 'break 15', 'cont', 'method instance a'
18
+ debug_file 'method'
19
+ check_output_includes /<methods>.*<method name="bla"\/>.*<\/methods>/
20
+ end
21
+ end
22
+
23
+ describe "show instance variables of an object" do
24
+ it "must show using full name command" do
25
+ enter 'break 15', 'cont', 'method iv a'
26
+ debug_file 'method'
27
+ check_output_includes(Regexp.new(
28
+ %{<variables>} +
29
+ %{<variable name="@a" kind="instance" value="b" type="String" hasChildren="false" objectId=".*?"/>} +
30
+ %{<variable name="@c" kind="instance" value="d" type="String" hasChildren="false" objectId=".*?"/>} +
31
+ %{</variables>}
32
+ ))
33
+ end
34
+ end
35
+
36
+ end
@@ -0,0 +1,193 @@
1
+ require_relative '../test_helper'
2
+
3
+ describe "Printers::Xml" do
4
+ include PrinterHelpers
5
+
6
+ let(:klass) { Printers::Xml }
7
+ let(:printer) { klass.new }
8
+ let(:yaml_xml) do
9
+ {
10
+ "foo" => {
11
+ "errors" => {
12
+ "bad" => "bad behavior"
13
+ },
14
+ "confirmations" => {
15
+ "okay" => "Okay?"
16
+ },
17
+ "debug" => {
18
+ "dbg" => "Debug message"
19
+ },
20
+ "bar" => {
21
+ "tag" => "xmltag",
22
+ "attributes" => {
23
+ "boo" => "{zee} > {uga}",
24
+ "agu" => "bew"
25
+ }
26
+ }
27
+ },
28
+ "variable" => {
29
+ "variable" => {
30
+ "tag" => "variable",
31
+ "attributes" => {
32
+ "name" => "{name}",
33
+ "kind" => "{kind}",
34
+ "value" => "{value}",
35
+ "type" => "{type}",
36
+ "hasChildren" => "{has_children}",
37
+ "objectId" => "{id}"
38
+ }
39
+ }
40
+ }
41
+ }
42
+ end
43
+
44
+ before do
45
+ YAML.stubs(:load_file).with(yaml_file_path('xml')).returns(yaml_xml)
46
+ YAML.stubs(:load_file).with(yaml_file_path('base')).returns({})
47
+ end
48
+
49
+ describe "#print" do
50
+ it "must return correctly translated string" do
51
+ xml = ::Builder::XmlMarkup.new.xmltag(boo: "zuu > aga", agu: "bew")
52
+ printer.print("foo.bar", zee: "zuu", uga: "aga").must_equal xml
53
+ end
54
+
55
+ it "must return error string" do
56
+ printer.print("foo.errors.bad").must_equal "<error>bad behavior</error>"
57
+ end
58
+
59
+ it "must return confirmation string" do
60
+ printer.print("foo.confirmations.okay").must_equal "<confirmation>Okay?</confirmation>"
61
+ end
62
+
63
+ it "must return debug string" do
64
+ printer.print("foo.debug.dbg").must_equal "Debug message"
65
+ end
66
+ end
67
+
68
+ describe "#print_collection" do
69
+ it "must print collection" do
70
+ expected = ::Builder::XmlMarkup.new.xmltags do |x|
71
+ x.xmltag(boo: "0 > a", agu: "bew") + x.xmltag(boo: "1 > b", agu: "bew")
72
+ end
73
+ result = printer.print_collection("foo.bar", [{uga: 'a'}, {uga: 'b'}]) do |item, index|
74
+ item.merge(zee: index)
75
+ end
76
+ result.must_equal expected
77
+ end
78
+ end
79
+
80
+ describe "#print_variables" do
81
+ it "must print variables" do
82
+ vars = [["a", "b"], ["c", "d"]]
83
+ expected = ::Builder::XmlMarkup.new.variables do |x|
84
+ vars.map do |key, value|
85
+ x.variable(name: key, kind: "instance", value: value, type: "String", hasChildren: "false", objectId: "%#+x" % value.object_id)
86
+ end.join("")
87
+ end
88
+ result = printer.print_variables(vars, 'instance')
89
+ result.must_equal expected
90
+ end
91
+ end
92
+
93
+ describe "Printers::Xml::Variable" do
94
+ let(:klass) { Printers::Xml::Variable }
95
+
96
+ describe "#has_children?" do
97
+ describe "value is Array" do
98
+ it("must be true for non-empty") { klass.new('bla', ['a']).has_children?.must_equal(true) }
99
+ it("must be false for empty") { klass.new('bla', []).has_children?.must_equal(false) }
100
+ end
101
+
102
+ describe "value is Hash" do
103
+ it("must be true for non-empty") { klass.new('bla', {a: 'b'}).has_children?.must_equal(true) }
104
+ it("must be false for empty") { klass.new('bla', {}).has_children?.must_equal(false) }
105
+ end
106
+
107
+ describe "value is some random class" do
108
+ unless const_defined?("VariableExampleWithInstanceVars")
109
+ class VariableExampleWithInstanceVars; def initialize; @a = '1'; end; end
110
+ end
111
+ unless const_defined?("VariableExampleWithClassVars")
112
+ class VariableExampleWithClassVars; def initialize; @@a = '1'; end; end
113
+ end
114
+ unless const_defined?("VariableExampleWithoutVars")
115
+ class VariableExampleWithoutVars; end
116
+ end
117
+ it("must be true if has instance variables") { klass.new('bla', VariableExampleWithInstanceVars.new).has_children?.must_equal(true) }
118
+ it("must be true if has class variables") { klass.new('bla', VariableExampleWithClassVars.new).has_children?.must_equal(true) }
119
+ it("must be false if has no any variables") { klass.new('bla', VariableExampleWithoutVars.new).has_children?.must_equal(false) }
120
+ it("must be false as a fallback") { klass.new('bla', BasicObject.new).has_children?.must_equal(false) }
121
+ end
122
+ end
123
+
124
+ describe "#value" do
125
+ describe "value is a Array" do
126
+ it("must return string for empty") { klass.new('bla', []).value.must_equal("Empty Array") }
127
+ it("must return result for non-empty") { klass.new('bla', [1, 2]).value.must_equal("Array (2 element(s))") }
128
+ end
129
+
130
+ describe "value is a Hash" do
131
+ it("must return string for empty") { klass.new('bla', {}).value.must_equal("Empty Hash") }
132
+ it("must return result for non-empty") { klass.new('bla', {a: 'b', c: 'd'}).value.must_equal("Hash (2 element(s))") }
133
+ end
134
+
135
+ describe "value is some random class" do
136
+ unless const_defined?("ToSReturnNotAString")
137
+ class ToSReturnNotAString; def to_s; {}; end; end
138
+ end
139
+ it("must return nil for nil") { klass.new('bla', nil).value.must_equal("nil") }
140
+ it("must return #to_s for any other class") do
141
+ klass.new('bla', OpenStruct.new(a: 'b')).value.must_equal '#<OpenStruct a="b">'
142
+ end
143
+ it("must be able to show error") do
144
+ klass.new('bla', BasicObject.new).value.must_match /<raised exception: undefined method/
145
+ end
146
+ it("must get rid of quotes") { klass.new('bla', '"foo"').value.must_equal 'foo' }
147
+ it("must return special message for binary") { klass.new('bla', "\xFF\x12").value.must_equal '[Binary Data]' }
148
+ it("must show error if returned value is not a string") do
149
+ klass.new('bla', ToSReturnNotAString.new).value.must_equal(
150
+ 'ERROR: ToSReturnNotAString.to_s method returns Hash. Should return String.'
151
+ )
152
+ end
153
+ end
154
+ end
155
+
156
+ describe "#id" do
157
+ it "must show object_id" do
158
+ object = Object.new
159
+ klass.new('bla', object).id.must_equal("%#+x" % object.object_id)
160
+ end
161
+
162
+ it "must return nil as a fallback" do
163
+ klass.new('bla', BasicObject.new).id.must_be_nil
164
+ end
165
+ end
166
+
167
+ describe "#type" do
168
+ it "must return class" do
169
+ klass.new('bla', Object.new).type.must_equal(Object.new.class)
170
+ end
171
+
172
+ it "must return 'Undefined' as a callback" do
173
+ klass.new('bla', BasicObject.new).type.must_equal "Undefined"
174
+ end
175
+ end
176
+
177
+ describe "#name" do
178
+ it "must return name as a string" do
179
+ klass.new(:bla, "value").name.must_equal "bla"
180
+ end
181
+ end
182
+
183
+ describe "#to_hash" do
184
+ it "must return a hash with values" do
185
+ var = "foo"
186
+ klass.new(:bla, var).to_hash.must_equal(
187
+ {name: "bla", kind: nil, value: var, type: String, has_children: false, id: "%#+x" % var.object_id}
188
+ )
189
+ end
190
+ end
191
+
192
+ end
193
+ end