my_todo 3.1.1 → 4.0.0.pre1

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 (46) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +63 -50
  3. data/Rakefile +3 -0
  4. data/bin/{my_todo → mytodo} +1 -1
  5. data/{lib/db → db}/config.yml +3 -3
  6. data/{lib/db → db}/migrate/20160912172429_create_items.rb +2 -2
  7. data/{lib/db → db}/migrate/20160913134552_create_tags.rb +1 -1
  8. data/{lib/db → db}/migrate/20160913134610_create_stubs.rb +1 -1
  9. data/{lib/db → db}/migrate/20161003121448_create_notes.rb +1 -1
  10. data/{lib/db → db}/migrate/20161005133023_add_detailed_status_to_items.rb +1 -1
  11. data/db/migrate/20161220143613_add_default_to_item_done.rb +5 -0
  12. data/{lib/db → db}/schema.rb +6 -14
  13. data/lib/my_todo.rb +4 -114
  14. data/lib/my_todo/ar_base.rb +3 -2
  15. data/lib/my_todo/models/item.rb +4 -0
  16. data/lib/my_todo/modules/actions.rb +129 -0
  17. data/lib/my_todo/modules/finders.rb +5 -3
  18. data/lib/my_todo/modules/my_todo_actions.rb +7 -6
  19. data/lib/my_todo/modules/templates.rb +1 -0
  20. data/lib/my_todo/templates/item.erb +6 -2
  21. data/lib/my_todo/templates/list.erb +3 -1
  22. data/lib/my_todo/templates/notes.erb +8 -5
  23. data/lib/my_todo/templates/results.erb +7 -1
  24. data/lib/my_todo/templates/standalone_migrations.yml.erb +4 -4
  25. data/lib/my_todo/version.rb +1 -1
  26. data/lib/setup.rb +2 -2
  27. data/my_todo.gemspec +19 -17
  28. data/spec/actions/create_spec.rb +17 -15
  29. data/spec/actions/delete_spec.rb +10 -4
  30. data/spec/actions/list_spec.rb +6 -5
  31. data/spec/actions/note_spec.rb +9 -7
  32. data/spec/actions/notes_spec.rb +6 -7
  33. data/spec/actions/rm_note_spec.rb +7 -8
  34. data/spec/actions/rm_tag_spec.rb +9 -8
  35. data/spec/actions/search_spec.rb +12 -13
  36. data/spec/actions/tag_spec.rb +12 -10
  37. data/spec/actions/update_spec.rb +5 -6
  38. data/{lib → spec}/db/todos_test.sqlite3 +0 -0
  39. data/spec/models/item_spec.rb +1 -1
  40. data/spec/models/stub_spec.rb +6 -6
  41. data/spec/spec_helper.rb +19 -5
  42. metadata +89 -76
  43. data/.standalone_migrations +0 -6
  44. data/lib/db/migrate/20161220143613_add_default_to_item_done.rb +0 -5
  45. data/lib/db/todos_development.sqlite3 +0 -0
  46. data/lib/my_todo/db/todos_test.sqlite3 +0 -0
@@ -5,9 +5,10 @@ module ArBase
5
5
  # Set path based on bin/my_todo
6
6
  path = case ENV['RAILS_ENV']
7
7
  when 'development'
8
- "#{__dir__}/../db/todos_development.sqlite3"
8
+ "#{__dir__}/../../db/todos_development.sqlite3"
9
9
  when 'test'
10
- "#{__dir__}/../db/todos_test.sqlite3"
10
+ # "#{__dir__}/../db/todos_test.sqlite3"
11
+ "#{__dir__}/../../spec/db/todos_test.sqlite3"
11
12
  else
12
13
  File.expand_path("#{`echo $HOME`.chomp}/.my_todo/data/todos_production.sqlite3", __FILE__)
13
14
  end
@@ -29,4 +29,8 @@ class Item < ActiveRecord::Base
29
29
  def update_done
30
30
  self.done = true if COMPLETE_STATUSES.include? self.detailed_status
31
31
  end
32
+
33
+ def done?
34
+ self.done == true ? 'Yes' : 'No'
35
+ end
32
36
  end
@@ -0,0 +1,129 @@
1
+ module MyTodo
2
+ module Actions
3
+ def self.included(thor)
4
+ thor.class_eval do
5
+
6
+ desc 'list <STATUS>', 'List todo items. Default: undone, [all], [done], [undone]'
7
+ def list(status='undone')
8
+ @status = status
9
+ print_list
10
+ end
11
+
12
+ desc "create \"<BODY>\" <TAGS> [Default: general]>", 'Create a todo item with optional and tags'
13
+ def create(body, *tags)
14
+ @body = body
15
+ @tags = tags.any? ? tags : %w[Default]
16
+
17
+ begin
18
+ create_item
19
+ print_item
20
+ rescue ActiveRecord::RecordInvalid => e
21
+ say e.message
22
+ end
23
+ end
24
+
25
+ desc 'update <ID> "<BODY>" <DONE>', 'Update a todo item'
26
+ def update(id, body=nil, done=nil)
27
+ @item = Item.find_by_id(id)
28
+ @body = body.nil? ? @item.body : body
29
+ @done = done.nil? ? @item.done : done
30
+
31
+ begin
32
+ update_item
33
+ print_item
34
+ rescue ActiveRecord::RecordInvalid => e
35
+ say e.message
36
+ end
37
+ end
38
+
39
+ desc 'delete <ID>', 'Destroy a todo item'
40
+ # option :id, required: true
41
+ def delete(id)
42
+ @item = Item.find_by_id(id)
43
+
44
+ begin
45
+ item.destroy!
46
+ say 'Item Deleted'
47
+ rescue StandardError => e
48
+ say e.message
49
+ end
50
+ end
51
+
52
+ desc 'search "<TEXT>"', 'Find a todo by item body, tag name, status or note body'
53
+ def search(text="")
54
+ @text = text
55
+ @items = Item.ransack(body_or_detailed_status_or_tags_name_or_notes_body_cont: @text).result
56
+ print_search_results
57
+ end
58
+
59
+ desc 'tag <ID> <TAGS>', 'Add tags to a todo item'
60
+ def tag(id, *tags)
61
+ @item = Item.find_by_id(id)
62
+
63
+ begin
64
+ if tags.any?
65
+ @banner = "Tags added to todo #{@item.id}"
66
+ tags.each {|tag| @item.tags.create!(name: tag)}
67
+ @item = @item.reload
68
+ print_item
69
+ end
70
+ rescue StandardError => e
71
+ say e.message
72
+ end
73
+ end
74
+
75
+ desc 'rm_tag <ID> <TAGS>', 'Remove tags from a todo item'
76
+ def rm_tag(id, *tags)
77
+ @item = Item.find_by_id(id)
78
+
79
+ begin
80
+ if tags.any?
81
+ @banner = "Tags removed from item #{@item.id}"
82
+ @item.tags.where(name: tags).destroy_all
83
+ print_item
84
+ end
85
+ rescue StandardError => e
86
+ say e.message
87
+ end
88
+ end
89
+
90
+ desc 'note <ID> "<TEXT>"', 'Adds note to a todo item'
91
+ def note(id, text="")
92
+ @item = Item.find_by_id(id)
93
+
94
+ begin
95
+ @item.notes.create(body: text) unless text.empty?
96
+ print_notes
97
+ rescue StandardError => e
98
+ say e.message
99
+ end
100
+ end
101
+
102
+ desc 'rm_note <ID> <NOTE_IDS>', 'Remove notes from todo item'
103
+ def rm_note(id, *note_ids)
104
+ @item = Item.find_by_id(id)
105
+
106
+ begin
107
+ @banner = "Note removed from item: #{@item.id}"
108
+ @item.notes.where(id: note_ids).destroy_all
109
+ print_item
110
+ rescue StandardError => e
111
+ say e.message
112
+ end
113
+ end
114
+
115
+ desc 'notes <ID>', 'Display notes for a todo item'
116
+ def notes(id)
117
+ @item = Item.find_by_id(id)
118
+
119
+ begin
120
+ print_notes
121
+ rescue StandardError => e
122
+ say e.message
123
+ end
124
+ end
125
+
126
+ end
127
+ end
128
+ end
129
+ end
@@ -1,6 +1,6 @@
1
1
  module Finders
2
2
  def item
3
- @item ||= Item.where(id: options[:id]).first
3
+ @item ||= Item.find_by_id(@id)
4
4
  end
5
5
 
6
6
  def item_notes
@@ -8,13 +8,15 @@ module Finders
8
8
  end
9
9
 
10
10
  def all_items
11
- @items = case options[:status]
11
+ @items = case @status
12
12
  when 'all'
13
13
  Item.all
14
14
  when 'done'
15
15
  Item.where(done: true)
16
- else
16
+ when 'undone'
17
17
  Item.where(done: false)
18
+ else
19
+ say 'Unknown status!'
18
20
  end
19
21
  end
20
22
 
@@ -4,16 +4,17 @@ module MyTodoActions
4
4
  @status = ask("Choose a status for item", default: set_default_status)
5
5
  end
6
6
 
7
- def create_item(options)
8
- @item = Item.new(options.except(:tags))
7
+ def create_item
8
+ @banner = 'Item Created'
9
+ @item = Item.new(body: @body)
9
10
  assign_detailed_status
10
11
  @item.save!
11
12
  set_tags
12
13
  end
13
14
 
14
- def update_item(options)
15
- item.assign_attributes(options)
16
- @item = item #Find a better way!!!!
15
+ def update_item
16
+ @banner = 'Item Updated'
17
+ @item.assign_attributes(body: @body, done: @done)
17
18
  assign_detailed_status
18
19
  item.save!
19
20
  end
@@ -24,7 +25,7 @@ module MyTodoActions
24
25
  end
25
26
 
26
27
  def set_tags
27
- options[:tags].split(' ').each{|tag| @item.tags.create(name: tag) } if options[:tags]
28
+ @tags.each{|tag| @item.tags.create(name: tag) } if @tags
28
29
  end
29
30
 
30
31
  def set_default_status
@@ -1,5 +1,6 @@
1
1
  module Templates
2
2
  def print_list
3
+ all_items
3
4
  say ERB.new(File.read("#{__dir__}/../templates/list.erb"), nil, '-').result(binding)
4
5
  end
5
6
 
@@ -1,2 +1,6 @@
1
- ID: <%= item.id %> | Created On: <%= item.created_at.strftime("%Y-%m-%d") %> | Tags: <%= item.tags.map(&:name).join(', ') %> | Status: <%= item.detailed_status %> | Complete: <%= item.done %>
2
- <%= item.body %>
1
+ <%= @banner %>
2
+
3
+ id: <%= @item.id %> notes: <%= @item.notes.size %> tags: <%= @item.tags.map(&:name).join(', ') %>
4
+ created: <%= @item.created_at.strftime("%Y-%m-%d") %> status: <%= @item.detailed_status %> (done: <%= @item.done? %>)
5
+
6
+ <%= @item.body %>
@@ -1,6 +1,8 @@
1
+ Items Found: <%= @items.count %>
1
2
 
2
3
  <% @items.each do |item| -%>
3
- ID: <%= item.id %> | Created On: <%= item.created_at.strftime("%Y-%m-%d") %> | Tags: <%= item.tags.map(&:name).join(', ') %> | Status: <%= item.detailed_status %> | Complete: <%= item.done %> | Notes: <%= item.notes.size %>
4
+ id: <%= item.id %> notes: <%= item.notes.size %> tags: <%= item.tags.map(&:name).join(', ') %>
5
+ created: <%= item.created_at.strftime("%Y-%m-%d") %> status: <%= item.detailed_status %> (done: <%= item.done? %>)
4
6
 
5
7
  <%= item.body %>
6
8
  <%= '*' * 100 %>
@@ -1,9 +1,12 @@
1
- <% if item_notes.any? -%>
2
- Notes for <%= item.id %>: <%= item.body %>
3
- <% item_notes.each do |note| -%>
4
- ID: <%= note.id %> | Created On: <%= note.created_at.strftime("%Y-%m-%d")%>
1
+ <% if @item.notes.any? -%>
2
+ notes for item <%= @item.id %>: <%= @item.body %>
3
+
4
+ <% @item.notes.each do |note| -%>
5
+ id: <%= note.id %>
6
+ created: <%= note.created_at.strftime("%Y-%m-%d")%>
7
+
5
8
  <%= note.body %>
6
9
  <% end %>
7
10
  <% else -%>
8
- No Notes for item <%= item.id %>
11
+ No Notes for item <%= @item.id %>
9
12
  <% end %>
@@ -1,6 +1,12 @@
1
+ Items Found: <%= @items.count %>
2
+
3
+ Search Text: <%= @text %>
1
4
 
2
5
  <% @items.each do |item| -%>
3
- ID: <%= item.id %> | Created On: <%= item.created_at.strftime("%Y-%m-%d") %> | Tags: <%= item.tags.map(&:name).join(', ') %> | Status: <%= item.detailed_status %> | Complete: <%= item.done %> | Notes: <%= item.notes.count %>
6
+
7
+ id: <%= item.id %> notes: <%= item.notes.size %> tags: <%= item.tags.map(&:name).join(', ') %>
8
+ created: <%= item.created_at.strftime("%Y-%m-%d") %> status: <%= item.detailed_status %> (done: <%= item.done? %>)
9
+
4
10
  <%= item.body %>
5
11
 
6
12
  <% if item.notes.any? -%>
@@ -1,6 +1,6 @@
1
1
  db:
2
- seeds: <%= GEM_DIR %>/lib/db/seeds.rb
3
- migrate: <%= GEM_DIR %>/lib/db/migrate
4
- schema: <%= GEM_DIR %>/lib/db/schema.rb
2
+ seeds: <%= GEM_DIR %>/db/seeds.rb
3
+ migrate: <%= GEM_DIR %>/db/migrate
4
+ schema: <%= GEM_DIR %>/db/schema.rb
5
5
  config:
6
- database: <%= GEM_DIR %>/lib/db/config.yml
6
+ database: <%= GEM_DIR %>/db/config.yml
@@ -1,3 +1,3 @@
1
1
  module MyTodo
2
- VERSION = "3.1.1"
2
+ VERSION = "4.0.0.pre1"
3
3
  end
@@ -16,11 +16,11 @@ class Setup < Thor
16
16
  `mkdir -p #{HOME_DIR}/.my_todo/data`
17
17
  say "Created .my_todo in #{HOME_DIR}"
18
18
  end
19
- template "config.yml.erb", "#{__dir__}/../lib/db/config.yml", force: true
19
+ template "config.yml.erb", "#{GEM_DIR}/db/config.yml", force: true
20
20
  end
21
21
 
22
22
  desc 'standard_migrations_override', 'Generate SM override file'
23
23
  def standard_migrations_override
24
- template "standalone_migrations.yml.erb", "#{__dir__}/../.standalone_migrations", force: true
24
+ template "standalone_migrations.yml.erb", "#{GEM_DIR}/.standalone_migrations", force: true
25
25
  end
26
26
  end
@@ -14,28 +14,30 @@ Gem::Specification.new do |spec|
14
14
  spec.homepage = "https://github.com/vmcilwain/my_todo"
15
15
  spec.license = "MIT"
16
16
 
17
- spec.files = Dir["{bin,lib}/**/*", "LICENSE.txt", "README.md", ".standalone_migrations", 'Rakefile', 'Gemfile', 'my_todo.gemspec']
17
+ spec.files = Dir["{bin,lib,db}/**/*", "LICENSE.txt", "README.md", 'Rakefile', 'Gemfile', 'my_todo.gemspec']
18
18
  spec.test_files = Dir["spec/**/*"]
19
19
  spec.bindir = "bin"
20
- spec.executables = ['my_todo']
20
+ spec.executables = ['mytodo']
21
21
  spec.require_paths = ["lib"]
22
22
 
23
- spec.add_development_dependency "bundler", "~> 1.12"
24
- spec.add_development_dependency "rake", "~> 10.0"
25
- spec.add_development_dependency "rspec", "~> 3.0"
26
- spec.add_development_dependency "factory_girl_rails", "~> 4.7.0"
27
- spec.add_development_dependency "database_cleaner", "~> 1.5.3"
28
- spec.add_development_dependency 'shoulda-matchers', '~> 3.1'
29
- spec.add_development_dependency 'byebug', '~> 9.0.5'
23
+ spec.add_development_dependency "rspec", "~> 3.8.0"
24
+ spec.add_development_dependency "factory_bot_rails", "~> 4.11.1"
25
+ spec.add_development_dependency "database_cleaner", "~> 1.7.0"
26
+ spec.add_development_dependency 'shoulda-matchers', '~> 3.1.2'
27
+ spec.add_development_dependency 'byebug', '~> 10.0.2'
30
28
  spec.add_development_dependency 'yard', '~> 0.9.5'
31
- spec.add_development_dependency 'simplecov', '~> 0.12.0'
32
- spec.add_development_dependency 'codeclimate-test-reporter'
33
- spec.add_dependency 'activerecord', '~> 5.0.0.1'
34
- spec.add_dependency 'activesupport', '~> 5.0.0.1'
35
- spec.add_dependency 'thor', '~> 0.19.1'
36
- spec.add_dependency 'standalone_migrations', '~> 5.0.0'
37
- spec.add_dependency 'sqlite3', '~> 1.3.11'
38
- spec.add_dependency 'ransack', '~> 1.8.2'
29
+ spec.add_development_dependency 'simplecov', '~> 0.16.1'
30
+ spec.add_development_dependency 'faker'
31
+ spec.add_development_dependency 'rubocop-rspec'
32
+ # spec.add_development_dependency 'codeclimate-test-reporter'
33
+ spec.add_dependency "bundler", "~> 1.16.0"
34
+ spec.add_dependency "rake", "~> 12.3.1"
35
+ spec.add_dependency 'activerecord', '~> 5.2.1'
36
+ spec.add_dependency 'activesupport', '~> 5.2.1'
37
+ spec.add_dependency 'thor', '~> 0.20.0'
38
+ spec.add_dependency 'standalone_migrations', '~> 5.2.6'
39
+ spec.add_dependency 'sqlite3', '~> 1.3.13'
40
+ spec.add_dependency 'ransack', '~> 2.0.1'
39
41
 
40
42
  spec.metadata["yard.run"] = "yri"
41
43
  spec.post_install_message = "Don't forget to migrate the db. `my_todo rake db:migrate`"
@@ -6,9 +6,10 @@ describe MyTodo do
6
6
  end
7
7
 
8
8
  describe 'create' do
9
- before do
10
- expect(Thor::LineEditor).to receive(:readline).with("Choose a status for item (1) ", {:default=>1}).and_return("")
11
- end
9
+ let(:body) {Faker::Lorem.words(5).join("\s")}
10
+ let(:statuses) {"0: None\n1: In Progress\n2: Waiting Feedback\n3: Complete\n4: Punted"}
11
+
12
+ before {expect(Thor::LineEditor).to receive(:readline).with("Choose a status for item (1) ", {default:1}).and_return("")}
12
13
 
13
14
  context 'successful creation' do
14
15
  describe 'creation without tags or setting done attribute' do
@@ -17,46 +18,47 @@ describe MyTodo do
17
18
  end
18
19
 
19
20
  it 'creates a todo item' do
20
- expect{MyTodo::Todo.start(%w(create --body=wierdness_of_text))}.to change{Item.count}.by(1)
21
+ expect{MyTodo::Todo.start(['create', body])}.to change{Item.count}.by(1)
21
22
  end
22
23
 
23
24
  it "sets tags to 'default' when tags option is not present" do
24
- MyTodo::Todo.start(%w[create --body=wierdness_of_text])
25
+ MyTodo::Todo.start(['create', body])
25
26
  todo = Item.last
26
- expect(todo.tags.map(&:name)).to eq ['default']
27
+ expect(todo.tags.map(&:name)).to eq ['Default']
27
28
  end
28
29
 
29
30
  it 'sets done to false by when done option is not present' do
30
- MyTodo::Todo.start(%w[create --body=wierdness_of_text])
31
+ MyTodo::Todo.start(['create', body])
31
32
  todo = Item.last
32
33
  expect(todo.done).to eq false
33
34
  end
34
35
 
35
36
  it 'displays the created todo item' do
36
- expect{MyTodo::Todo.start(%w(create --body=wierdness_of_text))}.to output("ToDo CREATED!\n0: None\n1: In Progress\n2: Waiting Feedback\n3: Complete\n4: Punted\nID: 1 | Created On: #{Date.today} | Tags: default | Status: In Progress | Complete: false\nwierdness_of_text\n").to_stdout
37
+ expect{MyTodo::Todo.start(['create', body])}.to output("#{statuses}\nItem Created\n\nid: 1 notes: 0 tags: Default\ncreated: #{Date.today} status: In Progress (done: No) \n\n#{body}\n").to_stdout
37
38
  end
38
39
  end
39
40
 
40
41
  describe 'creation with tags and without setting done attribute' do
41
42
  it 'creates associated tag' do
42
- expect{MyTodo::Todo.start(%w[create --body=wierdness_of_text --tags=tag1])}.to change{Tag.count}.by(1)
43
+ expect{MyTodo::Todo.start(['create', body, 'tag1'])}.to change{Tag.count}.by(1)
43
44
  end
44
45
 
45
46
  it 'displays the created todo item with tag' do
46
- expect{MyTodo::Todo.start(%w(create --body=wierdness_of_text --tags=tag1))}.to output("ToDo CREATED!\n0: None\n1: In Progress\n2: Waiting Feedback\n3: Complete\n4: Punted\nID: 1 | Created On: #{Date.today} | Tags: tag1 | Status: In Progress | Complete: false\nwierdness_of_text\n").to_stdout
47
+ expect{MyTodo::Todo.start(['create', body, 'tag1'])}.to output("#{statuses}\nItem Created\n\nid: 1 notes: 0 tags: tag1\ncreated: #{Date.today} status: In Progress (done: No) \n\n#{body}\n").to_stdout
47
48
  end
48
49
  end
49
50
 
50
- describe 'create with tags' do
51
- it 'displays the created to item with complete set to true' do
52
- expect{MyTodo::Todo.start(%w[create --body=wierdness_of_text])}.to output("ToDo CREATED!\n0: None\n1: In Progress\n2: Waiting Feedback\n3: Complete\n4: Punted\nID: 1 | Created On: #{Date.today} | Tags: default | Status: In Progress | Complete: false\nwierdness_of_text\n").to_stdout
51
+ describe 'create a completed item' do
52
+ xit 'displays the created to item with complete set to true', 'look into being able to set this option again' do
53
+ expect{MyTodo::Todo.start(['create', body])}.to output("#{statuses}\nItem Created\n\nid: 1 notes: 0 tags: Default\ncreated: #{Date.today} status: In Progress (done: Yes) \n\n#{body}\n").to_stdout
53
54
  end
54
55
  end
55
56
  end
56
57
 
57
58
  context 'unsuccessful creation' do
58
- it 'returns error message when body is missing' do
59
- expect{MyTodo::Todo.start(%w(create))}.to output("ToDo CREATED!\n0: None\n1: In Progress\n2: Waiting Feedback\n3: Complete\n4: Punted\nValidation failed: Body can't be blank\n").to_stdout
59
+ xit 'returns error message when body is missing', 'look into being able to set this option again' do
60
+ expect{MyTodo::Todo.start(['create'])}.to output("ERROR: \"my_todo create\" was called with no arguments\n
61
+ Usage: \"my_todo create \"<BODY>\" <TAGS> [Default: general]>\"").to_stdout
60
62
  end
61
63
  end
62
64
  end
@@ -2,14 +2,20 @@ require 'spec_helper'
2
2
 
3
3
  describe MyTodo do
4
4
  describe 'delete' do
5
- before {@todo = FactoryGirl.create(:item)}
6
-
5
+ let(:todo) {FactoryBot.create(:item)}
6
+
7
+ before {todo}
8
+
7
9
  it 'destroys todo item' do
8
- expect{MyTodo::Todo.start(%W(delete --id=#{@todo.id}))}.to change{Item.count}.by(-1)
10
+ expect{MyTodo::Todo.start(['delete', todo.id])}.to change{Item.count}.by(-1)
9
11
  end
10
12
 
11
13
  it 'returns nil exception if todo item is invalid' do
12
- expect{MyTodo::Todo.start(%W[delete --id=#{@todo.id + 1}])}.to output("undefined method `destroy!' for nil:NilClass\n").to_stdout
14
+ expect{MyTodo::Todo.start(['delete', todo.id + 1])}.to output("undefined method `destroy!' for nil:NilClass\n").to_stdout
15
+ end
16
+
17
+ it 'displays a notice' do
18
+ expect{MyTodo::Todo.start(['delete', todo.id])}.to output("Item Deleted\n").to_stdout
13
19
  end
14
20
  end
15
21
  end