the_dude 0.0.2
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/.gitignore +3 -0
- data/.rspec +1 -0
- data/.rvmrc +1 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +55 -0
- data/README.md +40 -0
- data/Rakefile +44 -0
- data/bin/dude +35 -0
- data/features/basic.feature +13 -0
- data/features/duderc.feature +27 -0
- data/features/step_definitions/the_dude_steps.rb +8 -0
- data/features/support/env.rb +15 -0
- data/lib/the_dude.rb +128 -0
- data/lib/the_dude/command.rb +61 -0
- data/lib/the_dude/config.rb +5 -0
- data/lib/the_dude/dsl.rb +7 -0
- data/lib/the_dude/expression.rb +66 -0
- data/lib/the_dude/http.rb +28 -0
- data/lib/the_dude/setup.rb +21 -0
- data/lib/the_dude/variable.rb +25 -0
- data/lib/the_dude/version.rb +3 -0
- data/spec/spec_helper.rb +11 -0
- data/spec/support/webmocks.rb +31 -0
- data/spec/support/webmocks/google2.html +92 -0
- data/spec/the_dude/command_spec.rb +68 -0
- data/spec/the_dude/config_spec.rb +8 -0
- data/spec/the_dude/dsl_spec.rb +41 -0
- data/spec/the_dude/expression_spec.rb +76 -0
- data/spec/the_dude/http_spec.rb +48 -0
- data/spec/the_dude/variable_spec.rb +30 -0
- data/spec/the_dude_spec.rb +68 -0
- data/the_dude.gemspec +25 -0
- metadata +212 -0
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--format d --color
|
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm use --create 1.9.3@dude
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
the_dude (0.0.2)
|
5
|
+
colored
|
6
|
+
hirb
|
7
|
+
nokogiri
|
8
|
+
|
9
|
+
GEM
|
10
|
+
remote: https://rubygems.org/
|
11
|
+
specs:
|
12
|
+
aruba (0.5.2)
|
13
|
+
childprocess (~> 0.3.6)
|
14
|
+
cucumber (>= 1.1.1)
|
15
|
+
rspec-expectations (>= 2.7.0)
|
16
|
+
builder (3.0.4)
|
17
|
+
childprocess (0.3.9)
|
18
|
+
ffi (~> 1.0, >= 1.0.11)
|
19
|
+
colored (1.2)
|
20
|
+
cucumber (1.3.1)
|
21
|
+
builder (>= 2.1.2)
|
22
|
+
diff-lcs (>= 1.1.3)
|
23
|
+
gherkin (~> 2.12.0)
|
24
|
+
multi_json (~> 1.3)
|
25
|
+
diff-lcs (1.2.4)
|
26
|
+
fakeweb (1.3.0)
|
27
|
+
ffi (1.8.1)
|
28
|
+
gherkin (2.12.0)
|
29
|
+
multi_json (~> 1.3)
|
30
|
+
hirb (0.7.1)
|
31
|
+
json (1.7.7)
|
32
|
+
multi_json (1.7.3)
|
33
|
+
nokogiri (1.5.9)
|
34
|
+
rake (10.0.4)
|
35
|
+
rdoc (4.0.1)
|
36
|
+
json (~> 1.4)
|
37
|
+
rspec (2.13.0)
|
38
|
+
rspec-core (~> 2.13.0)
|
39
|
+
rspec-expectations (~> 2.13.0)
|
40
|
+
rspec-mocks (~> 2.13.0)
|
41
|
+
rspec-core (2.13.1)
|
42
|
+
rspec-expectations (2.13.0)
|
43
|
+
diff-lcs (>= 1.1.3, < 2.0)
|
44
|
+
rspec-mocks (2.13.1)
|
45
|
+
|
46
|
+
PLATFORMS
|
47
|
+
ruby
|
48
|
+
|
49
|
+
DEPENDENCIES
|
50
|
+
aruba
|
51
|
+
fakeweb
|
52
|
+
rake
|
53
|
+
rdoc
|
54
|
+
rspec
|
55
|
+
the_dude!
|
data/README.md
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# The Dude
|
2
|
+
|
3
|
+
[](https://codeclimate.com/github/adamphillips/the\_dude)
|
4
|
+
|
5
|
+
The Dude is here to make your terminal life more chilled.
|
6
|
+
|
7
|
+
You preload The Dude with your commonly used commands. Then you run them.
|
8
|
+
Alternatively, The Dude already knows how to do a bunch of stuff for you.
|
9
|
+
|
10
|
+
For example:
|
11
|
+
|
12
|
+
$ dude fetch google.co.uk # opens a google.co.uk in lynx
|
13
|
+
$ dude show me pictures of cool stuff # opens a browser with a google images search for 'cool stuff'
|
14
|
+
$ dude wassup # starts top
|
15
|
+
|
16
|
+
If this sounds like fancypants bash aliases, you'd be about right. So what's the point?
|
17
|
+
|
18
|
+
Well, there's a few reasons
|
19
|
+
- a single dude command can trigger a bunch of other commands. Sure you
|
20
|
+
could write a shell script and run that using an alias but that assumes
|
21
|
+
you're happy writing shell scripts.
|
22
|
+
- for the command-line phobic, dude provides a friendly interface to the
|
23
|
+
command line.
|
24
|
+
- for those who are happy on the command line, there can still be a benefit
|
25
|
+
in reducing the cognitive overhead required to run scripts
|
26
|
+
|
27
|
+
## Talking to The Dude
|
28
|
+
|
29
|
+
The Dude is still very young and not on rubygems quite yet. For the mean
|
30
|
+
time, you will need to checkout the repo and build the gem.
|
31
|
+
|
32
|
+
The Dude comes with a 'dude' binary so you can use this to run commands
|
33
|
+
|
34
|
+
dude why? # will output 'because'
|
35
|
+
|
36
|
+
Alternatively, you can start the dude inteactively with
|
37
|
+
|
38
|
+
dude -i
|
39
|
+
|
40
|
+
Then you get a dude prompt you can enter commands straight into.
|
data/Rakefile
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'rake/clean'
|
2
|
+
require 'rubygems'
|
3
|
+
require 'rubygems/package_task'
|
4
|
+
require 'rdoc/task'
|
5
|
+
require 'cucumber'
|
6
|
+
require 'cucumber/rake/task'
|
7
|
+
Rake::RDocTask.new do |rd|
|
8
|
+
rd.main = "README.rdoc"
|
9
|
+
rd.rdoc_files.include("README.rdoc","lib/**/*.rb","bin/**/*")
|
10
|
+
rd.title = 'Your application title'
|
11
|
+
end
|
12
|
+
|
13
|
+
spec = eval(File.read('the_dude.gemspec'))
|
14
|
+
|
15
|
+
Gem::PackageTask.new(spec) do |pkg|
|
16
|
+
end
|
17
|
+
CUKE_RESULTS = 'results.html'
|
18
|
+
CLEAN << CUKE_RESULTS
|
19
|
+
desc 'Run features'
|
20
|
+
Cucumber::Rake::Task.new(:features) do |t|
|
21
|
+
opts = "features --format html -o #{CUKE_RESULTS} --format progress -x"
|
22
|
+
opts += " --tags #{ENV['TAGS']}" if ENV['TAGS']
|
23
|
+
t.cucumber_opts = opts
|
24
|
+
t.fork = false
|
25
|
+
end
|
26
|
+
|
27
|
+
desc 'Run features tagged as work-in-progress (@wip)'
|
28
|
+
Cucumber::Rake::Task.new('features:wip') do |t|
|
29
|
+
tag_opts = ' --tags ~@pending'
|
30
|
+
tag_opts = ' --tags @wip'
|
31
|
+
t.cucumber_opts = "features --format html -o #{CUKE_RESULTS} --format pretty -x -s#{tag_opts}"
|
32
|
+
t.fork = false
|
33
|
+
end
|
34
|
+
|
35
|
+
task :cucumber => :features
|
36
|
+
task 'cucumber:wip' => 'features:wip'
|
37
|
+
task :wip => 'features:wip'
|
38
|
+
require 'rake/testtask'
|
39
|
+
Rake::TestTask.new do |t|
|
40
|
+
t.libs << "test"
|
41
|
+
t.test_files = FileList['test/*_test.rb']
|
42
|
+
end
|
43
|
+
|
44
|
+
task :default => [:test,:features]
|
data/bin/dude
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'the_dude'
|
3
|
+
|
4
|
+
include TheDude::Dsl
|
5
|
+
|
6
|
+
DUDERC_PATH = "#{ENV['HOME']}/.duderc"
|
7
|
+
load DUDERC_PATH if File.exists? DUDERC_PATH
|
8
|
+
|
9
|
+
# Setup rlwrap for readline support
|
10
|
+
if ARGV.include? '-i'
|
11
|
+
interactive_mode = true
|
12
|
+
if !ENV['__REPL_WRAPPED'] && system("which rlwrap > /dev/null 2> /dev/null")
|
13
|
+
ENV['__REPL_WRAPPED'] = '0'
|
14
|
+
|
15
|
+
exec "rlwrap #{Shellwords.escape(__FILE__)} #{ARGV.join(' ')}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
question = ARGV.join(' ')
|
20
|
+
|
21
|
+
if interactive_mode
|
22
|
+
loop do
|
23
|
+
print "dude, "
|
24
|
+
begin
|
25
|
+
question = $stdin.gets.chomp
|
26
|
+
rescue NoMethodError, Interrupt
|
27
|
+
exit
|
28
|
+
end
|
29
|
+
|
30
|
+
puts TheDude.ask question
|
31
|
+
puts ''
|
32
|
+
end
|
33
|
+
else
|
34
|
+
puts TheDude.ask question
|
35
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
Feature: Basic interaction with the Dude
|
2
|
+
|
3
|
+
Scenario: App just runs
|
4
|
+
When I get help for "dude"
|
5
|
+
Then the exit status should be 0
|
6
|
+
|
7
|
+
Scenario: Asking a question
|
8
|
+
When I run `dude why?`
|
9
|
+
Then the output should contain "because"
|
10
|
+
|
11
|
+
Scenario: Asking a question that requires running another command
|
12
|
+
When I run `dude date`
|
13
|
+
Then the output should contain today's date
|
@@ -0,0 +1,27 @@
|
|
1
|
+
Feature: Reading from ~/.duderc
|
2
|
+
It should be possible to configure the dude from a duderc file
|
3
|
+
|
4
|
+
Scenario: File doesn't exist should still work
|
5
|
+
When I run `dude`
|
6
|
+
Then the exit status should be 0
|
7
|
+
|
8
|
+
#Scenario: File containing a question definition
|
9
|
+
#Given a file named "~/.duderc" with:
|
10
|
+
#"""
|
11
|
+
#command "another question" do
|
12
|
+
#"another answer"
|
13
|
+
#end
|
14
|
+
#"""
|
15
|
+
#When I run `dude another question`
|
16
|
+
#Then the output should contain "another answer"
|
17
|
+
|
18
|
+
#Scenario: File containing a question definition
|
19
|
+
#Given a file named "~/.duderc" with:
|
20
|
+
#"""
|
21
|
+
#command "say something" do
|
22
|
+
#`echo "hello"`
|
23
|
+
#end
|
24
|
+
#"""
|
25
|
+
#When I run `dude another question`
|
26
|
+
#Then the output should contain exactly "hello"
|
27
|
+
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'aruba/cucumber'
|
2
|
+
|
3
|
+
ENV['PATH'] = "#{File.expand_path(File.dirname(__FILE__) + '/../../bin')}#{File::PATH_SEPARATOR}#{ENV['PATH']}"
|
4
|
+
LIB_DIR = File.join(File.expand_path(File.dirname(__FILE__)),'..','..','lib')
|
5
|
+
|
6
|
+
Before do
|
7
|
+
# Using "announce" causes massive warnings on 1.9.2
|
8
|
+
@puts = true
|
9
|
+
@original_rubylib = ENV['RUBYLIB']
|
10
|
+
ENV['RUBYLIB'] = LIB_DIR + File::PATH_SEPARATOR + ENV['RUBYLIB'].to_s
|
11
|
+
end
|
12
|
+
|
13
|
+
After do
|
14
|
+
ENV['RUBYLIB'] = @original_rubylib
|
15
|
+
end
|
data/lib/the_dude.rb
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
require 'date'
|
2
|
+
require 'shellwords'
|
3
|
+
|
4
|
+
require 'hirb'
|
5
|
+
require 'colored'
|
6
|
+
|
7
|
+
require 'the_dude/command'
|
8
|
+
require 'the_dude/config'
|
9
|
+
require 'the_dude/dsl'
|
10
|
+
require 'the_dude/expression'
|
11
|
+
require 'the_dude/http'
|
12
|
+
require 'the_dude/variable'
|
13
|
+
require 'the_dude/version'
|
14
|
+
|
15
|
+
module TheDude
|
16
|
+
|
17
|
+
class << self
|
18
|
+
# Asks the dude a question
|
19
|
+
def ask question
|
20
|
+
command = find_command_for question
|
21
|
+
return "Wtf? What do you mean #{question}" unless command
|
22
|
+
arguments = arguments_for question, command
|
23
|
+
|
24
|
+
# this is a nasty way of dealing with the fact that we get question
|
25
|
+
# returned if it exactly matches the command
|
26
|
+
arguments = nil if arguments == question
|
27
|
+
|
28
|
+
command.ask *arguments
|
29
|
+
end
|
30
|
+
|
31
|
+
# @return [Hash] Returns the commands the dude knows about
|
32
|
+
def commands
|
33
|
+
@commands || {}
|
34
|
+
end
|
35
|
+
|
36
|
+
# Outputs something using angry-dude-formatting
|
37
|
+
def complain something
|
38
|
+
puts angry_dude_format something
|
39
|
+
end
|
40
|
+
|
41
|
+
# @return [Hash] Returns the variables the dude knows about
|
42
|
+
def variables
|
43
|
+
@variables || {}
|
44
|
+
end
|
45
|
+
|
46
|
+
# Registers a new command with the dude
|
47
|
+
#
|
48
|
+
# @param [TheDude::Command] command The command to register
|
49
|
+
def register_command command
|
50
|
+
@commands ||= {}
|
51
|
+
@commands[command.question] = command
|
52
|
+
end
|
53
|
+
|
54
|
+
# Registers a new variable with the dude
|
55
|
+
#
|
56
|
+
# @param [TheDude::Variable] variable The variable to register
|
57
|
+
def register_variable variable
|
58
|
+
@variables ||= {}
|
59
|
+
@variables[variable.name] = variable
|
60
|
+
end
|
61
|
+
|
62
|
+
# Resets the dude
|
63
|
+
def reset
|
64
|
+
reset_commands
|
65
|
+
reset_variables
|
66
|
+
end
|
67
|
+
|
68
|
+
# Outputs something use dude-formatting
|
69
|
+
def say something
|
70
|
+
puts dude_format something
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
# @return [String] Applies angry dude formatting for error messages
|
76
|
+
def angry_dude_format string
|
77
|
+
string
|
78
|
+
end
|
79
|
+
|
80
|
+
# Returns the arguments for a question based on the specified command
|
81
|
+
#
|
82
|
+
# @param [String] question The question
|
83
|
+
# @param [TheDude::Command] command The command
|
84
|
+
#
|
85
|
+
# @return [Array] An array of arguments
|
86
|
+
def arguments_for question, command
|
87
|
+
question.scan(command.question)[0]
|
88
|
+
end
|
89
|
+
|
90
|
+
# @return [String] Applies dude formatting
|
91
|
+
def dude_format string
|
92
|
+
string
|
93
|
+
end
|
94
|
+
|
95
|
+
# Returns the command for answering the specified question
|
96
|
+
#
|
97
|
+
# @param [String] question The question asked
|
98
|
+
#
|
99
|
+
# @return [TheDude::Command]
|
100
|
+
def find_command_for question
|
101
|
+
# if there's an exact match return that first
|
102
|
+
return commands[question] if commands[question]
|
103
|
+
|
104
|
+
commands.each do |key, val|
|
105
|
+
if key.kind_of? Regexp
|
106
|
+
return val if key =~ question
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
return false
|
111
|
+
end
|
112
|
+
|
113
|
+
# Resets the collection of commands
|
114
|
+
def reset_commands
|
115
|
+
@commands = {}
|
116
|
+
end
|
117
|
+
|
118
|
+
# Resets the collection of commands
|
119
|
+
def reset_variables
|
120
|
+
@variables = {}
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
124
|
+
|
125
|
+
class UndefinedVariableError < Exception; end
|
126
|
+
end
|
127
|
+
|
128
|
+
require 'the_dude/setup' # Load default commands and variables
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module TheDude
|
2
|
+
class Command
|
3
|
+
# [String | Regexp] The question for this command
|
4
|
+
attr_reader :question
|
5
|
+
|
6
|
+
# [String | Proc | Block] The answer for this command
|
7
|
+
attr_accessor :answer
|
8
|
+
|
9
|
+
# Initializes a new command.
|
10
|
+
#
|
11
|
+
# An instance is created to hold the details of the command and the command
|
12
|
+
# is registered in TheDude.commands using the provided key. The answer can
|
13
|
+
# be a string, proc or block. If both a string/proc and block are passed,
|
14
|
+
# the block will take precedence.
|
15
|
+
#
|
16
|
+
# @example
|
17
|
+
#
|
18
|
+
# command = TheDude::Command.new 'hello', 'hello'
|
19
|
+
# TheDude.commands['hello'] == command # true
|
20
|
+
#
|
21
|
+
# TheDude::Command.new 'hello' do
|
22
|
+
# puts 'hello'
|
23
|
+
# puts 'how are you?'
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
# @param [String | Regexp] question the question to ask
|
27
|
+
# @param [String | Proc] answer the answer to the question
|
28
|
+
#
|
29
|
+
# @return [TheDude::Command]
|
30
|
+
def initialize question, answer=nil, &block_answer
|
31
|
+
@question = Expression.new(question).to_regex
|
32
|
+
@answer = block_answer || answer
|
33
|
+
|
34
|
+
TheDude.register_command self
|
35
|
+
end
|
36
|
+
|
37
|
+
# Asks the question with the specified arguments. If the answer is a string
|
38
|
+
# it is returned. If it is a proc or block it is evaluated with the passed
|
39
|
+
# parameters.
|
40
|
+
def ask *args
|
41
|
+
begin
|
42
|
+
if answer.kind_of? Proc
|
43
|
+
instance_exec(*args, &answer)
|
44
|
+
else
|
45
|
+
answer
|
46
|
+
end
|
47
|
+
rescue SystemExit
|
48
|
+
exit
|
49
|
+
rescue Interrupt
|
50
|
+
TheDude.say 'Fine, i\'ll leave it then'
|
51
|
+
rescue Exception => e
|
52
|
+
TheDude.complain "Man, what are you doing? Look what you just did\n
|
53
|
+
#{e.class}
|
54
|
+
#{e.message}
|
55
|
+
#{e.backtrace}
|
56
|
+
At least you didn't pee on my rug I guess
|
57
|
+
"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|