skadategems-dev 0.0.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +16 -0
  3. data/.travis.yml +12 -0
  4. data/Gemfile +13 -0
  5. data/Guardfile +8 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +74 -0
  8. data/Rakefile +6 -0
  9. data/bin/skadate +9 -0
  10. data/bin/skadategems-dev +18 -0
  11. data/integration/README.md +97 -0
  12. data/integration/cli/skadate_init_spec.rb +86 -0
  13. data/integration/cli/skadate_spec.rb +50 -0
  14. data/integration/cli/spec_helper.rb +23 -0
  15. data/integration/lib/remote/download_file_spec.rb +136 -0
  16. data/integration/spec_helper.rb +101 -0
  17. data/lib/skadategems/dev/bundle.rb +115 -0
  18. data/lib/skadategems/dev/cli/remote.rb +364 -0
  19. data/lib/skadategems/dev/cli/skadate.rb +184 -0
  20. data/lib/skadategems/dev/remote.rb +170 -0
  21. data/lib/skadategems/dev/remote/clone_logger.rb +118 -0
  22. data/lib/skadategems/dev/remote/configs.rb +97 -0
  23. data/lib/skadategems/dev/remote/directory.rb +88 -0
  24. data/lib/skadategems/dev/remote/file.rb +157 -0
  25. data/lib/skadategems/dev/remote/includes/mysql_connect.php +8 -0
  26. data/lib/skadategems/dev/skadate.rb +30 -0
  27. data/lib/skadategems/dev/software_version.rb +66 -0
  28. data/lib/skadategems/dev/source_controller.rb +101 -0
  29. data/lib/skadategems/dev/version.rb +5 -0
  30. data/skadategems-dev.gemspec +33 -0
  31. data/spec/skadategems/dev/bundle_spec.rb +268 -0
  32. data/spec/skadategems/dev/cli/remote_spec.rb +623 -0
  33. data/spec/skadategems/dev/cli/skadate_spec.rb +182 -0
  34. data/spec/skadategems/dev/remote/clone_logger_spec.rb +324 -0
  35. data/spec/skadategems/dev/remote/directory_spec.rb +117 -0
  36. data/spec/skadategems/dev/remote/file_spec.rb +74 -0
  37. data/spec/skadategems/dev/remote_spec.rb +95 -0
  38. data/spec/skadategems/dev/skadate_spec.rb +62 -0
  39. data/spec/skadategems/dev/software_version_spec.rb +92 -0
  40. data/spec/skadategems/dev/source_controller_spec.rb +243 -0
  41. data/spec/spec_helper.rb +32 -0
  42. metadata +153 -0
@@ -0,0 +1,117 @@
1
+ require 'spec_helper'
2
+ require 'skadategems/dev/remote/directory'
3
+
4
+ module SkadateGems::Dev
5
+ class Remote
6
+
7
+ describe Directory do
8
+ let(:remote) { double Remote, exec: nil }
9
+
10
+ describe '#initialize' do
11
+ let(:remote_dir) { Directory.new(remote, '/', 2244) }
12
+
13
+ it 'assigns a given @path value' do
14
+ expect(remote_dir.path).to eq '/'
15
+ end
16
+
17
+ it 'assigns a given @size value' do
18
+ expect(remote_dir.size).to eq 2244
19
+ end
20
+ end
21
+
22
+ describe '#basename' do
23
+ let(:remote_dir) do
24
+ Directory.new(remote, '/layout/themes/dp', 2244)
25
+ end
26
+
27
+ it 'returns the base name of a file' do
28
+ expect(remote_dir.basename).to eq 'dp'
29
+ end
30
+ end
31
+
32
+ describe '#list' do
33
+ let(:remote_dir) { Directory.new(remote, '/', 0) }
34
+ let(:script_batch) { double 'script batch', :<< => nil }
35
+ let(:response_body) { '[]' }
36
+ let(:response) { double 'http response',
37
+ :code => 200,
38
+ :message => 'OK',
39
+ :body => response_body }
40
+
41
+ before(:each) do
42
+ allow(remote).to receive(:exec)
43
+ .and_yield(script_batch)
44
+ .and_return(response)
45
+ end
46
+
47
+ [
48
+ [ '/', '' ],
49
+ [ '/admin', 'admin' ],
50
+ [ '/admin/templates', 'admin/templates' ],
51
+ [ 'admin/templates/', 'admin/templates' ],
52
+ [ 'admin/templates', 'admin/templates' ],
53
+ [ '/admin/templates/', 'admin/templates' ]
54
+ ].each do |entry|
55
+ context "for `#{entry[0]}`" do
56
+ let(:remote_dir) { Directory.new(remote, entry[0], 0) }
57
+
58
+ it "requests dir(DIR_SITE_ROOT . '#{entry[1]}')" do
59
+ allow(script_batch).to receive(:<<) do |script|
60
+ expect(script).to include <<-PHPSCRIPT
61
+ $dir = dir(DIR_SITE_ROOT . '#{entry[1]}');
62
+ PHPSCRIPT
63
+ end
64
+
65
+ remote_dir.list
66
+ end
67
+ end
68
+ end
69
+
70
+ context 'when a response is received' do
71
+ it 'parses it‘s body as a json' do
72
+ expect(JSON).to receive(:parse).with('[]').and_return []
73
+ expect(remote_dir.list).to eq []
74
+ end
75
+
76
+ context 'when the resulting json contains directory' do
77
+ let(:response_body) { '[ ["admin", "d", 3910] ]' }
78
+
79
+ it 'initializes it as a Remote::Directory instance' do
80
+ list_entry = remote_dir.list[0]
81
+
82
+ expect(list_entry).to be_a Directory
83
+ expect(list_entry.path).to eq '/admin'
84
+ expect(list_entry.size).to eq 3910
85
+ end
86
+ end
87
+
88
+ context 'when the resulting json contains a file' do
89
+ let(:response_body) { '[ ["index.php", "f", 204] ]' }
90
+
91
+ it 'initializes it as a Remote::File instance' do
92
+ list_entry = remote_dir.list[0]
93
+ expect(list_entry).to be_a(File)
94
+ expect(list_entry).not_to be_directory
95
+
96
+ expect(list_entry.path).to eq '/index.php'
97
+ expect(list_entry.size).to eq 204
98
+ end
99
+ end
100
+
101
+ context 'when the resulting json is a combination of directories and files' do
102
+ let(:response_body) do
103
+ '[ ["index.php", "f", 204], ["admin", "d", 3910], ["logo.jpg", "f", 13954] ]'
104
+ end
105
+
106
+ it 'orders the resulting @list of files by their `basename`' do
107
+ names = remote_dir.list.map(&:basename)
108
+ expect(names).to eq %w[admin index.php logo.jpg]
109
+ end
110
+ end
111
+ end
112
+
113
+ end
114
+ end
115
+
116
+ end
117
+ end
@@ -0,0 +1,74 @@
1
+ require 'spec_helper'
2
+ require 'skadategems/dev/remote/file'
3
+
4
+ module SkadateGems::Dev
5
+ describe Remote::File do
6
+ let(:remote) { double Remote, :exec => nil }
7
+
8
+ describe '#initialize' do
9
+ let(:remote_file) { Remote::File.new(remote, '/index.php', 204) }
10
+
11
+ it 'assigns @path' do
12
+ expect(remote_file.path).to eq '/index.php'
13
+ end
14
+
15
+ it 'assigns @size' do
16
+ expect(remote_file.size).to eq 204
17
+ end
18
+ end
19
+
20
+ describe '#basename' do
21
+ let(:remote_file) do
22
+ Remote::File.new(remote, '/internals/API/Layout.class.php', 2244)
23
+ end
24
+
25
+ it 'returns the base name of a file' do
26
+ expect(remote_file.basename).to eq 'Layout.class.php'
27
+ end
28
+ end
29
+
30
+ describe '#extname' do
31
+ let(:remote_file) do
32
+ Remote::File.new(remote, '/internals/API/Layout.class.php', 2244)
33
+ end
34
+
35
+ it 'returns the extension of a file' do
36
+ expect(remote_file.extname).to eq '.php'
37
+ end
38
+ end
39
+
40
+ describe '#friendly_size' do
41
+ let(:size) { nil }
42
+ let(:remote_file) { Remote::File.new(remote, '/image.png', size) }
43
+
44
+ subject { remote_file.friendly_size }
45
+
46
+ [ [0, '0 B'],
47
+
48
+ [512, '512 B'],
49
+ [513, '0.5 KiB'],
50
+ [767, '0.7 KiB'],
51
+
52
+ [1024, '1 KiB'],
53
+ [(1024 * 512), '512 KiB'],
54
+ [(1024 * 513), '0.5 MiB'],
55
+
56
+ [(1024 * 1024), '1 MiB'],
57
+ [(1024 * 1024 * 512), '512 MiB'],
58
+ [(1024 * 1024 * 513), '0.5 GiB'] ].each do |entry|
59
+ context "when @size = #{entry[0]}" do
60
+ let(:size) { entry[0] }
61
+ it { should eq entry[1] }
62
+ end
63
+ end
64
+ end
65
+
66
+ describe '#request' do
67
+ # @see integration/lib/remote/download_file_spec.rb
68
+ end
69
+
70
+ describe '#save_as' do
71
+ # ...
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,95 @@
1
+ require 'spec_helper'
2
+ require 'skadategems/dev/skadate'
3
+
4
+ module SkadateGems
5
+ module Dev
6
+
7
+ describe Remote do
8
+
9
+ let(:accessor) do
10
+ double ExecPHP::RemoteServer,
11
+ execphp_uri: 'http://localhost/exec.php',
12
+ exec: nil
13
+ end
14
+
15
+ let(:remote) do
16
+ allow(ExecPHP::RemoteServer)
17
+ .to receive(:from_file)
18
+ .with('.dev/remote-server.json')
19
+ .and_return(accessor)
20
+
21
+ Remote.new('.dev/remote-server.json')
22
+ end
23
+
24
+ describe '#initialize' do
25
+ it 'instantiates a ExecPHP::RemoteServer' do
26
+ expect(remote.accessor).to equal(accessor)
27
+ end
28
+ end
29
+
30
+ describe '#directory' do
31
+ it 'returns a Remote::Directory with a given path' do
32
+ remote_dir = remote.directory '/internals'
33
+
34
+ expect(remote_dir).to be_a Remote::Directory
35
+ expect(remote_dir.path).to eq '/internals'
36
+ end
37
+ end
38
+
39
+ describe '#root' do
40
+ it 'returns a root Remote::Directory' do
41
+ root = remote.root
42
+
43
+ expect(root).to be_a Remote::Directory
44
+ expect(root.path).to eq '/'
45
+ end
46
+ end
47
+
48
+ # ...
49
+
50
+ describe '#userfiles' do
51
+ let(:response) do
52
+ double 'http response', code: '500',
53
+ message: 'Internal Server Error'
54
+ end
55
+
56
+ before(:each) do
57
+ allow(accessor).to receive(:exec).and_return(response)
58
+ end
59
+
60
+ context 'for `/$userfiles` case' do
61
+ let(:response) do
62
+ double 'http response', code: '200',
63
+ message: 'OK',
64
+ body: '/$userfiles'
65
+ end
66
+
67
+ it "returns a Remote::Directory(path: '/$userfiles')" do
68
+ userfiles = remote.userfiles
69
+
70
+ expect(userfiles).to be_a Remote::Directory
71
+ expect(userfiles.path).to eq '/$userfiles'
72
+ end
73
+ end
74
+
75
+ context 'for `/$userfiles` case' do
76
+ let(:response) do
77
+ double 'http response', code: '200',
78
+ message: 'OK',
79
+ body: '/userfiles'
80
+ end
81
+
82
+ it "returns a Remote::Directory(path: '/userfiles')" do
83
+ userfiles = remote.userfiles
84
+
85
+ expect(userfiles).to be_a Remote::Directory
86
+ expect(userfiles.path).to eq '/userfiles'
87
+ end
88
+ end
89
+
90
+ end
91
+
92
+ end
93
+
94
+ end
95
+ end
@@ -0,0 +1,62 @@
1
+ require 'spec_helper'
2
+ require 'skadategems/dev/skadate'
3
+
4
+ module SkadateGems
5
+ module Dev
6
+
7
+ describe Skadate do
8
+ let(:skadate) { Skadate.new('/path/to/skadate') }
9
+
10
+ describe '#initialize' do
11
+ it 'assigns @root_dir' do
12
+ expect(skadate.root_dir).to eq '/path/to/skadate'
13
+ end
14
+ end
15
+
16
+ describe '#source' do
17
+ let(:source) { double(SourceController) }
18
+
19
+ it 'initializes a new source controller' do
20
+ expect(SourceController)
21
+ .to receive(:new)
22
+ .with('/path/to/skadate')
23
+
24
+ skadate.source
25
+ end
26
+
27
+ it 'always returns link to a single object' do
28
+ expect(SourceController)
29
+ .to receive(:new)
30
+ .once
31
+ .with('/path/to/skadate')
32
+ .and_return(source)
33
+ .and_return(source)
34
+
35
+ expect(skadate.source).to equal(skadate.source)
36
+ end
37
+ end
38
+
39
+ describe '#remote' do
40
+ let(:remote) { double(Remote) }
41
+
42
+ it 'initializes a remote accessor using the `remote-server.json` file' do
43
+ expect(Remote).to receive(:new)
44
+ .with('/path/to/skadate/.dev/remote-server.json')
45
+ .and_return(remote)
46
+
47
+ expect(skadate.remote).to equal(remote)
48
+ end
49
+
50
+ it 'always returns link to a single object' do
51
+ expect(Remote).to receive(:new)
52
+ .once
53
+ .with('/path/to/skadate/.dev/remote-server.json')
54
+ .and_return(remote)
55
+
56
+ expect(skadate.remote).to equal(remote)
57
+ end
58
+ end
59
+ end
60
+
61
+ end
62
+ end
@@ -0,0 +1,92 @@
1
+ require 'spec_helper'
2
+ require 'skadategems/dev/software_version'
3
+
4
+ module SkadateGems
5
+ module Dev
6
+
7
+ describe SoftwareVersion do
8
+ let(:version) { SoftwareVersion.new('9.2.2960') }
9
+
10
+ describe '#initialize' do
11
+ it 'assigns @major' do
12
+ expect(version.major).to eq 9
13
+ end
14
+
15
+ it 'assigns @minor' do
16
+ expect(version.minor).to eq 2
17
+ end
18
+
19
+ it 'assigns @build' do
20
+ expect(version.build).to eq 2960
21
+ end
22
+ end
23
+
24
+ describe '#to_s' do
25
+ it 'returns string version representation' do
26
+ expect(version.to_s).to eq '9.2.2960'
27
+ end
28
+ end
29
+
30
+ describe '#==(other)' do
31
+ let(:version_a) { SoftwareVersion.new('9.2.2960') }
32
+
33
+ context 'when comparing with a string' do
34
+ context 'the same versions' do
35
+ let(:version_b) { '9.2.2960' }
36
+
37
+ it 'are equal' do
38
+ expect(version_a).to eq(version_b)
39
+ end
40
+ end
41
+
42
+ context 'a different versions' do
43
+ let(:version_b) { '9.0.2500' }
44
+
45
+ it 'are not equal' do
46
+ expect(version_a).to_not eq(version_b)
47
+ end
48
+ end
49
+ end
50
+
51
+ context 'when comparing with other version object' do
52
+ context 'the same versions' do
53
+ let(:version_b) { SoftwareVersion.new('9.2.2960') }
54
+
55
+ it 'are equal' do
56
+ expect(version_a).to eq(version_b)
57
+ end
58
+ end
59
+
60
+ context 'a different versions' do
61
+ let(:version_b) { SoftwareVersion.new('9.0.2500') }
62
+
63
+ it 'are not equal' do
64
+ expect(version_a).to_not eq(version_b)
65
+ end
66
+ end
67
+ end
68
+ end
69
+
70
+ describe '#supported?' do
71
+
72
+ context 'for supported version' do
73
+ let(:version) { SoftwareVersion.new('9.2.2960') }
74
+
75
+ it 'returns true' do
76
+ expect(version).to be_supported
77
+ end
78
+ end
79
+
80
+ context 'for unknown version' do
81
+ let(:version) { SoftwareVersion.new('3.2.123') }
82
+
83
+ it 'returns false' do
84
+ expect(version).not_to be_supported
85
+ end
86
+ end
87
+
88
+ end
89
+ end
90
+
91
+ end
92
+ end
@@ -0,0 +1,243 @@
1
+ require 'spec_helper'
2
+ require 'skadategems/dev/source_controller'
3
+
4
+ module SkadateGems
5
+ module Dev
6
+
7
+ describe SourceController do
8
+ let(:source) { SourceController.new('/path/to/skadate') }
9
+
10
+ describe '#initialize' do
11
+ it 'assigns @root' do
12
+ expect(source.root).to eq '/path/to/skadate'
13
+ end
14
+ end
15
+
16
+ describe '#filename' do
17
+ it 'returns a full path to a file' do
18
+ expect(source.filename('index.php')).to eq '/path/to/skadate/index.php'
19
+ end
20
+ end
21
+
22
+ describe '#version' do
23
+ context 'when the `#header_inc_php` file can not be found' do
24
+ before(:each) do
25
+ expect(source).to receive(:parse_version_definition).and_raise(Errno::ENOENT)
26
+ end
27
+
28
+ it "doesn't raise an error" do
29
+ expect { source.version }.to_not raise_error
30
+ end
31
+
32
+ it 'returns nil' do
33
+ expect(source.version).to be_nil
34
+ end
35
+ end
36
+
37
+ context 'when the version definition can not be parsed' do
38
+ before(:each) do
39
+ expect(source).to receive(:parse_version_definition).and_raise(SourceController::ParseError)
40
+ end
41
+
42
+ it "doesn't raise an error" do
43
+ expect { source.version }.to_not raise_error
44
+ end
45
+
46
+ it 'returns nil' do
47
+ expect(source.version).to be_nil
48
+ end
49
+ end
50
+
51
+ context 'when the `#header_inc_php` file is ok' do
52
+
53
+ context 'for supported version' do
54
+ before(:each) do
55
+ expect(source).to receive(:parse_version_definition).and_return('9.0.2500')
56
+ end
57
+
58
+ it 'returns a software version object' do
59
+ expect(source.version).to equal SoftwareVersion::MAP['9.0.2500']
60
+ end
61
+
62
+ it 'caches a returned value' do
63
+ source.version
64
+ expect(source).to_not receive(:parse_version_definition)
65
+ expect(source.version).to equal SoftwareVersion::MAP['9.0.2500']
66
+ end
67
+ end
68
+
69
+ context 'for unsupported version' do
70
+ before(:each) do
71
+ expect(source).to receive(:parse_version_definition).and_return('3.2.123')
72
+ end
73
+
74
+ it 'initializes a new software version object' do
75
+ version = source.version
76
+ expect(version).to be_a SoftwareVersion
77
+ expect(version).to eq '3.2.123'
78
+ end
79
+
80
+ it 'caches a returned value' do
81
+ source.version
82
+ expect(source).to_not receive(:parse_version_definition)
83
+ expect(source.version).to eq '3.2.123'
84
+ end
85
+ end
86
+ end
87
+ end
88
+
89
+ describe '#version!' do
90
+ context 'when the `#header_inc_php` file can not be found' do
91
+ before(:each) do
92
+ expect(source).to receive(:parse_version_definition).and_raise(Errno::ENOENT)
93
+ end
94
+
95
+ it 'raises an Errno::ENOENT' do
96
+ expect { source.version! }.to raise_error(Errno::ENOENT)
97
+ end
98
+ end
99
+
100
+ context 'when the version definition can not be parsed' do
101
+ before(:each) do
102
+ expect(source).to receive(:parse_version_definition).and_raise(SourceController::ParseError)
103
+ end
104
+
105
+ it 'raises a SourceController::ParseError' do
106
+ expect { source.version! }.to raise_error(SourceController::ParseError)
107
+ end
108
+ end
109
+
110
+ context 'when the `#header_inc_php` file is ok' do
111
+ context 'for supported version' do
112
+ it 'returns a software version object' do
113
+ expect(source).to receive(:parse_version_definition).and_return('9.0.2555')
114
+ expect(source.version!).to equal SoftwareVersion::MAP['9.0.2555']
115
+ end
116
+
117
+ it 'caches a returned value' do
118
+ expect(source).to receive(:parse_version_definition).once.and_return('9.0.2555')
119
+ source.version!
120
+ expect(source.version!).to equal SoftwareVersion::MAP['9.0.2555']
121
+ end
122
+ end
123
+
124
+ context 'for unsupported version' do
125
+ before(:each) do
126
+ expect(source).to receive(:parse_version_definition).and_return('3.2.123')
127
+ end
128
+
129
+ it 'raises a SoftwareVersion::CompatibilityError' do
130
+ expect { source.version! }.to raise_error(SoftwareVersion::CompatibilityError)
131
+ end
132
+ end
133
+ end
134
+ end
135
+
136
+ describe '#parse_version_definition' do
137
+ let(:file) { double(File, read: file_contents) }
138
+ let(:header_filename) { '/path/to/skadate/internals/Header.inc.php' }
139
+
140
+ context 'when the `#header_inc_php` file can not be found' do
141
+ before(:each) do
142
+ expect(File).to receive(:open).with(header_filename).and_raise(Errno::ENOENT)
143
+ end
144
+
145
+ it 'raises an Errno::ENOENT' do
146
+ expect { source.parse_version_definition }.to raise_error(Errno::ENOENT)
147
+ end
148
+ end
149
+
150
+ context 'when the version definition can not be parsed' do
151
+ let(:file_contents) { "<?php ... ?>\n" }
152
+
153
+ before(:each) do
154
+ expect(File).to receive(:open).with(header_filename) do |&block|
155
+ block.call(file)
156
+ end
157
+ end
158
+
159
+ it 'raises a SourceController::ParseError' do
160
+ expect {source.parse_version_definition}.to raise_error(SourceController::ParseError)
161
+ end
162
+ end
163
+
164
+ context 'when the `#header_inc_php` file is ok' do
165
+ let(:file_contents) do
166
+ <<-PHPSCRIPT
167
+ <?php define('PACKAGE_VERSION', '9.2.2657'); ?>
168
+ PHPSCRIPT
169
+ end
170
+
171
+ before(:each) do
172
+ expect(File).to receive(:open)
173
+ .with(header_filename) do |&block|
174
+ block.call(file)
175
+ end
176
+ end
177
+
178
+ it 'parses the file and returns a version definition' do
179
+ expect(source.parse_version_definition).to eq '9.2.2657'
180
+ end
181
+ end
182
+ end
183
+
184
+ %w[internal_c external_c userfiles].each do |dirname|
185
+
186
+ describe "##{dirname}" do
187
+ context 'for a version earlier than `9.2.2960`' do
188
+ let(:version) { SoftwareVersion::MAP['9.2.2657'] }
189
+
190
+ before(:each) do
191
+ expect(source).to receive(:version!).and_return(version)
192
+ end
193
+
194
+ it "returns `/path/to/skadate/$#{dirname}`" do
195
+ expect(source.send(dirname.to_sym)).to eq "/path/to/skadate/$#{dirname}"
196
+ end
197
+ end
198
+
199
+ context 'for version `9.2.2960` and above' do
200
+ let(:version) { SoftwareVersion::MAP['9.2.2960'] }
201
+
202
+ before(:each) do
203
+ expect(source).to receive(:version!).and_return(version)
204
+ end
205
+
206
+ it "returns `/path/to/skadate/#{dirname}`" do
207
+ expect(source.send(dirname.to_sym)).to eq "/path/to/skadate/#{dirname}"
208
+ end
209
+ end
210
+ end
211
+
212
+ end
213
+
214
+ describe '#config_php' do
215
+
216
+ context 'for a version earlier than `9.2.2960`' do
217
+ let(:version) { SoftwareVersion::MAP['9.2.2657'] }
218
+
219
+ before(:each) do
220
+ expect(source).to receive(:version!).and_return(version)
221
+ end
222
+
223
+ it 'returns `/path/to/skadate/internals/$config.php`' do
224
+ expect(source.config_php).to eq '/path/to/skadate/internals/$config.php'
225
+ end
226
+ end
227
+
228
+ context 'for version `9.2.2960` and above' do
229
+ let(:version) { SoftwareVersion::MAP['9.2.2960'] }
230
+
231
+ before(:each) do
232
+ expect(source).to receive(:version!).and_return(version)
233
+ end
234
+
235
+ it 'returns `/path/to/skadate/internals/config.php`' do
236
+ expect(source.config_php).to eq '/path/to/skadate/internals/config.php'
237
+ end
238
+ end
239
+ end
240
+ end
241
+
242
+ end
243
+ end