rubikon 0.1.0

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/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