howitzer 0.0.3 → 1.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 (81) hide show
  1. data/.gitignore +8 -0
  2. data/.rspec +3 -0
  3. data/.travis.yml +4 -0
  4. data/CHANGELOG.md +32 -0
  5. data/GETTING_STARTED.md +529 -0
  6. data/Gemfile +4 -2
  7. data/LICENSE +2 -2
  8. data/README.md +57 -13
  9. data/Rakefile +9 -2
  10. data/bin/howitzer +79 -31
  11. data/generators/base_generator.rb +87 -0
  12. data/generators/config/config_generator.rb +16 -20
  13. data/generators/config/templates/default.yml +26 -7
  14. data/generators/cucumber/cucumber_generator.rb +20 -26
  15. data/generators/{tasks → cucumber}/templates/cucumber.rake +0 -0
  16. data/generators/{config → cucumber}/templates/cucumber.yml +0 -0
  17. data/generators/emails/emails_generator.rb +11 -18
  18. data/generators/emails/templates/example_email.rb +1 -0
  19. data/generators/pages/pages_generator.rb +16 -18
  20. data/generators/pages/templates/example_menu.rb +1 -0
  21. data/generators/pages/templates/example_page.rb +1 -1
  22. data/generators/root/root_generator.rb +18 -20
  23. data/generators/root/templates/.gitignore +2 -1
  24. data/generators/root/templates/Gemfile +3 -2
  25. data/generators/root/templates/Rakefile +10 -0
  26. data/generators/root/templates/boot.rb +3 -9
  27. data/generators/rspec/rspec_generator.rb +23 -0
  28. data/generators/rspec/templates/example_spec.rb +7 -0
  29. data/generators/rspec/templates/rspec.rake +34 -0
  30. data/generators/rspec/templates/spec_helper.rb +56 -0
  31. data/generators/tasks/tasks_generator.rb +11 -17
  32. data/generators/tasks/templates/common.rake +15 -0
  33. data/howitzer.gemspec +13 -2
  34. data/lib/howitzer.rb +1 -0
  35. data/lib/howitzer/helpers.rb +87 -2
  36. data/lib/howitzer/init.rb +0 -1
  37. data/lib/howitzer/patches/rawler_patched.rb +86 -0
  38. data/lib/howitzer/settings.rb +27 -0
  39. data/lib/howitzer/utils.rb +3 -12
  40. data/lib/howitzer/utils/capybara_patched.rb +3 -2
  41. data/lib/howitzer/utils/capybara_settings.rb +158 -24
  42. data/lib/howitzer/utils/data_generator/data_storage.rb +35 -1
  43. data/lib/howitzer/utils/data_generator/gen.rb +45 -3
  44. data/lib/howitzer/utils/email/email.rb +44 -5
  45. data/lib/howitzer/utils/email/mail_client.rb +28 -22
  46. data/lib/howitzer/utils/email/mailgun_helper.rb +30 -4
  47. data/lib/howitzer/utils/locator_store.rb +111 -19
  48. data/lib/howitzer/utils/log.rb +137 -0
  49. data/lib/howitzer/utils/page_validator.rb +86 -0
  50. data/lib/howitzer/vendor/firebug-1.12.1-fx.xpi +0 -0
  51. data/lib/howitzer/vendor/firepath-0.9.7-fx.xpi +0 -0
  52. data/lib/howitzer/version.rb +2 -2
  53. data/lib/howitzer/web_page.rb +159 -19
  54. data/spec/active_resource.rb +0 -0
  55. data/spec/config/custom.yml +1 -0
  56. data/spec/config/default.yml +28 -0
  57. data/spec/spec_helper.rb +46 -1
  58. data/spec/support/boot_helper.rb +15 -0
  59. data/spec/support/generator_helper.rb +13 -0
  60. data/spec/support/logger_helper.rb +12 -0
  61. data/spec/unit/bin/howitzer_spec.rb +175 -0
  62. data/spec/unit/generators/generators_spec.rb +175 -0
  63. data/spec/unit/lib/capybara_settings_spec.rb +170 -0
  64. data/spec/unit/lib/helpers_spec.rb +619 -0
  65. data/spec/unit/lib/init_spec.rb +14 -0
  66. data/spec/unit/lib/settings_spec.rb +17 -0
  67. data/spec/unit/lib/utils/data_generator/data_storage_spec.rb +62 -0
  68. data/spec/unit/lib/utils/data_generator/gen_spec.rb +151 -0
  69. data/spec/unit/lib/utils/email/email_spec.rb +75 -0
  70. data/spec/unit/lib/utils/email/mail_client_spec.rb +115 -0
  71. data/spec/unit/lib/utils/email/mailgun_helper_spec.rb +95 -0
  72. data/spec/unit/lib/utils/locator_store_spec.rb +122 -0
  73. data/spec/unit/lib/utils/log_spec.rb +107 -0
  74. data/spec/unit/lib/utils/page_validator_spec.rb +142 -0
  75. data/spec/unit/lib/web_page_spec.rb +250 -0
  76. data/spec/unit/version_spec.rb +8 -0
  77. metadata +215 -15
  78. data/Gemfile.lock +0 -103
  79. data/History.md +0 -20
  80. data/lib/howitzer/utils/logger.rb +0 -108
  81. data/spec/howitzer/version_spec.rb +0 -8
File without changes
@@ -0,0 +1 @@
1
+ txt_log: log.txt
@@ -0,0 +1,28 @@
1
+ ###########################################################
2
+ # LOGGER SETTINGS #
3
+ ###########################################################
4
+ log_dir: "spec/log"
5
+ txt_log: log.txt
6
+ html_log: log.html
7
+ rawler_log: rawler_log.txt
8
+ selenium_server_log: selenium-server.log
9
+ required_clean_logs: true
10
+ hide_datetime_from_log: true
11
+
12
+ driver: selenium
13
+
14
+ ###########################################################
15
+ # MAIL SETTINGS #
16
+ ###########################################################
17
+ mail_smtp_server: smtp.demo.com
18
+ mail_smtp_port: 25
19
+ mail_smtp_domain: demo.com
20
+ mail_smtp_user_name: test_user
21
+ mail_smtp_user_pass: mypass
22
+
23
+ mail_pop3_server: pop.demo.com
24
+ mail_pop3_port: 995
25
+ mail_pop3_timeout: 120
26
+ mail_pop3_domain: ${mail_smtp_domain}
27
+ mail_pop3_user_name: test_user
28
+ mail_pop3_user_pass: mypass
data/spec/spec_helper.rb CHANGED
@@ -1 +1,46 @@
1
- require 'howitzer'
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+ require 'simplecov'
4
+ require 'tmpdir'
5
+ require 'ffaker'
6
+ require 'capybara'
7
+ require 'json'
8
+ require 'capybara/dsl'
9
+
10
+ SimpleCov.start do
11
+ add_filter "/spec/"
12
+ add_filter '/config/'
13
+ add_filter do |source_file|
14
+ source_file.lines.count < 5
15
+ end
16
+ add_group "generators", "/generators"
17
+ add_group "lib", "/lib"
18
+ end
19
+
20
+ Dir[File.join(File.dirname(__FILE__), 'support', '**', '*.rb')].each{ |f| require f }
21
+
22
+ RSpec.configure do |config|
23
+ config.include GeneratorHelper
24
+ config.expect_with :rspec do |c|
25
+ c.syntax = :expect
26
+ end
27
+ config.mock_with :rspec do |c|
28
+ c.syntax = :expect
29
+ end
30
+ end
31
+
32
+ def project_path
33
+ File.expand_path(File.join(File.dirname(__FILE__), '..'))
34
+ end
35
+
36
+ def lib_path
37
+ File.join(project_path, 'lib')
38
+ end
39
+
40
+ def generators_path
41
+ File.join(project_path, 'generators')
42
+ end
43
+
44
+ def log_path
45
+ File.join(project_path, 'spec/log')
46
+ end
@@ -0,0 +1,15 @@
1
+ require 'sexy_settings'
2
+ SexySettings.configure do |config|
3
+ config.path_to_default_settings = File.expand_path("default.yml", File.join(File.dirname(__FILE__), '..', 'config'))
4
+ config.path_to_custom_settings = File.expand_path("custom.yml", File.join(File.dirname(__FILE__), '..', 'config'))
5
+ end
6
+
7
+ def settings
8
+ SexySettings::Base.instance
9
+ end
10
+
11
+ def log
12
+ Howitzer::Log.instance
13
+ end
14
+
15
+ ENV['TEST_MODE'] = "TRUE"
@@ -0,0 +1,13 @@
1
+ module GeneratorHelper
2
+ def file_tree_info(root)
3
+ Dir["#{root}/**/*"].sort_by{|name| name.sub(root, '') }.map do |name|
4
+ hash = {name: name.sub(root, ''), is_directory: File.directory?(name)}
5
+ hash[:size] = File.stat(name).size unless hash[:is_directory]
6
+ hash
7
+ end
8
+ end
9
+
10
+ def template_file_size(root_directory, file)
11
+ File.size(File.join(generators_path, root_directory, 'templates', file))
12
+ end
13
+ end
@@ -0,0 +1,12 @@
1
+ module LoggerHelper
2
+ def read_file(file)
3
+ path = File.expand_path(file, __FILE__)
4
+ IO.read(path)
5
+ end
6
+
7
+ def clear_file(file)
8
+ path = File.expand_path(file, __FILE__)
9
+ File.open(path, "w"){ }
10
+ end
11
+ end
12
+
@@ -0,0 +1,175 @@
1
+ require "spec_helper"
2
+ load "bin/howitzer"
3
+
4
+ HELP_MESSAGE = %{
5
+ howitzer [command {--cucumber, --rspec}] | {options}
6
+
7
+ Commands are ...
8
+ install Generate test framework units:
9
+ --rspec add RSpec integration with framework
10
+ --cucumber add Cucumber integration with framework
11
+ Options are ...
12
+ --help Display this help message.
13
+ --version Display the program version.
14
+ }
15
+
16
+ shared_examples "check argument" do |arg|
17
+ before do
18
+ stub_const("ARGV", [arg])
19
+ expect(self).to_not receive(:puts)
20
+ expect(self).to_not receive(:exit)
21
+ end
22
+ context "(#{arg})" do
23
+ let(:arg) { arg }
24
+ it { expect(subject).to be_nil }
25
+ end
26
+ end
27
+
28
+ describe Howitzer do
29
+ describe "#check_arguments" do
30
+ subject { check_arguments }
31
+ context "when right arguments specified" do
32
+ it_behaves_like "check argument", 'install --cucumber'
33
+ it_behaves_like "check argument", 'install --rspec'
34
+ it_behaves_like "check argument", 'install --cucumber --rspec'
35
+ it_behaves_like "check argument", 'install --rspec --cucumber'
36
+ it_behaves_like "check argument", '--version'
37
+ it_behaves_like "check argument", '--help'
38
+ it_behaves_like "check argument", '--HELP'
39
+ end
40
+ context "when arguments incorrect" do
41
+ before do
42
+ expect(self).to receive(:puts).with("ERROR: incorrect options. Please, see help for details").once
43
+ expect(self).to receive(:puts).with(HELP_MESSAGE)
44
+ expect(self).to receive(:exit).with(1)
45
+ end
46
+ context "(missing arguments)" do
47
+ it { expect(subject).to be_nil }
48
+ end
49
+ context "UNKNOWN argument" do
50
+ let(:arg) { '--unknown' }
51
+ before { stub_const("ARGV", [arg]) }
52
+ it { expect(subject).to be_nil }
53
+ end
54
+ end
55
+ end
56
+
57
+ describe "#parse_options" do
58
+ subject { parse_options }
59
+ context "when correct argument received" do
60
+ before do
61
+ stub_const("ARGV", arg)
62
+ expect(self).not_to receive(:puts).with("ERROR: incorrect first argument '#{arg}'")
63
+ end
64
+ context "'--version' argument given" do
65
+ let(:arg) { ['--version'] }
66
+ before{ stub_const("Howitzer::VERSION", '1.0.0') }
67
+ it do
68
+ expect(self).to receive(:exit).with(0).once
69
+ expect(self).to receive(:puts).with("Version: 1.0.0")
70
+ subject
71
+ end
72
+ end
73
+ context "'--help' argument given" do
74
+ let(:arg) { ['--help'] }
75
+ it do
76
+ expect(self).to receive(:puts).with(HELP_MESSAGE)
77
+ subject
78
+ end
79
+ end
80
+ context "'install' argument" do
81
+ let(:primary_arg) { 'install' }
82
+ let(:generator) { double('generator') }
83
+ before do
84
+ expect(Howitzer::ConfigGenerator).to receive(:new)
85
+ expect(Howitzer::PagesGenerator).to receive(:new)
86
+ expect(Howitzer::TasksGenerator).to receive(:new)
87
+ expect(Howitzer::EmailsGenerator).to receive(:new)
88
+ expect(Howitzer::RootGenerator).to receive(:new)
89
+ expect(self).not_to receive(:puts).with("ERROR: Empty option specified.")
90
+ expect(self).not_to receive(:puts).with("ERROR: Unknown option specified.")
91
+ expect(self).not_to receive(:puts).with(HELP_MESSAGE)
92
+ end
93
+ context "with option '--cucumber'" do
94
+ let(:arg) { [primary_arg, '--cucumber'] }
95
+ it do
96
+ expect(Howitzer::RspecGenerator).to_not receive(:new)
97
+ expect(Howitzer::CucumberGenerator).to receive(:new)
98
+ subject
99
+ end
100
+ end
101
+ context "with option '--rspec'" do
102
+ let(:arg) {[primary_arg, '--rspec']}
103
+ it do
104
+ expect(Howitzer::RspecGenerator).to receive(:new)
105
+ expect(Howitzer::CucumberGenerator).to_not receive(:new)
106
+ subject
107
+ end
108
+ end
109
+ end
110
+ end
111
+ context "when incorrect arguments received" do
112
+ before { stub_const("ARGV", arg) }
113
+ context "with (missing) argument" do
114
+ let(:arg) {[]}
115
+ it do
116
+ expect(self).to receive(:puts).with("ERROR: incorrect first argument ('')")
117
+ expect(self).to receive(:puts).with(HELP_MESSAGE)
118
+ expect(self).to receive(:exit).with(1)
119
+ subject
120
+ end
121
+ end
122
+ context "with UNKNOWN argument" do
123
+ let(:arg) {['unknown']}
124
+ it do
125
+ expect(self).to receive(:puts).with("ERROR: incorrect first argument ('unknown')")
126
+ expect(self).to receive(:puts).with(HELP_MESSAGE)
127
+ expect(self).to receive(:exit).with(1)
128
+ subject
129
+ end
130
+ end
131
+ end
132
+
133
+ context "when 'install' option received with incorrect arguments" do
134
+ let(:primary_arg) { 'install' }
135
+ let(:generator) { double('generator') }
136
+ before { stub_const("ARGV", arg) }
137
+ context "with UNKNOWN option specified" do
138
+ let(:arg) {[primary_arg, '--unknown']}
139
+ before do
140
+ expect(Howitzer::ConfigGenerator).to_not receive(:new)
141
+ expect(Howitzer::PagesGenerator).to_not receive(:new)
142
+ expect(Howitzer::TasksGenerator).to_not receive(:new)
143
+ expect(Howitzer::EmailsGenerator).to_not receive(:new)
144
+ expect(Howitzer::RootGenerator).to_not receive(:new)
145
+ expect(Howitzer::RspecGenerator).to_not receive(:new)
146
+ expect(Howitzer::CucumberGenerator).to_not receive(:new)
147
+ end
148
+ it do
149
+ expect(self).to receive(:puts).with("ERROR: Unknown '--unknown' option specified.")
150
+ expect(self).to receive(:puts).with(HELP_MESSAGE)
151
+ expect(self).to receive(:exit).with(1)
152
+ subject
153
+ end
154
+ end
155
+ context "with no option specified" do
156
+ let(:arg) {[primary_arg]}
157
+ before do
158
+ expect(Howitzer::ConfigGenerator).to_not receive(:new)
159
+ expect(Howitzer::PagesGenerator).to_not receive(:new)
160
+ expect(Howitzer::TasksGenerator).to_not receive(:new)
161
+ expect(Howitzer::EmailsGenerator).to_not receive(:new)
162
+ expect(Howitzer::RootGenerator).to_not receive(:new)
163
+ expect(Howitzer::RspecGenerator).to_not receive(:new)
164
+ expect(Howitzer::CucumberGenerator).to_not receive(:new)
165
+ end
166
+ it do
167
+ expect(self).to receive(:puts).with("ERROR: No option specified for install command.")
168
+ expect(self).to receive(:puts).with(HELP_MESSAGE)
169
+ expect(self).to receive(:exit).with(1)
170
+ subject
171
+ end
172
+ end
173
+ end
174
+ end
175
+ end
@@ -0,0 +1,175 @@
1
+ require 'spec_helper'
2
+ require 'fileutils'
3
+
4
+ Dir[File.join(generators_path, '**', '*_generator.rb')].each{ |f| require f }
5
+
6
+ describe "Generators" do
7
+ let(:destination) { Dir.mktmpdir }
8
+ let(:output) { StringIO.new }
9
+ subject { file_tree_info(destination) }
10
+ before do
11
+ Howitzer::BaseGenerator.logger = output
12
+ Howitzer::BaseGenerator.destination = destination
13
+ generator_name.new
14
+ end
15
+
16
+ after { FileUtils.rm_r(destination) }
17
+
18
+ describe "ConfigGenerator" do
19
+ let(:generator_name) { Howitzer::ConfigGenerator }
20
+ let(:expected_result) do
21
+ [
22
+ {:name=>"/config", :is_directory=>true},
23
+ {:name=>"/config/custom.yml", :is_directory=>false, :size=>template_file_size('config', 'custom.yml')},
24
+ {:name=>"/config/default.yml", :is_directory=>false, :size=>template_file_size('config', 'default.yml')}
25
+ ]
26
+ end
27
+
28
+ it { expect(subject).to eql(expected_result) }
29
+ describe "output" do
30
+ let(:expected_output) do
31
+ " Creates config files.
32
+ Added 'config/custom.yml' file
33
+ Added 'config/default.yml' file\n"
34
+ end
35
+ subject { output.string }
36
+ it { expect(subject).to eql(expected_output) }
37
+ end
38
+ end
39
+ describe "PagesGenerator" do
40
+ let(:generator_name) { Howitzer::PagesGenerator }
41
+ let(:expected_result) do
42
+ [
43
+ {:name=>"/pages", :is_directory=>true},
44
+ {:name=>"/pages/example_menu.rb", :is_directory=>false, :size=>template_file_size('pages', 'example_menu.rb')},
45
+ {:name=>"/pages/example_page.rb", :is_directory=>false, :size=>template_file_size('pages', 'example_page.rb')}
46
+ ]
47
+ end
48
+ it { expect(subject).to eql(expected_result) }
49
+ describe "output" do
50
+ let(:expected_output) do
51
+ " Creates PageOriented pattern structure
52
+ Added 'pages/example_page.rb' file
53
+ Added 'pages/example_menu.rb' file\n"
54
+ end
55
+ subject { output.string }
56
+ it { expect(subject).to eql(expected_output) }
57
+ end
58
+ end
59
+ describe "TasksGenerator" do
60
+ let(:generator_name) { Howitzer::TasksGenerator }
61
+ let(:expected_result) do
62
+ [
63
+ {:name=>"/tasks", :is_directory=>true},
64
+ {:name=>"/tasks/common.rake", :is_directory=>false, :size=>template_file_size('tasks', 'common.rake')}
65
+ ]
66
+ end
67
+ it { expect(subject).to eql(expected_result) }
68
+ describe "output" do
69
+ let(:expected_output) do
70
+ " Creates RAKE tasks folder and file.
71
+ Added 'tasks/common.rake' file\n"
72
+ end
73
+ subject { output.string }
74
+ it { expect(subject).to eql(expected_output) }
75
+ end
76
+ end
77
+ describe "EmailsGenerator" do
78
+ let(:generator_name) { Howitzer::EmailsGenerator }
79
+ let(:expected_result) do
80
+ [
81
+ {:name=>"/emails", :is_directory=>true},
82
+ {:name=>"/emails/example_email.rb", :is_directory=>false, :size=>template_file_size('emails', 'example_email.rb')}
83
+ ]
84
+ end
85
+ it { expect(subject).to eql(expected_result) }
86
+ describe "output" do
87
+ let(:expected_output) do
88
+ " Creates a simple email class.\"
89
+ Added '/emails/example_email.rb' file\n"
90
+ end
91
+ subject { output.string }
92
+ it { expect(subject).to eql(expected_output) }
93
+ end
94
+ end
95
+ describe "RootGenerator" do
96
+ let(:generator_name) { Howitzer::RootGenerator }
97
+ let(:expected_result) do
98
+ [
99
+ {:name=>"/Gemfile", :is_directory=>false, :size=>template_file_size('root', 'Gemfile')},
100
+ {:name=>"/Rakefile", :is_directory=>false, :size=>template_file_size('root', 'Rakefile')},
101
+ {:name=>"/boot.rb", :is_directory=>false, :size=>template_file_size('root', 'boot.rb')}
102
+ ]
103
+ end
104
+ it { expect(subject).to eql(expected_result) }
105
+ describe "output" do
106
+ let(:expected_output) do
107
+ " Creates root config files.
108
+ Added '.gitignore' file
109
+ Added 'Gemfile' file
110
+ Added 'Rakefile' file
111
+ Added 'boot.rb' file\n"
112
+ end
113
+ subject { output.string }
114
+ it { expect(subject).to eql(expected_output) }
115
+ end
116
+ end
117
+ describe "CucumberGenerator" do
118
+ let(:generator_name) { Howitzer::CucumberGenerator }
119
+ let(:expected_result) do
120
+ [
121
+ {:name=>"/config", :is_directory=>true},
122
+ {:name=>"/config/cucumber.yml", :is_directory=>false, :size=>template_file_size('cucumber', 'cucumber.yml')},
123
+ {:name=>"/features", :is_directory=>true},
124
+ {:name=>"/features/example.feature", :is_directory=>false, :size=>template_file_size('cucumber', 'example.feature')},
125
+ {:name=>"/features/step_definitions", :is_directory=>true},
126
+ {:name=>"/features/step_definitions/common_steps.rb", :is_directory=>false, :size=>template_file_size('cucumber', 'common_steps.rb')},
127
+ {:name=>"/features/support", :is_directory=>true},
128
+ {:name=>"/features/support/env.rb", :is_directory=>false, :size=>template_file_size('cucumber', 'env.rb')},
129
+ {:name=>"/features/support/transformers.rb", :is_directory=>false, :size=>template_file_size('cucumber', 'transformers.rb')},
130
+ {:name=>"/tasks", :is_directory=>true},
131
+ {:name=>"/tasks/cucumber.rake", :is_directory=>false, :size=>template_file_size('cucumber', 'cucumber.rake')}
132
+ ]
133
+ end
134
+ it { expect(subject).to eql(expected_result) }
135
+ describe "output" do
136
+ let(:expected_output) do
137
+ " Integrates Cucumber to the framework
138
+ Added 'features/step_definitions/common_steps.rb' file
139
+ Added 'features/support/env.rb' file
140
+ Added 'features/support/transformers.rb' file
141
+ Added 'features/example.feature' file
142
+ Added 'tasks/cucumber.rake' file
143
+ Added 'config/cucumber.yml' file\n"
144
+ end
145
+ subject { output.string }
146
+ it { expect(subject).to eql(expected_output) }
147
+ end
148
+ end
149
+ describe "RSpecGenerator" do
150
+ let(:generator_name) { Howitzer::RspecGenerator }
151
+ let(:expected_result) do
152
+ [
153
+ {:name=>"/spec", :is_directory=>true},
154
+ {:name=>"/spec/example_spec.rb", :is_directory=>false, :size=>template_file_size('rspec', 'example_spec.rb')},
155
+ {:name=>"/spec/spec_helper.rb", :is_directory=>false, :size=>template_file_size('rspec', 'spec_helper.rb')},
156
+ {:name=>"/tasks", :is_directory=>true},
157
+ {:name=>"/tasks/rspec.rake", :is_directory=>false, :size=>template_file_size('rspec', 'rspec.rake')}
158
+ ]
159
+ end
160
+ it { expect(subject).to eql(expected_result) }
161
+ describe "output" do
162
+ let(:expected_output) do
163
+ " Integrates RSpec to the framework.
164
+ Added 'spec/spec_helper.rb' file
165
+ Added 'spec/example_spec.rb' file
166
+ Added 'tasks/rspec.rake' file\n"
167
+ end
168
+ subject { output.string }
169
+ it { expect(subject).to eql(expected_output) }
170
+ end
171
+ end
172
+
173
+
174
+ end
175
+