shellout 0.4 → 0.5
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.
- data/.ruby-version +1 -0
- data/.travis.yml +2 -0
- data/CHANGES +5 -0
- data/Gemfile +2 -3
- data/README.md +56 -53
- data/Rakefile +8 -1
- data/example/bin/hunger.rb +67 -0
- data/example/features/order_a_pizza.feature +12 -0
- data/example/features/start_the_app.feature +11 -0
- data/example/features/step_definitions/hunger_steps.rb +71 -0
- data/example/features/support/env.rb +1 -0
- data/example/features/view_order.feature +14 -0
- data/lib/shellout/calendar.rb +6 -2
- data/lib/shellout/command_loop.rb +21 -0
- data/lib/shellout/date.rb +55 -0
- data/lib/shellout/date_query.rb +50 -0
- data/lib/shellout/menu.rb +4 -2
- data/lib/shellout/menu_query.rb +51 -0
- data/lib/shellout/paragraph.rb +31 -0
- data/lib/shellout/query.rb +31 -0
- data/lib/shellout/task.rb +41 -0
- data/lib/shellout/version.rb +1 -1
- data/shellout.gemspec +6 -3
- data/spec/shellout/calendar_spec.rb +30 -1
- data/spec/shellout/command_loop_spec.rb +9 -0
- data/spec/shellout/date_query_spec.rb +9 -0
- data/spec/shellout/date_spec.rb +87 -0
- data/spec/shellout/menu_query_spec.rb +39 -0
- data/spec/shellout/menu_spec.rb +29 -18
- data/spec/shellout/paragraph_spec.rb +39 -0
- data/spec/shellout/query_spec.rb +39 -0
- data/spec/shellout/task_spec.rb +43 -0
- metadata +121 -19
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.9.3
|
data/.travis.yml
ADDED
data/CHANGES
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
[](http://travis-ci.org/kjellm/shellout)
|
2
|
+
|
1
3
|
Shellout
|
2
4
|
========
|
3
5
|
|
@@ -19,62 +21,63 @@ Usage
|
|
19
21
|
|
20
22
|
### Shadowboxes
|
21
23
|
|
22
|
-
Shadowbox("Hello world").print
|
24
|
+
> Shadowbox("Hello world").print
|
23
25
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
26
|
+
┌──────────────────────────────────────────┐
|
27
|
+
│ Hello world │▒
|
28
|
+
└──────────────────────────────────────────┘▒
|
29
|
+
▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
|
28
30
|
|
29
31
|
### Tables
|
30
32
|
|
31
|
-
Table(headers: %w{Team Points},
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
33
|
+
> Table(headers: %w{Team Points},
|
34
|
+
rows: [['Man City', 22],
|
35
|
+
['Man Utd', 20],
|
36
|
+
['Chelsea', 19],
|
37
|
+
['Newcastle', 16]]).print
|
36
38
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
39
|
+
┌───────────┬────────┐
|
40
|
+
│ Team │ Points │
|
41
|
+
├───────────┼────────┤
|
42
|
+
│ Man City │ 22 │
|
43
|
+
│ Man Utd │ 20 │
|
44
|
+
│ Chelsea │ 19 │
|
45
|
+
│ Newcastle │ 16 │
|
46
|
+
└───────────┴────────┘
|
45
47
|
|
46
48
|
|
47
49
|
### Calendars
|
48
50
|
|
49
|
-
today = Date.today
|
51
|
+
> today = Date.today
|
52
|
+
=> #<Date: 2011-10-31 (...)>
|
50
53
|
|
51
|
-
Calendar(today).print
|
54
|
+
> Calendar(today).print
|
52
55
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
56
|
+
October 2011
|
57
|
+
Mo Tu We Th Fr Sa Su
|
58
|
+
1 2
|
59
|
+
3 4 5 6 7 8 9
|
60
|
+
10 11 12 13 14 15 16
|
61
|
+
17 18 19 20 21 22 23
|
62
|
+
24 25 26 27 28 29 30
|
63
|
+
31
|
61
64
|
|
62
|
-
Calendar(today).print3
|
65
|
+
> Calendar(today).print3
|
63
66
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
67
|
+
2011
|
68
|
+
September October November
|
69
|
+
Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su
|
70
|
+
1 2 3 4 1 2 1 2 3 4 5 6
|
71
|
+
5 6 7 8 9 10 11 3 4 5 6 7 8 9 7 8 9 10 11 12 13
|
72
|
+
12 13 14 15 16 17 18 10 11 12 13 14 15 16 14 15 16 17 18 19 20
|
73
|
+
19 20 21 22 23 24 25 17 18 19 20 21 22 23 21 22 23 24 25 26 27
|
74
|
+
26 27 28 29 30 24 25 26 27 28 29 30 28 29 30
|
75
|
+
31
|
73
76
|
|
74
77
|
|
75
78
|
### Menus
|
76
79
|
|
77
|
-
Menu([
|
80
|
+
> Menu([
|
78
81
|
"Do this",
|
79
82
|
"No, this",
|
80
83
|
"Or, maybe this",
|
@@ -89,18 +92,18 @@ Usage
|
|
89
92
|
"Might as well choose this one",
|
90
93
|
]).print
|
91
94
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
95
|
+
1. Do this
|
96
|
+
2. No, this
|
97
|
+
3. Or, maybe this
|
98
|
+
4. Forget it, this is the right choice
|
99
|
+
5. Amazing things will happen if you choose this
|
100
|
+
6. Ignore #5. Pick me!
|
101
|
+
7. I'm the obvious choice
|
102
|
+
8. There is no end to the awesomeness that will come from choosing this
|
103
|
+
9. Don't choose this
|
104
|
+
10. Or this
|
105
|
+
11. Things are getting worse
|
106
|
+
12. Might as well choose this one
|
104
107
|
|
105
108
|
Bugs
|
106
109
|
----
|
@@ -111,7 +114,7 @@ Report bugs to <http://github.com/kjellm/shellout/issues>
|
|
111
114
|
Author
|
112
115
|
------
|
113
116
|
|
114
|
-
Kjell-Magne Øierud <kjellm AT
|
117
|
+
Kjell-Magne Øierud <kjellm AT oierud DOT net>
|
115
118
|
|
116
119
|
|
117
120
|
License
|
@@ -119,7 +122,7 @@ License
|
|
119
122
|
|
120
123
|
(The MIT License)
|
121
124
|
|
122
|
-
Copyright © 2011 Kjell-Magne Øierud
|
125
|
+
Copyright © 2011-2013 Kjell-Magne Øierud
|
123
126
|
|
124
127
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
125
128
|
associated documentation files (the ‘Software’), to deal in the Software without restriction, including
|
data/Rakefile
CHANGED
@@ -0,0 +1,67 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'shellout'
|
4
|
+
require 'shellout/command_loop'
|
5
|
+
require 'shellout/date_query'
|
6
|
+
require 'shellout/menu_query'
|
7
|
+
require 'shellout/task'
|
8
|
+
require 'shellout/query'
|
9
|
+
|
10
|
+
class App
|
11
|
+
|
12
|
+
include Shellout
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
@session = []
|
16
|
+
end
|
17
|
+
|
18
|
+
def define_course_task(dishes)
|
19
|
+
Task.new do |t|
|
20
|
+
t.dish = MenuQuery.new(dishes)
|
21
|
+
t.quantity = Query.new('How many?', 1)
|
22
|
+
t.printf("%{quantity} %{dish} added to your order\n")
|
23
|
+
t.on_call_done do
|
24
|
+
@session << t
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def main
|
30
|
+
|
31
|
+
starters_task = define_course_task(%w(Gazpacho Bruschetta))
|
32
|
+
main_course_task = define_course_task(%w(Pizza Pasta))
|
33
|
+
desserts_task = define_course_task(%w(Gelato Tiramisu))
|
34
|
+
|
35
|
+
checkout_task = Task.new do |t|
|
36
|
+
t.date = DateQuery.new
|
37
|
+
t.name = Query.new("Your name")
|
38
|
+
t.on_call_done do
|
39
|
+
confirmed = Query.new("Confirm (y|n)").call
|
40
|
+
@session = [] if confirmed == 'y'
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
view_order_task = ->do
|
45
|
+
#FIXME ugly
|
46
|
+
rows = @session.map do |t|
|
47
|
+
[t.instance_variable_get(:@results)[:quantity], t.instance_variable_get(:@results)[:dish]]
|
48
|
+
end
|
49
|
+
Table(headers: %w(quantity dish), rows: rows).print
|
50
|
+
end
|
51
|
+
|
52
|
+
main_menu_items = {
|
53
|
+
"Starters" => starters_task,
|
54
|
+
"Main courses" => main_course_task,
|
55
|
+
"Desserts" => desserts_task,
|
56
|
+
"View Order" => view_order_task,
|
57
|
+
"Checkout" => checkout_task,
|
58
|
+
"Exit" => ->{ exit }
|
59
|
+
}
|
60
|
+
main_menu = MenuQuery.new(main_menu_items, true)
|
61
|
+
|
62
|
+
Shadowbox("Give up to your hunger!").print
|
63
|
+
CommandLoop.new(main_menu).call
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
App.new.main
|
@@ -0,0 +1,12 @@
|
|
1
|
+
Feature: Order a Pizza
|
2
|
+
As a pathetic person who is unable to make my own supper
|
3
|
+
I want to order a pizza for tomorrows supper
|
4
|
+
In order to not starve to death
|
5
|
+
|
6
|
+
Scenario: Order a Pizza
|
7
|
+
Given that hunger is running
|
8
|
+
When I choose main course from the main menu
|
9
|
+
And I choose Pizza from the main course menu
|
10
|
+
And I choose the default quantity
|
11
|
+
And I quit
|
12
|
+
Then I should see "1 Pizza added to your order"
|
@@ -0,0 +1,11 @@
|
|
1
|
+
Feature: Start the app
|
2
|
+
As a user of the system
|
3
|
+
I want to be able to start the system
|
4
|
+
In order to be able to use the system
|
5
|
+
|
6
|
+
Scenario: Start the app
|
7
|
+
Given that hunger is not running
|
8
|
+
When I start hunger
|
9
|
+
And I quit
|
10
|
+
Then I shall see a greeting
|
11
|
+
And I shall see the main menu
|
@@ -0,0 +1,71 @@
|
|
1
|
+
Given /^that hunger is not running$/ do
|
2
|
+
end
|
3
|
+
|
4
|
+
When /^I start hunger$/ do
|
5
|
+
steps %{When I run `hunger.rb` interactively}
|
6
|
+
end
|
7
|
+
|
8
|
+
When /^I quit$/ do
|
9
|
+
steps %{When I type "6"}
|
10
|
+
end
|
11
|
+
|
12
|
+
Then /^I shall see a greeting$/ do
|
13
|
+
steps %{Then the stdout should contain "Give up to your hunger!"}
|
14
|
+
end
|
15
|
+
|
16
|
+
Then /^I shall see the main menu$/ do
|
17
|
+
steps %{Then the stdout should contain:
|
18
|
+
"""
|
19
|
+
1. Starters
|
20
|
+
2. Main courses
|
21
|
+
3. Desserts
|
22
|
+
4. View Order
|
23
|
+
5. Checkout
|
24
|
+
0. Exit
|
25
|
+
"""
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
Given /^that hunger is running$/ do
|
30
|
+
steps %{When I start hunger}
|
31
|
+
end
|
32
|
+
|
33
|
+
When /^I choose main course from the main menu$/ do
|
34
|
+
steps %{When I type "2"}
|
35
|
+
end
|
36
|
+
|
37
|
+
When /^I choose Pizza from the main course menu$/ do
|
38
|
+
steps %{When I type "1"}
|
39
|
+
end
|
40
|
+
|
41
|
+
When /^I choose the default quantity$/ do
|
42
|
+
steps %{When I type ""}
|
43
|
+
end
|
44
|
+
|
45
|
+
Then /^I should see "([^"]*)"$/ do |arg1|
|
46
|
+
steps %{Then the stdout should contain "#{arg1}"}
|
47
|
+
end
|
48
|
+
|
49
|
+
Given /^I order (\d+) Pizza$/ do |quantity|
|
50
|
+
steps %{
|
51
|
+
When I type "2"
|
52
|
+
When I type "1"
|
53
|
+
When I type "#{quantity}"
|
54
|
+
}
|
55
|
+
end
|
56
|
+
|
57
|
+
Given /^I order (\d+) Gelato$/ do |quantity|
|
58
|
+
steps %{
|
59
|
+
When I type "3"
|
60
|
+
When I type "1"
|
61
|
+
When I type "#{quantity}"
|
62
|
+
}
|
63
|
+
end
|
64
|
+
|
65
|
+
Given /^I choose to view ordered dishes$/ do
|
66
|
+
steps %{When I type "4"}
|
67
|
+
end
|
68
|
+
|
69
|
+
Then /^I should see:$/ do |string|
|
70
|
+
steps %{Then the stdout should contain "#{string}"}
|
71
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'aruba/cucumber'
|
@@ -0,0 +1,14 @@
|
|
1
|
+
Feature: View ordered dishes
|
2
|
+
As a user
|
3
|
+
I want to be able to see what I have ordered
|
4
|
+
So that I can verify that the system has gotten it right
|
5
|
+
|
6
|
+
Scenario: view ordered dishes
|
7
|
+
Given that hunger is running
|
8
|
+
And I order 1 Pizza
|
9
|
+
And I order 2 Gelato
|
10
|
+
And I choose to view ordered dishes
|
11
|
+
And I quit
|
12
|
+
Then I should see "┌──────────┬────────┐\n│ quantity │ dish │\n├──────────┼────────┤\n│ 1 │ Pizza │\n│ 2 │ Gelato │\n└──────────┴────────┘"
|
13
|
+
#FIXME Could not get multiline pystrings to work (lexing error)
|
14
|
+
|
data/lib/shellout/calendar.rb
CHANGED
@@ -63,7 +63,7 @@ module Shellout
|
|
63
63
|
# FIXME: UGLY!!!
|
64
64
|
foo = dates.map do |for_date|
|
65
65
|
bar = days(for_date)
|
66
|
-
if is_current_month?(for_date)
|
66
|
+
if is_current_month?(for_date) && is_current_year?(for_date)
|
67
67
|
bar.map! {|d| d == Date.today.day ? ansi_reverse_color(d) : d}
|
68
68
|
end
|
69
69
|
bar
|
@@ -91,6 +91,10 @@ module Shellout
|
|
91
91
|
Date.today.month == date.month
|
92
92
|
end
|
93
93
|
|
94
|
+
def is_current_year?(date)
|
95
|
+
Date.today.year == date.year
|
96
|
+
end
|
97
|
+
|
94
98
|
def days(for_date)
|
95
99
|
days = (1..last_day_of_month(for_date).day).to_a
|
96
100
|
unshift_empty_strings_for_missing_week(days, for_date)
|
@@ -117,4 +121,4 @@ module Shellout
|
|
117
121
|
|
118
122
|
|
119
123
|
end
|
120
|
-
end
|
124
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
|
2
|
+
module Shellout
|
3
|
+
class CommandLoop
|
4
|
+
|
5
|
+
def initialize(menu)
|
6
|
+
@menu = menu
|
7
|
+
end
|
8
|
+
|
9
|
+
def call
|
10
|
+
loop do
|
11
|
+
begin
|
12
|
+
task = @menu.call
|
13
|
+
task.call
|
14
|
+
rescue Interrupt # ^C
|
15
|
+
puts # Add a new line in case we are prompting
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'date'
|
2
|
+
|
3
|
+
module Shellout
|
4
|
+
class Date < ::Date
|
5
|
+
|
6
|
+
def self.from_str(date_str, base=Date.today)
|
7
|
+
raise ArgumentError.new("Can't convert nil to a Date") \
|
8
|
+
if date_str.nil?
|
9
|
+
|
10
|
+
args = [date_str, base]
|
11
|
+
return parse_str_from_english(*args) \
|
12
|
+
|| parse_str_from_offset(*args) \
|
13
|
+
|| parse_str_from_ymd(*args) \
|
14
|
+
|| (raise ArgumentError.new(
|
15
|
+
"Failed to convert String(#{date_str}) to a Date"))
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def self.parse_str_from_english(date_str, base)
|
21
|
+
return base if date_str == 'today' || date_str.empty?
|
22
|
+
return nil
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.parse_str_from_offset(date_str, base)
|
26
|
+
return case date_str.chars.first
|
27
|
+
when '-'
|
28
|
+
base - Integer(date_str[1..-1])
|
29
|
+
when '+'
|
30
|
+
base + Integer(date_str[1..-1])
|
31
|
+
else
|
32
|
+
nil
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.parse_str_from_ymd(date_str, base)
|
37
|
+
date = date_str.split('-').collect {|d| d.to_i}
|
38
|
+
case date.length
|
39
|
+
when 1
|
40
|
+
return Date.civil(base.year, base.month, *date)
|
41
|
+
when 2
|
42
|
+
return Date.civil(base.year, *date)
|
43
|
+
when 3
|
44
|
+
if date[0] < 70
|
45
|
+
date[0] += 2000
|
46
|
+
elsif date[0] < 100
|
47
|
+
date[0] += 1900
|
48
|
+
end
|
49
|
+
return Date.civil(*date)
|
50
|
+
end
|
51
|
+
return nil
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|