vagrantup 0.1.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 (88) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +11 -0
  3. data/Gemfile +17 -0
  4. data/README.md +45 -0
  5. data/Rakefile +41 -0
  6. data/VERSION +1 -0
  7. data/bin/.gitignore +0 -0
  8. data/bin/vagrant +15 -0
  9. data/bin/vagrant-box +35 -0
  10. data/bin/vagrant-down +28 -0
  11. data/bin/vagrant-halt +29 -0
  12. data/bin/vagrant-init +28 -0
  13. data/bin/vagrant-package +30 -0
  14. data/bin/vagrant-reload +30 -0
  15. data/bin/vagrant-resume +28 -0
  16. data/bin/vagrant-ssh +28 -0
  17. data/bin/vagrant-suspend +28 -0
  18. data/bin/vagrant-up +30 -0
  19. data/config/default.rb +29 -0
  20. data/lib/vagrant.rb +14 -0
  21. data/lib/vagrant/actions/base.rb +93 -0
  22. data/lib/vagrant/actions/box/add.rb +22 -0
  23. data/lib/vagrant/actions/box/destroy.rb +14 -0
  24. data/lib/vagrant/actions/box/download.rb +63 -0
  25. data/lib/vagrant/actions/box/unpackage.rb +49 -0
  26. data/lib/vagrant/actions/runner.rb +128 -0
  27. data/lib/vagrant/actions/vm/destroy.rb +14 -0
  28. data/lib/vagrant/actions/vm/down.rb +12 -0
  29. data/lib/vagrant/actions/vm/export.rb +41 -0
  30. data/lib/vagrant/actions/vm/forward_ports.rb +32 -0
  31. data/lib/vagrant/actions/vm/halt.rb +14 -0
  32. data/lib/vagrant/actions/vm/import.rb +17 -0
  33. data/lib/vagrant/actions/vm/move_hard_drive.rb +53 -0
  34. data/lib/vagrant/actions/vm/package.rb +61 -0
  35. data/lib/vagrant/actions/vm/provision.rb +71 -0
  36. data/lib/vagrant/actions/vm/reload.rb +17 -0
  37. data/lib/vagrant/actions/vm/resume.rb +16 -0
  38. data/lib/vagrant/actions/vm/shared_folders.rb +69 -0
  39. data/lib/vagrant/actions/vm/start.rb +50 -0
  40. data/lib/vagrant/actions/vm/suspend.rb +16 -0
  41. data/lib/vagrant/actions/vm/up.rb +35 -0
  42. data/lib/vagrant/box.rb +129 -0
  43. data/lib/vagrant/busy.rb +73 -0
  44. data/lib/vagrant/commands.rb +174 -0
  45. data/lib/vagrant/config.rb +156 -0
  46. data/lib/vagrant/downloaders/base.rb +13 -0
  47. data/lib/vagrant/downloaders/file.rb +21 -0
  48. data/lib/vagrant/downloaders/http.rb +47 -0
  49. data/lib/vagrant/env.rb +140 -0
  50. data/lib/vagrant/ssh.rb +43 -0
  51. data/lib/vagrant/util.rb +45 -0
  52. data/lib/vagrant/vm.rb +57 -0
  53. data/script/vagrant-ssh-expect.sh +22 -0
  54. data/templates/Vagrantfile +8 -0
  55. data/test/test_helper.rb +91 -0
  56. data/test/vagrant/actions/base_test.rb +32 -0
  57. data/test/vagrant/actions/box/add_test.rb +37 -0
  58. data/test/vagrant/actions/box/destroy_test.rb +18 -0
  59. data/test/vagrant/actions/box/download_test.rb +118 -0
  60. data/test/vagrant/actions/box/unpackage_test.rb +101 -0
  61. data/test/vagrant/actions/runner_test.rb +236 -0
  62. data/test/vagrant/actions/vm/destroy_test.rb +24 -0
  63. data/test/vagrant/actions/vm/down_test.rb +32 -0
  64. data/test/vagrant/actions/vm/export_test.rb +88 -0
  65. data/test/vagrant/actions/vm/forward_ports_test.rb +50 -0
  66. data/test/vagrant/actions/vm/halt_test.rb +27 -0
  67. data/test/vagrant/actions/vm/import_test.rb +36 -0
  68. data/test/vagrant/actions/vm/move_hard_drive_test.rb +108 -0
  69. data/test/vagrant/actions/vm/package_test.rb +155 -0
  70. data/test/vagrant/actions/vm/provision_test.rb +103 -0
  71. data/test/vagrant/actions/vm/reload_test.rb +44 -0
  72. data/test/vagrant/actions/vm/resume_test.rb +27 -0
  73. data/test/vagrant/actions/vm/shared_folders_test.rb +117 -0
  74. data/test/vagrant/actions/vm/start_test.rb +55 -0
  75. data/test/vagrant/actions/vm/suspend_test.rb +27 -0
  76. data/test/vagrant/actions/vm/up_test.rb +76 -0
  77. data/test/vagrant/box_test.rb +92 -0
  78. data/test/vagrant/busy_test.rb +81 -0
  79. data/test/vagrant/commands_test.rb +252 -0
  80. data/test/vagrant/config_test.rb +123 -0
  81. data/test/vagrant/downloaders/base_test.rb +20 -0
  82. data/test/vagrant/downloaders/file_test.rb +32 -0
  83. data/test/vagrant/downloaders/http_test.rb +40 -0
  84. data/test/vagrant/env_test.rb +293 -0
  85. data/test/vagrant/ssh_test.rb +95 -0
  86. data/test/vagrant/util_test.rb +64 -0
  87. data/test/vagrant/vm_test.rb +96 -0
  88. metadata +263 -0
@@ -0,0 +1,32 @@
1
+ require File.join(File.dirname(__FILE__), '..', '..', 'test_helper')
2
+
3
+ class BaseActionTest < Test::Unit::TestCase
4
+ should "include the util class so subclasses have access to it" do
5
+ assert Vagrant::Actions::Base.include?(Vagrant::Util)
6
+ end
7
+
8
+ context "base instance" do
9
+ setup do
10
+ @mock_vm = mock("vm")
11
+ @base = Vagrant::Actions::Base.new(@mock_vm)
12
+ end
13
+
14
+ should "allow read-only access to the runner" do
15
+ assert_equal @mock_vm, @base.runner
16
+ end
17
+
18
+ should "implement prepare which does nothing" do
19
+ assert_nothing_raised do
20
+ assert @base.respond_to?(:prepare)
21
+ @base.prepare
22
+ end
23
+ end
24
+
25
+ should "implement the execute! method which does nothing" do
26
+ assert_nothing_raised do
27
+ assert @base.respond_to?(:execute!)
28
+ @base.execute!
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,37 @@
1
+ require File.join(File.dirname(__FILE__), '..', '..', '..', 'test_helper')
2
+
3
+ class AddBoxActionTest < Test::Unit::TestCase
4
+ setup do
5
+ @runner, @vm, @action = mock_action(Vagrant::Actions::Box::Add)
6
+ mock_config
7
+ end
8
+
9
+ context "prepare" do
10
+ setup do
11
+ @default_order = [Vagrant::Actions::Box::Download, Vagrant::Actions::Box::Unpackage]
12
+ @runner.stubs(:directory).returns("foo")
13
+ File.stubs(:exists?).returns(false)
14
+ end
15
+
16
+ def setup_action_expectations
17
+ default_seq = sequence("default_seq")
18
+ @default_order.each do |action|
19
+ @runner.expects(:add_action).with(action).once.in_sequence(default_seq)
20
+ end
21
+ end
22
+
23
+ should "setup the proper sequence of actions" do
24
+ setup_action_expectations
25
+ @action.prepare
26
+ end
27
+
28
+ should "result in an action exception if the box already exists" do
29
+ File.expects(:exists?).once.returns(true)
30
+ @runner.expects(:name).twice.returns('foo')
31
+ @runner.expects(:add_action).never
32
+ assert_raise Vagrant::Actions::ActionException do
33
+ @action.prepare
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,18 @@
1
+ require File.join(File.dirname(__FILE__), '..', '..', '..', 'test_helper')
2
+
3
+ class DestroyBoxActionTest < Test::Unit::TestCase
4
+ setup do
5
+ @name = "foo"
6
+ @dir = "foo"
7
+ @runner, @vm, @action = mock_action(Vagrant::Actions::Box::Destroy)
8
+ @runner.stubs(:directory).returns(@dir)
9
+ mock_config
10
+ end
11
+
12
+ context "executing" do
13
+ should "rm_rf the directory" do
14
+ FileUtils.expects(:rm_rf).with(@dir).once
15
+ @action.execute!
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,118 @@
1
+ require File.join(File.dirname(__FILE__), '..', '..', '..', 'test_helper')
2
+
3
+ class DownloadBoxActionTest < Test::Unit::TestCase
4
+ setup do
5
+ @uri = "foo.com"
6
+ @runner, @vm, @action = mock_action(Vagrant::Actions::Box::Download)
7
+ @runner.stubs(:uri).returns(@uri)
8
+ @runner.stubs(:temp_path=)
9
+ mock_config
10
+
11
+ Vagrant::Env.stubs(:tmp_path).returns("foo")
12
+ end
13
+
14
+ context "preparing" do
15
+ setup do
16
+ @uri = mock("uri")
17
+ @uri.stubs(:is_a?).returns(false)
18
+ URI.stubs(:parse).returns(@uri)
19
+ end
20
+
21
+ should "raise an exception if no URI type is matched" do
22
+ @uri.stubs(:is_a?).returns(false)
23
+ assert_raises(Vagrant::Actions::ActionException) {
24
+ @action.prepare
25
+ }
26
+ end
27
+
28
+ should "set the downloader to file if URI is generic" do
29
+ @uri.stubs(:is_a?).with(URI::Generic).returns(true)
30
+ @action.prepare
31
+ assert @action.downloader.is_a?(Vagrant::Downloaders::File)
32
+ end
33
+
34
+ should "set the downloader to HTTP if URI is HTTP" do
35
+ @uri.stubs(:is_a?).with(URI::HTTP).returns(true)
36
+ @action.prepare
37
+ assert @action.downloader.is_a?(Vagrant::Downloaders::HTTP)
38
+ end
39
+ end
40
+
41
+ context "executing" do
42
+ setup do
43
+ @path = "foo"
44
+
45
+ @tempfile = mock("tempfile")
46
+ @tempfile.stubs(:path).returns(@path)
47
+
48
+ @action.stubs(:with_tempfile).yields(@tempfile)
49
+ @action.stubs(:download_to)
50
+ end
51
+
52
+ should "make a tempfile and copy the URI contents to it" do
53
+ @action.expects(:with_tempfile).yields(@tempfile)
54
+ @action.expects(:download_to).with(@tempfile)
55
+ @action.execute!
56
+ end
57
+
58
+ should "save the tempfile path" do
59
+ @runner.expects(:temp_path=).with(@path).once
60
+ @action.execute!
61
+ end
62
+ end
63
+
64
+ context "rescue" do
65
+ should "call cleanup method" do
66
+ @action.expects(:cleanup).once
67
+ @action.rescue(nil)
68
+ end
69
+ end
70
+
71
+ context "tempfile" do
72
+ should "create a tempfile in the vagrant tmp directory" do
73
+ Tempfile.expects(:open).with(Vagrant::Actions::Box::Download::BASENAME, Vagrant::Env.tmp_path).once
74
+ @action.with_tempfile
75
+ end
76
+
77
+ should "yield the tempfile object" do
78
+ @tempfile = mock("tempfile")
79
+ Tempfile.expects(:open).yields(@tempfile)
80
+
81
+ @action.with_tempfile do |otherfile|
82
+ assert @tempfile.equal?(otherfile)
83
+ end
84
+ end
85
+ end
86
+
87
+ context "cleaning up" do
88
+ setup do
89
+ @temp_path = "foo"
90
+ @runner.stubs(:temp_path).returns(@temp_path)
91
+ File.stubs(:exist?).returns(true)
92
+ end
93
+
94
+ should "delete the temporary file if it exists" do
95
+ File.expects(:unlink).with(@temp_path).once
96
+ @action.cleanup
97
+ end
98
+
99
+ should "not delete anything if it doesn't exist" do
100
+ File.stubs(:exist?).returns(false)
101
+ File.expects(:unlink).never
102
+ @action.cleanup
103
+ end
104
+ end
105
+
106
+ context "downloading" do
107
+ setup do
108
+ @downloader = mock("downloader")
109
+ @action.stubs(:downloader).returns(@downloader)
110
+ end
111
+
112
+ should "call download! on the download with the URI and tempfile" do
113
+ tempfile = "foo"
114
+ @downloader.expects(:download!).with(@runner.uri, tempfile)
115
+ @action.download_to(tempfile)
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,101 @@
1
+ require File.join(File.dirname(__FILE__), '..', '..', '..', 'test_helper')
2
+
3
+ class UnpackageBoxActionTest < Test::Unit::TestCase
4
+ setup do
5
+ @runner, @vm, @action = mock_action(Vagrant::Actions::Box::Unpackage)
6
+ @runner.stubs(:name).returns("foo")
7
+ @runner.stubs(:temp_path).returns("bar")
8
+ mock_config
9
+
10
+ Vagrant::Env.stubs(:boxes_path).returns("bar")
11
+ end
12
+
13
+ context "executing" do
14
+ setup do
15
+ @runner.stubs(:invoke_around_callback).yields
16
+ end
17
+
18
+ should "execute the proper actions in the proper order" do
19
+ exec_seq = sequence("exec_seq")
20
+ @action.expects(:setup_box_dir).in_sequence(exec_seq)
21
+ @action.expects(:decompress).in_sequence(exec_seq)
22
+ @action.execute!
23
+ end
24
+
25
+ should "execute it in a around block" do
26
+ @runner.expects(:invoke_around_callback).with(:unpackage).once
27
+ @action.execute!
28
+ end
29
+ end
30
+
31
+ context "rescuing" do
32
+ setup do
33
+ File.stubs(:directory?).returns(false)
34
+ FileUtils.stubs(:rm_rf)
35
+
36
+ @box_dir = mock("foo")
37
+ @action.stubs(:box_dir).returns(@box_dir)
38
+ end
39
+
40
+ should "do nothing if a directory doesn't exist" do
41
+ FileUtils.expects(:rm_rf).never
42
+ @action.rescue(nil)
43
+ end
44
+
45
+ should "remove the box directory if it exists" do
46
+ File.expects(:directory?).returns(true)
47
+ FileUtils.expects(:rm_rf).with(@box_dir).once
48
+ @action.rescue(nil)
49
+ end
50
+ end
51
+
52
+ context "box directory" do
53
+ should "return the runner directory" do
54
+ result = mock("object")
55
+ @runner.expects(:directory).once.returns(result)
56
+ assert result.equal?(@action.box_dir)
57
+ end
58
+ end
59
+
60
+ context "setting up the box directory" do
61
+ setup do
62
+ File.stubs(:directory?).returns(false)
63
+ FileUtils.stubs(:mkdir_p)
64
+
65
+ @box_dir = "foo"
66
+ @action.stubs(:box_dir).returns(@box_dir)
67
+ end
68
+
69
+ should "error and exit if the directory exists" do
70
+ File.expects(:directory?).returns(true)
71
+ @action.expects(:error_and_exit).once
72
+ @action.setup_box_dir
73
+ end
74
+
75
+ should "create the directory" do
76
+ FileUtils.expects(:mkdir_p).with(@box_dir).once
77
+ @action.setup_box_dir
78
+ end
79
+ end
80
+
81
+ context "decompressing" do
82
+ setup do
83
+ @box_dir = "foo"
84
+
85
+ @action.stubs(:box_dir).returns(@box_dir)
86
+ Dir.stubs(:chdir).yields
87
+ end
88
+
89
+ should "change to the box directory" do
90
+ Dir.expects(:chdir).with(@box_dir)
91
+ @action.decompress
92
+ end
93
+
94
+ should "open the tar file within the new directory, and extract it all" do
95
+ @tar = mock("tar")
96
+ @tar.expects(:extract_all).once
97
+ Tar.expects(:open).with(@runner.temp_path, anything, anything, anything).yields(@tar)
98
+ @action.decompress
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,236 @@
1
+ require File.join(File.dirname(__FILE__), '..', '..', 'test_helper')
2
+
3
+ class ActionRunnerTest < Test::Unit::TestCase
4
+ def mock_fake_action
5
+ action = mock("action")
6
+ action.stubs(:prepare)
7
+ action.stubs(:execute!)
8
+ action.stubs(:cleanup)
9
+ action
10
+ end
11
+
12
+ context "callbacks" do
13
+ setup do
14
+ @runner = Vagrant::Actions::Runner.new
15
+ end
16
+
17
+ context "around callbacks" do
18
+ should "invoke before/after_name for around callbacks" do
19
+ block_obj = mock("block_obj")
20
+ around_seq = sequence("around_seq")
21
+ @runner.expects(:invoke_callback).with(:before_foo).once.in_sequence(around_seq)
22
+ block_obj.expects(:foo).once.in_sequence(around_seq)
23
+ @runner.expects(:invoke_callback).with(:after_foo).once.in_sequence(around_seq)
24
+
25
+ @runner.invoke_around_callback(:foo) do
26
+ block_obj.foo
27
+ end
28
+ end
29
+
30
+ should "forward arguments to invoke_callback" do
31
+ @runner.expects(:invoke_callback).with(:before_foo, "foo").once
32
+ @runner.expects(:invoke_callback).with(:after_foo, "foo").once
33
+ @runner.invoke_around_callback(:foo, "foo") do; end
34
+ end
35
+ end
36
+
37
+ should "not invoke callback on actions which don't respond to it" do
38
+ action = mock("action")
39
+ action.stubs(:respond_to?).with(:foo).returns(false)
40
+ action.expects(:foo).never
41
+
42
+ assert_nothing_raised do
43
+ @runner.actions << action
44
+ @runner.invoke_callback(:foo)
45
+ end
46
+ end
47
+
48
+ should "invoke callback on actions which do respond to the method" do
49
+ action = mock("action")
50
+ action.expects(:foo).once
51
+
52
+ @runner.actions << action
53
+ @runner.invoke_callback(:foo)
54
+ end
55
+
56
+ should "collect all the results and return them as an array" do
57
+ result = []
58
+ 3.times do |i|
59
+ action = mock("action#{i}")
60
+ action.expects(:foo).returns("foo#{i}").once
61
+
62
+ @runner.actions << action
63
+ result << "foo#{i}"
64
+ end
65
+
66
+ assert_equal result, @runner.invoke_callback(:foo)
67
+ end
68
+ end
69
+
70
+ context "finding actions" do
71
+ setup do
72
+ @runner = Vagrant::Actions::Runner.new
73
+ end
74
+
75
+ should "return nil if the action could not be found" do
76
+ assert_nil @runner.find_action(Vagrant::Actions::VM::Export)
77
+ end
78
+
79
+ should "return the first instance of the action found" do
80
+ @runner.add_action(Vagrant::Actions::VM::Export)
81
+ @runner.add_action(Vagrant::Actions::VM::Export)
82
+
83
+ assert @runner.actions[0].equal?(@runner.find_action(Vagrant::Actions::VM::Export))
84
+ end
85
+ end
86
+
87
+ context "adding actions" do
88
+ setup do
89
+ @runner = Vagrant::Actions::Runner.new
90
+ end
91
+
92
+ should "initialize the action when added" do
93
+ action_klass = mock("action_class")
94
+ action_inst = mock("action_inst")
95
+ action_klass.expects(:new).once.returns(action_inst)
96
+ @runner.add_action(action_klass)
97
+ assert_equal 1, @runner.actions.length
98
+ end
99
+
100
+ should "initialize the action with given arguments when added" do
101
+ action_klass = mock("action_class")
102
+ action_klass.expects(:new).with(@runner, "foo", "bar").once
103
+ @runner.add_action(action_klass, "foo", "bar")
104
+ end
105
+ end
106
+
107
+ context "class method execute" do
108
+ should "run actions on class method execute!" do
109
+ vm = mock("vm")
110
+ execute_seq = sequence("execute_seq")
111
+ Vagrant::Actions::Runner.expects(:new).returns(vm).in_sequence(execute_seq)
112
+ vm.expects(:add_action).with("foo").in_sequence(execute_seq)
113
+ vm.expects(:execute!).once.in_sequence(execute_seq)
114
+
115
+ Vagrant::Actions::Runner.execute!("foo")
116
+ end
117
+
118
+ should "forward arguments to add_action on class method execute!" do
119
+ vm = mock("vm")
120
+ execute_seq = sequence("execute_seq")
121
+ Vagrant::Actions::Runner.expects(:new).returns(vm).in_sequence(execute_seq)
122
+ vm.expects(:add_action).with("foo", "bar", "baz").in_sequence(execute_seq)
123
+ vm.expects(:execute!).once.in_sequence(execute_seq)
124
+
125
+ Vagrant::Actions::Runner.execute!("foo", "bar", "baz")
126
+ end
127
+ end
128
+
129
+ context "instance method execute" do
130
+ setup do
131
+ @runner = Vagrant::Actions::Runner.new
132
+ end
133
+
134
+ should "clear the actions and run a single action if given to execute!" do
135
+ action = mock("action")
136
+ run_action = mock("action_run")
137
+ run_class = mock("run_class")
138
+ run_class.expects(:new).once.returns(run_action)
139
+ @runner.actions << action
140
+
141
+ [:prepare, :execute!, :cleanup].each do |method|
142
+ action.expects(method).never
143
+ run_action.expects(method).once
144
+ end
145
+
146
+ @runner.execute!(run_class)
147
+ end
148
+
149
+ should "clear actions after running execute!" do
150
+ @runner.actions << mock_fake_action
151
+ @runner.actions << mock_fake_action
152
+ assert !@runner.actions.empty? # sanity
153
+ @runner.execute!
154
+ assert @runner.actions.empty?
155
+ end
156
+
157
+ should "run #prepare on all actions, then #execute!" do
158
+ action_seq = sequence("action_seq")
159
+ actions = []
160
+ 5.times do |i|
161
+ action = mock("action#{i}")
162
+
163
+ @runner.actions << action
164
+ actions << action
165
+ end
166
+
167
+ [:prepare, :execute!, :cleanup].each do |method|
168
+ actions.each do |action|
169
+ action.expects(method).once.in_sequence(action_seq)
170
+ end
171
+ end
172
+
173
+ @runner.execute!
174
+ end
175
+
176
+ context "exceptions" do
177
+ setup do
178
+ @actions = [mock_fake_action, mock_fake_action]
179
+ @actions.each do |a|
180
+ a.stubs(:rescue)
181
+ @runner.actions << a
182
+ end
183
+
184
+ @exception = Exception.new
185
+ end
186
+
187
+ should "call #rescue on each action if an exception is raised during execute!" do
188
+ @actions.each do |a|
189
+ a.expects(:rescue).with(@exception).once
190
+ end
191
+
192
+ @actions[0].stubs(:execute!).raises(@exception)
193
+
194
+ @runner.expects(:error_and_exit).never
195
+ assert_raises(Exception) { @runner.execute! }
196
+ end
197
+
198
+ should "call #rescue on each action if an exception is raised during prepare" do
199
+ @actions.each do |a|
200
+ a.expects(:rescue).with(@exception).once
201
+ end
202
+
203
+ @actions[0].stubs(:prepare).raises(@exception)
204
+
205
+ @runner.expects(:error_and_exit).never
206
+ assert_raises(Exception) { @runner.execute! }
207
+ end
208
+
209
+ should "call error_and_exit if it is an ActionException" do
210
+ msg = "Message"
211
+ @exception = Vagrant::Actions::ActionException.new(msg)
212
+ @actions[0].stubs(:prepare).raises(@exception)
213
+
214
+ @runner.expects(:error_and_exit).with(msg).once
215
+ @runner.execute!
216
+ end
217
+ end
218
+ end
219
+
220
+ context "actions" do
221
+ setup do
222
+ @runner = Vagrant::Actions::Runner.new
223
+ end
224
+
225
+ should "setup actions to be an array" do
226
+ assert_nil @runner.instance_variable_get(:@actions)
227
+ actions = @runner.actions
228
+ assert actions.is_a?(Array)
229
+ assert actions.equal?(@runner.actions)
230
+ end
231
+
232
+ should "be empty initially" do
233
+ assert @runner.actions.empty?
234
+ end
235
+ end
236
+ end