rubikon 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,25 @@
1
+ Copyright (c) 2009, Sebastian Staudt
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without modification,
5
+ are permitted provided that the following conditions are met:
6
+
7
+ * Redistributions of source code must retain the above copyright notice,
8
+ this list of conditions and the following disclaimer.
9
+ * Redistributions in binary form must reproduce the above copyright notice,
10
+ this list of conditions and the following disclaimer in the documentation
11
+ and/or other materials provided with the distribution.
12
+ * Neither the name of the author nor the names of its contributors
13
+ may be used to endorse or promote products derived from this software
14
+ without specific prior written permission.
15
+
16
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
20
+ ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
23
+ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/README.md ADDED
@@ -0,0 +1,129 @@
1
+ Rubikon
2
+ =======
3
+
4
+ Rubikon is a simple to use, yet powerful Ruby framework for building
5
+ console-based applications.
6
+
7
+ ### Installation
8
+
9
+ You can install Rubikon using RubyGem. This is the easiest way of installing
10
+ and recommended for most users.
11
+
12
+ $ gem install rubikon
13
+
14
+ If you want to use the development code you should clone the Git repository:
15
+
16
+ $ git clone git://github.com/koraktor/rubikon.git
17
+ $ cd rubikon
18
+ $ rake install
19
+
20
+ ## Usage
21
+
22
+ Creating a Rubikon application is as simple as creating a Ruby class:
23
+
24
+ require 'rubygems'
25
+ require 'rubikon'
26
+
27
+ class MyApplication < Rubikon::Application
28
+ end
29
+
30
+ If you save this code in a file called `myapp.rb` you can run it using
31
+ `ruby myapp.rb`. Or you could even add a *shebang* (`#!/usr/bin/env ruby`) to
32
+ the top of the file and make it executable. You would then be able to run it
33
+ even more easy by typing `./myapp.rb`.
34
+
35
+ Now go on and define what your application should do when the user runs it.
36
+ This is done using `default`:
37
+
38
+ class MyApplication < Rubikon::Application
39
+
40
+ default do
41
+ puts 'Hello World!'
42
+ end
43
+
44
+ end
45
+
46
+ If you run this application it will just print `Hello World!`.
47
+
48
+ You can also add command-line options to your appication using `action`:
49
+
50
+ class MyApplication < Rubikon::Application
51
+
52
+ action 'hello' do
53
+ puts 'Hello World!'
54
+ end
55
+
56
+ end
57
+
58
+ This way your application would do nothing when called without options, but it
59
+ would print `Hello World!` when called using `ruby myapp.rb --hello`.
60
+ Please note that Rubikon will add dashes to options by default. If you don't
61
+ like this behaviour and want options like RubyGem's `install` or `update` just
62
+ use the following inside your application class:
63
+
64
+ set :dashed_options, false
65
+
66
+ Please see the `samples` directory for more in detail sample applications.
67
+
68
+
69
+ **Warning**:
70
+
71
+ Rubikon is still in an early development stage. If you want to use it be aware
72
+ that you will probably run into problems and or restrictions. See the
73
+ Contribute section if you want to help making Rubikon better.
74
+
75
+ ## Features
76
+
77
+ * A simple to use DSL
78
+ * Automatic checks for option arguments
79
+ * User defined type safety of option arguments
80
+ * Built-in methods to capture user input and display throbbers
81
+
82
+ ## Future plans
83
+
84
+ * Automatic generation of help screens
85
+ * Improved error handling
86
+ * Built-in support for configuration files
87
+ * Built-in support for colored output and progress bars
88
+
89
+ ## Requirements
90
+
91
+ * Linux, MacOS X or Windows
92
+ * Ruby 1.8.6 or newer
93
+
94
+ ## Contribute
95
+
96
+ There are several ways of contributing to Rubikon's development:
97
+
98
+ * Build apps using it and spread the word.<br />
99
+ * Report problems and request features using the [issue tracker][2].
100
+ * Write patches yourself to fix bugs and implement new functionality.
101
+ * Create a Rubikon fork on [GitHub][1] and start hacking.
102
+
103
+ ## About the name
104
+
105
+ Rubikon is the German name of the river Rubicone in Italy. It had a historical
106
+ relevance in ancient Rome when Julius Caesar crossed that river with his army
107
+ and thereby declared war to the Roman senate. The phrase "to cross the Rubicon"
108
+ originates from this event.
109
+
110
+ You may also see Rubikon as a morphed composition of *"Ruby"* and *"console"*.
111
+
112
+ ## License
113
+
114
+ This code is free software; you can redistribute it and/or modify it under the
115
+ terms of the new BSD License. A copy of this license can be found in the LICENSE
116
+ file.
117
+
118
+ ## Credits
119
+
120
+ * Sebastian Staudt -- koraktor(at)gmail.com
121
+
122
+ ## See Also
123
+
124
+ * [API documentation](http://www.rdoc.info/projects/koraktor/rubikon)
125
+ * [GitHub project page][1]
126
+ * [GitHub issue tracker][2]
127
+
128
+ [1]: http://github.com/koraktor/rubikon
129
+ [2]: http://github.com/koraktor/rubikon/issues
data/Rakefile ADDED
@@ -0,0 +1,55 @@
1
+ # This code is free software; you can redistribute it and/or modify it under the
2
+ # terms of the new BSD License.
3
+ #
4
+ # Copyright (c) 2009, Sebastian Staudt
5
+
6
+ require 'rake/rdoctask'
7
+ require 'rake/testtask'
8
+
9
+ src_files = Dir.glob(File.join('lib', '**', '*.rb'))
10
+ test_files = Dir.glob(File.join('test', '**', '*.rb'))
11
+
12
+ task :default => :test
13
+
14
+ # Test task
15
+ Rake::TestTask.new do |t|
16
+ t.libs << 'lib'
17
+ t.test_files = test_files
18
+ t.verbose = true
19
+ end
20
+
21
+ begin
22
+ require 'jeweler'
23
+ # Gem specification
24
+ Jeweler::Tasks.new do |gem|
25
+ gem.authors = ['Sebastian Staudt']
26
+ gem.email = 'koraktor@gmail.com'
27
+ gem.description = 'A simple to use, yet powerful Ruby framework for building console-based applications.'
28
+ gem.date = Time.now
29
+ gem.homepage = 'http://koraktor.github.com/rubikon'
30
+ gem.name = gem.rubyforge_project = 'rubikon'
31
+ gem.summary = 'Rubikon - A Ruby console app framework'
32
+
33
+ gem.files = %w(README.md Rakefile LICENSE VERSION.yml) + src_files + test_files
34
+ gem.rdoc_options = ['--all', '--inline-source', '--line-numbers', '--charset=utf-8', '--webcvs=http://github.com/koraktor/rubikon/blob/master/%s']
35
+ end
36
+ rescue LoadError
37
+ puts "You need Jeweler to build the gem. Install it using `gem install jeweler`."
38
+ end
39
+
40
+ # Create a rake task +:rdoc+ to build the documentation
41
+ desc 'Building docs'
42
+ Rake::RDocTask.new do |rdoc|
43
+ rdoc.title = 'Rubikon - API documentation'
44
+ rdoc.rdoc_files.include ['lib/**/*.rb', 'LICENSE', 'README.md']
45
+ rdoc.main = 'README.md'
46
+ rdoc.rdoc_dir = 'doc'
47
+ rdoc.options = ['--all', '--inline-source', '--line-numbers', '--charset=utf-8', '--webcvs=http://github.com/koraktor/rubikon/blob/master/%s']
48
+ end
49
+
50
+ # Task for cleaning documentation and package directories
51
+ desc 'Clean documentation and package directories'
52
+ task :clean do
53
+ FileUtils.rm_rf 'doc'
54
+ FileUtils.rm_rf 'pkg'
55
+ end
data/VERSION.yml ADDED
@@ -0,0 +1,4 @@
1
+ ---
2
+ :major: 0
3
+ :minor: 1
4
+ :patch: 0
@@ -0,0 +1,71 @@
1
+ # This code is free software; you can redistribute it and/or modify it under the
2
+ # terms of the new BSD License.
3
+ #
4
+ # Copyright (c) 2009, Sebastian Staudt
5
+
6
+ module Rubikon
7
+
8
+ # Instances of the Action class are used to define the real code that should
9
+ # be executed when running the application.
10
+ class Action
11
+
12
+ @@action_count = 0
13
+
14
+ attr_reader :block, :description, :name, :param_type
15
+
16
+ # Create a new Action using the given name, options and code block.
17
+ #
18
+ # +name+:: The name of this Action, used in Application options
19
+ # +options+:: A Hash of options that define more details about the Action
20
+ # +block+:: The code block which should be executed by this Action
21
+ #
22
+ # Options:
23
+ # +description+:: A description of the action. This isn't used at the
24
+ # moment.
25
+ # +param_type+:: A single Class or a Array of classes that represent the
26
+ # type(s) of argument(s) this action expects
27
+ def initialize(name, options = {}, &block)
28
+ raise BlockMissingError unless block_given?
29
+
30
+ @name = name
31
+
32
+ @description = options[:description] || ''
33
+ @param_type = options[:param_type] || Object
34
+
35
+ @block = block
36
+ end
37
+
38
+ # Run this action's code block
39
+ #
40
+ # +args+:: The argument which should be relayed to the block of this Action
41
+ def run(*args)
42
+ if (@block.arity >= 0 and args.size < @block.arity) or (@block.arity < 0 and args.size < -@block.arity - 1)
43
+ raise MissingArgumentError
44
+ end
45
+ raise Rubikon::ArgumentTypeError unless check_argument_types(args)
46
+ @block[*args]
47
+ end
48
+
49
+ private
50
+
51
+ # Checks the types of the given arguments using the Class or Array of
52
+ # classes given in the +:param_type+ option of this action.
53
+ #
54
+ # +args+:: The arguments which should be checked
55
+ def check_argument_types(args)
56
+ if @param_type.is_a? Array
57
+ args.each_index do |i|
58
+ return false unless args[i].is_a? @param_type[i]
59
+ end
60
+ else
61
+ args.each do |arg|
62
+ return false unless arg.is_a? @param_type
63
+ end
64
+ end
65
+
66
+ true
67
+ end
68
+
69
+ end
70
+
71
+ end
@@ -0,0 +1,331 @@
1
+ # This code is free software; you can redistribute it and/or modify it under the
2
+ # terms of the new BSD License.
3
+ #
4
+ # Copyright (c) 2009, Sebastian Staudt
5
+
6
+ require 'singleton'
7
+ require 'yaml'
8
+
9
+ require 'rubikon/action'
10
+ require 'rubikon/exceptions'
11
+
12
+ module Rubikon
13
+
14
+ version = YAML.load_file(File.join(File.dirname(__FILE__), '..', '..', 'VERSION.yml'))
15
+ VERSION = "#{version[:major]}.#{version[:minor]}.#{version[:patch]}"
16
+
17
+ # The main class of Rubikon. Let your own application class inherit from this
18
+ # one.
19
+ class Application
20
+
21
+ include Singleton
22
+
23
+ attr_reader :settings
24
+
25
+ # Initialize with default settings (see set for more detail)
26
+ #
27
+ # If you really need to override this in your application class, be sure to
28
+ # call +super+
29
+ def initialize
30
+ @actions = {}
31
+ @aliases = {}
32
+ @default = nil
33
+ @settings = {
34
+ :autorun => true,
35
+ :dashed_options => true,
36
+ :help_banner => "Usage: #{$0}",
37
+ :istream => $stdin,
38
+ :name => self.class.to_s,
39
+ :ostream => $stdout,
40
+ :raise_errors => false
41
+ }
42
+ end
43
+
44
+ # Define an Application Action
45
+ #
46
+ # +name+:: The name of the action. Used as an option parameter.
47
+ # +options+:: A Hash of options to be used on the created Action
48
+ # (default: <tt>{}</tt>)
49
+ # +block+:: A block containing the code that should be executed when this
50
+ # Action is called, i.e. when the Application is called with
51
+ # the associated option parameter
52
+ def action(name, options = {}, &block)
53
+ raise "No block given" unless block_given?
54
+
55
+ key = name
56
+ key = "--#{key}" if @settings[:dashed_options]
57
+
58
+ @actions[key.to_sym] = Action.new(name, options, &block)
59
+ end
60
+
61
+ # Define an alias to an Action
62
+ #
63
+ # +name+:: The name of the alias
64
+ # +action+:: The name of the Action that should be aliased
65
+ #
66
+ # Example:
67
+ #
68
+ # action_alias :doit, :dosomething
69
+ def action_alias(name, action)
70
+ @aliases[name.to_sym] = action.to_sym
71
+ end
72
+
73
+ # Define the default Action of the Application
74
+ #
75
+ # +options+:: A Hash of options to be used on the created Action
76
+ # (default: <tt>{}</tt>)
77
+ # +block+:: A block containing the code that should be executed when this
78
+ # Action is called, i.e. when no option is given to the
79
+ # Application
80
+ def default(options = {}, &block)
81
+ @default = Action.new(:default, options, &block)
82
+ end
83
+
84
+ # Prompts the user for input
85
+ #
86
+ # If +prompt+ is not empty this will display a prompt using
87
+ # <tt>prompt.to_s</tt>.
88
+ #
89
+ # +prompt+:: A String or other Object responding to +to_s+ used for
90
+ # displaying a prompt to the user (default: <tt>''</tt>)
91
+ #
92
+ # Example:
93
+ #
94
+ # action 'interactive' do
95
+ # # Display a prompt "Please type something: "
96
+ # user_provided_value = input 'Please type something'
97
+ #
98
+ # # Do something with the data
99
+ # ...
100
+ # end
101
+ def input(prompt = '')
102
+ unless prompt.to_s.empty?
103
+ ostream << "#{prompt}: "
104
+ end
105
+ @settings[:istream].gets[0..-2]
106
+ end
107
+
108
+ # Convenience method for accessing the user-defined output stream
109
+ #
110
+ # Use this if you want to work directly with the output stream
111
+ #
112
+ # Example:
113
+ #
114
+ # ostream.flush
115
+ def ostream
116
+ @settings[:ostream]
117
+ end
118
+
119
+ # Output text using +IO#<<+ of the output stream
120
+ #
121
+ # +text+:: The text to write into the output stream
122
+ def put(text)
123
+ ostream << text
124
+ ostream.flush
125
+ end
126
+
127
+ # Output a character using +IO#putc+ of the output stream
128
+ #
129
+ # +char+:: The character to write into the output stream
130
+ def putc(char)
131
+ ostream.putc char
132
+ end
133
+
134
+ # Output a line of text using +IO#puts+ of the output stream
135
+ #
136
+ # +text+:: The text to write into the output stream
137
+ def puts(text)
138
+ ostream.puts text
139
+ end
140
+
141
+ # Run this application
142
+ #
143
+ # +args+:: The command line arguments that should be given to the
144
+ # application as options
145
+ #
146
+ # Calling this method explicitly is not required when you want to create a
147
+ # simple application (having one main class inheriting from
148
+ # Rubikon::Application). But it's useful for testing or if you want to have
149
+ # some sort of sub-applications.
150
+ def run(args = ARGV)
151
+ begin
152
+ assign_aliases unless @aliases.empty?
153
+ action_results = []
154
+
155
+ if !@default.nil? and args.empty?
156
+ action_results << @default.run
157
+ else
158
+ parse_options(args).each do |action, args|
159
+ action_results << @actions[action].run(*args)
160
+ end
161
+ end
162
+ rescue
163
+ if @settings[:raise_errors]
164
+ raise $!
165
+ else
166
+ puts "Error:\n #{$!.message}"
167
+ puts " #{$!.backtrace.join("\n ")}" if $DEBUG
168
+ exit 1
169
+ end
170
+ end
171
+
172
+ action_results
173
+ end
174
+
175
+ # Sets an application setting
176
+ #
177
+ # +setting+:: The name of the setting to change, will be symbolized first.
178
+ # +value+:: The value the setting should be changed to
179
+ #
180
+ # Available settings
181
+ # +autorun+:: If true, let the application run as soon as its class
182
+ # is defined
183
+ # +dashed_options+:: If true, each option is prepended with a double-dash
184
+ # (<tt>-</tt><tt>-</tt>)
185
+ # +help_banner+:: Defines a banner for the help message (<em>unused</em>)
186
+ # +istream+:: Defines an input stream to use
187
+ # +name+:: Defines the name of the application
188
+ # +ostream+:: Defines an output stream to use
189
+ # +raise_errors+:: If true, raise errors, otherwise fail gracefully
190
+ #
191
+ # Example:
192
+ #
193
+ # set :name, 'My App'
194
+ # set :autorun, false
195
+ def set(setting, value)
196
+ @settings[setting.to_sym] = value
197
+ end
198
+
199
+ # Displays a throbber while the given block is executed
200
+ #
201
+ # Example:
202
+ #
203
+ # action 'slow' do
204
+ # throbber do
205
+ # # Add some long running code here
206
+ # ...
207
+ # end
208
+ # end
209
+ def throbber(&block)
210
+ spinner = '-\|/'
211
+ current_ostream = ostream
212
+ @settings[:ostream] = StringIO.new
213
+
214
+ code_thread = Thread.new { block.call }
215
+
216
+ throbber_thread = Thread.new do
217
+ i = 0
218
+ current_ostream.putc 32
219
+ while code_thread.alive?
220
+ current_ostream.putc 8
221
+ current_ostream.putc spinner[i]
222
+ current_ostream.flush
223
+ i = (i + 1) % 4
224
+ sleep 0.25
225
+ end
226
+ current_ostream.putc 8
227
+ end
228
+
229
+ code_thread.join
230
+ throbber_thread.join
231
+
232
+ current_ostream << ostream.string
233
+ @settings[:ostream] = current_ostream
234
+ end
235
+
236
+ private
237
+
238
+ # Returns whether this application should be ran automatically
239
+ def self.autorun?
240
+ instance.settings[:autorun] || false
241
+ end
242
+
243
+ # Enables autorun functionality using <tt>Kernel#at_exit</tt>
244
+ #
245
+ # +subclass+:: The subclass inheriting from Application. This is the user's
246
+ # application.
247
+ #
248
+ # <em>This is called automatically when subclassing Application.</em>
249
+ def self.inherited(subclass)
250
+ Singleton.__init__(subclass)
251
+ at_exit { subclass.run if subclass.autorun? }
252
+ end
253
+
254
+ # This is used for convinience. Method calls on the class itself are
255
+ # relayed to the singleton instance.
256
+ #
257
+ # +method_name+:: The name of the method being called
258
+ # +args+:: Any arguments that are given to the method
259
+ # +block+:: A block that may be given to the method
260
+ #
261
+ # <em>This is called automatically when calling methods on the class.</em>
262
+ def self.method_missing(method_name, *args, &block)
263
+ instance.send(method_name, *args, &block)
264
+ end
265
+
266
+ # Relay putc to the instance method
267
+ #
268
+ # This is used to hide <tt>Kernel#putc</tt> so that the Application's
269
+ # output IO object is used for printing text
270
+ #
271
+ # +text+:: The text to write into the output stream
272
+ def self.putc(text)
273
+ instance.putc text
274
+ end
275
+
276
+ # Relay puts to the instance method
277
+ #
278
+ # This is used to hide <tt>Kernel#puts</tt> so that the Application's
279
+ # output IO object is used for printing text
280
+ #
281
+ # +text+:: The text to write into the output stream
282
+ def self.puts(text)
283
+ instance.puts text
284
+ end
285
+
286
+ # Assigns aliases to the actions that have been defined using action_alias
287
+ #
288
+ # Clears the aliases Hash afterwards
289
+ def assign_aliases
290
+ @aliases.each do |key, action|
291
+ if @settings[:dashed_options]
292
+ action = "--#{action}".to_sym
293
+ key = "--#{key}".to_sym
294
+ end
295
+
296
+ unless @actions.key? key
297
+ @actions[key] = @actions[action]
298
+ else
299
+ warn "There's already an action called \"#{key}\"."
300
+ end
301
+ end
302
+
303
+ @aliases = {}
304
+ end
305
+
306
+ # Parses the options used when starting the application
307
+ #
308
+ # +options+:: An Array of Strings that should be used as application
309
+ # options. Usually +ARGV+ is used for this.
310
+ def parse_options(options)
311
+ actions_to_call = {}
312
+ last_action = nil
313
+
314
+ options.each do |option|
315
+ option_sym = option.to_sym
316
+ if @actions.keys.include? option_sym
317
+ actions_to_call[option_sym] = []
318
+ last_action = option_sym
319
+ elsif last_action.nil? || (option.is_a?(String) && @settings[:dashed_options] && option[0..1] == '--')
320
+ raise UnknownOptionError.new(option)
321
+ else
322
+ actions_to_call[last_action] << option
323
+ end
324
+ end
325
+
326
+ actions_to_call
327
+ end
328
+
329
+ end
330
+
331
+ end
@@ -0,0 +1,28 @@
1
+ # This code is free software; you can redistribute it and/or modify it under the
2
+ # terms of the new BSD License.
3
+ #
4
+ # Copyright (c) 2009, Sebastian Staudt
5
+
6
+ module Rubikon
7
+
8
+ class ArgumentTypeError < ArgumentError
9
+ end
10
+
11
+ class BlockMissingError < ArgumentError
12
+ end
13
+
14
+ class MissingArgumentError < ArgumentError
15
+ end
16
+
17
+ class MissingOptionError < ArgumentError
18
+ end
19
+
20
+ class UnknownOptionError < ArgumentError
21
+
22
+ def initialize(arg)
23
+ super "Unknown argument: #{arg}"
24
+ end
25
+
26
+ end
27
+
28
+ end
data/lib/rubikon.rb ADDED
@@ -0,0 +1,9 @@
1
+ # This code is free software; you can redistribute it and/or modify it under the
2
+ # terms of the new BSD License.
3
+ #
4
+ # Copyright (c) 2009, Sebastian Staudt
5
+
6
+ libdir = File.dirname(__FILE__)
7
+ $:.unshift(libdir) unless $:.include?(libdir)
8
+
9
+ require 'rubikon/application'
data/test/test.rb ADDED
@@ -0,0 +1,173 @@
1
+ # This code is free software; you can redistribute it and/or modify it under the
2
+ # terms of the new BSD License.
3
+ #
4
+ # Copyright (c) 2009, Sebastian Staudt
5
+
6
+ require 'rubygems'
7
+ require 'shoulda'
8
+
9
+ begin require 'redgreen'; rescue LoadError; end
10
+
11
+ $: << File.join(File.dirname(__FILE__), '..', 'lib')
12
+ $: << File.dirname(__FILE__)
13
+ require 'rubikon'
14
+ require 'testapp'
15
+
16
+ class RubikonTests < Test::Unit::TestCase
17
+
18
+ context 'A Rubikon application\'s class' do
19
+
20
+ setup do
21
+ @app = RubikonTestApp.instance
22
+ end
23
+
24
+ should 'run it\'s instance for called methods' do
25
+ assert_equal @app.run(%w{--object_id}), RubikonTestApp.run(%w{--object_id})
26
+ end
27
+ end
28
+
29
+ context 'A Rubikon application' do
30
+
31
+ setup do
32
+ @app = RubikonTestApp
33
+ @ostream = StringIO.new
34
+ @app.set :ostream, @ostream
35
+ end
36
+
37
+ should 'be a singleton' do
38
+ assert_raise NoMethodError do
39
+ RubikonTestApp.new
40
+ end
41
+ end
42
+
43
+ should 'exit gracefully' do
44
+ unknown = '--unknown'
45
+ @app.set :raise_errors, false
46
+ begin
47
+ @app.run([unknown])
48
+ rescue Exception => e
49
+ end
50
+ assert_instance_of SystemExit, e
51
+ assert_equal 1, e.status
52
+ @ostream.rewind
53
+ assert_equal "Error:\n", @ostream.gets
54
+ assert_equal " Unknown argument: #{unknown}\n", @ostream.gets
55
+ @app.set :raise_errors, true
56
+ end
57
+
58
+ should 'run it\'s default action without options' do
59
+ result = @app.run
60
+ assert_equal 1, result.size
61
+ assert_equal 'default action', result.first
62
+ end
63
+
64
+ should 'run with a mandatory option' do
65
+ result = @app.run(%w{--required arg})
66
+ assert_equal 1, result.size
67
+ assert_equal 'required argument was arg', result.first
68
+ end
69
+
70
+ should 'not run without a mandatory argument' do
71
+ assert_raise Rubikon::MissingArgumentError do
72
+ @app.run(%w{--required})
73
+ end
74
+ end
75
+
76
+ should 'require an argument type if it has been defined' do
77
+ assert_raise Rubikon::ArgumentTypeError do
78
+ @app.run(['--output', 6])
79
+ end
80
+ assert_raise Rubikon::ArgumentTypeError do
81
+ @app.run(['--number_string', 6, 7])
82
+ end
83
+ assert_raise Rubikon::ArgumentTypeError do
84
+ @app.run(['--number_string', 'test' , 6])
85
+ end
86
+ end
87
+
88
+ should 'raise an exception when calling an action with the wrong number of
89
+ arguments' do
90
+ assert_raise Rubikon::MissingArgumentError do
91
+ @app.run(%w{--output})
92
+ end
93
+ assert_raise ArgumentError do
94
+ @app.run(%w{--output}, 'test', 3)
95
+ end
96
+ end
97
+
98
+ should 'raise an exception when using an unknown option' do
99
+ assert_raise Rubikon::UnknownOptionError do
100
+ @app.run(%w{--unknown})
101
+ end
102
+ assert_raise Rubikon::UnknownOptionError do
103
+ @app.run(%w{--noarg --unknown})
104
+ end
105
+ assert_raise Rubikon::UnknownOptionError do
106
+ @app.run(%w{--unknown --noarg})
107
+ end
108
+ end
109
+
110
+ should 'be able to handle user input' do
111
+ @istream = StringIO.new
112
+ @app.set :istream, @istream
113
+
114
+ input_string = 'test'
115
+ @istream.puts input_string
116
+ @istream.rewind
117
+ assert_equal [input_string], @app.run(%w{--input})
118
+ @ostream.rewind
119
+ assert_equal 'input: ', @ostream.gets
120
+ end
121
+
122
+ should 'write output to the user given output stream' do
123
+ input_string = 'test'
124
+ @app.run(['--output', input_string])
125
+ @ostream.rewind
126
+ assert_equal "#{input_string}\n", @ostream.gets
127
+ assert_equal "#{input_string}#{input_string[0].chr}", @ostream.gets
128
+ end
129
+
130
+ should 'provide a throbber' do
131
+ @app.run(%w{--throbber})
132
+ @ostream.rewind
133
+ assert_equal " \b-\b\\\b|\b/\b", @ostream.string
134
+ @app.run(%w{--throbber true})
135
+ @ostream.rewind
136
+ assert_equal " \b-\b\\\b|\b/\bdon't\nbreak\n", @ostream.string
137
+ end
138
+
139
+ should 'have working action aliases' do
140
+ assert_equal @app.run(%w{--alias_before}), @app.run(%w{--object_id})
141
+ assert_equal @app.run(%w{--alias_after}), @app.run(%w{--object_id})
142
+ end
143
+
144
+ end
145
+
146
+ context 'A Rubikon action' do
147
+
148
+ should 'throw an exception when no code block is given' do
149
+ assert_raise Rubikon::BlockMissingError do
150
+ Rubikon::Action.new 'name'
151
+ end
152
+ assert_raise Rubikon::BlockMissingError do
153
+ Rubikon::Action.new 'name', {}
154
+ end
155
+ end
156
+
157
+ should 'not raise an exception when created without options' do
158
+ action_name = 'someaction'
159
+ action_options = {
160
+ :description => 'this is an action',
161
+ :param_type => String
162
+ }
163
+ assert_nothing_raised do
164
+ action = Rubikon::Action.new action_name, action_options do end
165
+ assert_equal action_name, action.name
166
+ assert_equal action_options[:description], action.description
167
+ assert_equal action_options[:param_type], action.param_type
168
+ end
169
+ end
170
+
171
+ end
172
+
173
+ end
data/test/testapp.rb ADDED
@@ -0,0 +1,64 @@
1
+ # This code is free software; you can redistribute it and/or modify it under the
2
+ # terms of the new BSD License.
3
+ #
4
+ # Copyright (c) 2009, Sebastian Staudt
5
+
6
+ class RubikonTestApp < Rubikon::Application
7
+
8
+ set :autorun, false
9
+ set :name, 'Rubikon test application'
10
+ set :raise_errors, true
11
+
12
+ default do
13
+ 'default action'
14
+ end
15
+
16
+ action 'input' do
17
+ input 'input'
18
+ end
19
+
20
+ action_alias :alias_before, :object_id
21
+
22
+ action 'object_id' do
23
+ object_id
24
+ end
25
+
26
+ action_alias :alias_after, :object_id
27
+
28
+ action 'noarg' do
29
+ 'noarg action'
30
+ end
31
+
32
+ action 'realnoarg' do ||
33
+ end
34
+
35
+ action 'noarg2' do
36
+ end
37
+
38
+ action 'number_string', :param_type => [Numeric, String] do |s,n|
39
+ end
40
+
41
+ action 'output', :param_type => String do |s|
42
+ puts s
43
+ put s
44
+ putc s[0]
45
+ end
46
+
47
+ action 'required' do |what|
48
+ "required argument was #{what}"
49
+ end
50
+
51
+ action 'throbber' do |*output|
52
+ throbber do
53
+ if output[0]
54
+ sleep 0.5
55
+ puts 'don\'t'
56
+ sleep 0.5
57
+ puts 'break'
58
+ else
59
+ sleep 1
60
+ end
61
+ end
62
+ end
63
+
64
+ end
metadata ADDED
@@ -0,0 +1,70 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rubikon
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Sebastian Staudt
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-10-28 00:00:00 +01:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: A simple to use, yet powerful Ruby framework for building console-based applications.
17
+ email: koraktor@gmail.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - LICENSE
24
+ - README.md
25
+ files:
26
+ - LICENSE
27
+ - README.md
28
+ - Rakefile
29
+ - VERSION.yml
30
+ - lib/rubikon.rb
31
+ - lib/rubikon/action.rb
32
+ - lib/rubikon/application.rb
33
+ - lib/rubikon/exceptions.rb
34
+ - test/test.rb
35
+ - test/testapp.rb
36
+ has_rdoc: true
37
+ homepage: http://koraktor.github.com/rubikon
38
+ licenses: []
39
+
40
+ post_install_message:
41
+ rdoc_options:
42
+ - --all
43
+ - --inline-source
44
+ - --line-numbers
45
+ - --charset=utf-8
46
+ - --webcvs=http://github.com/koraktor/rubikon/blob/master/%s
47
+ require_paths:
48
+ - lib
49
+ required_ruby_version: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: "0"
54
+ version:
55
+ required_rubygems_version: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: "0"
60
+ version:
61
+ requirements: []
62
+
63
+ rubyforge_project: rubikon
64
+ rubygems_version: 1.3.5
65
+ signing_key:
66
+ specification_version: 3
67
+ summary: Rubikon - A Ruby console app framework
68
+ test_files:
69
+ - test/test.rb
70
+ - test/testapp.rb