shellout 0.4 → 0.5
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Build Status](https://secure.travis-ci.org/kjellm/shellout.png)](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
|