opts 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,3 @@
1
+ pkg/*
2
+ *.gem
3
+ .bundle
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source :gemcutter
2
+
3
+ # Specify your gem's dependencies in opts.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2010 Simon Menke
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
@@ -0,0 +1,27 @@
1
+ $:.unshift File.expand_path('../../lib', __FILE__)
2
+ require 'opts'
3
+
4
+ class CLI
5
+ include Opts::DSL
6
+
7
+ class_use Opts::Shell
8
+ class_opt 'verbose', :short => 'v', :type => :boolean
9
+ class_opt 'path', :type => :string, :short => 'p'
10
+
11
+ arg 'GEMFILE', :type => :string
12
+ arg 'COUNT', :type => :numeric, :required => false, :splat => true
13
+ arg 'PATHS', :type => :string, :splat => true
14
+ def pack(env, args)
15
+ env[:shell].say [env, args].inspect, :green
16
+ end
17
+
18
+ def deploy(env, args)
19
+ p [env, args]
20
+ end
21
+
22
+ end
23
+
24
+ packer = CLI.new
25
+ packer.call({}, ['-v', 'pack', 'Gemfile', '1', '2', 'hello'])
26
+ packer.call({}, ['--verbose', 'deploy', 'Gemfile'])
27
+ packer.call({}, ['--verbose=f', '-p', 'example.g', 'pack', 'Gemfile', 'lib', 'app'])
@@ -0,0 +1,49 @@
1
+ $:.unshift File.expand_path('../../lib', __FILE__)
2
+ require 'opts'
3
+
4
+ class CLI
5
+ include Opts
6
+
7
+ def initialize
8
+ cli = self
9
+ @opts = Opts::Builder.new do
10
+ use Opts::Shell
11
+ use Opts::OptionParser do
12
+ opt 'verbose',
13
+ :short => 'v', :type => :boolean
14
+ opt 'path',
15
+ :short => 'p', :type => :string
16
+ end
17
+ use Opts::CommandParser do
18
+ cmd 'pack', Opts::Builder do
19
+ use Opts::ArgumentParser do
20
+ arg 'GEMFILE', :type => :string
21
+ arg 'COUNT', :type => :numeric, :required => false, :splat => true
22
+ arg 'PATHS', :type => :string, :splat => true
23
+ end
24
+ run cli.method(:pack)
25
+ end
26
+ cmd 'deploy', cli.method(:deploy)
27
+ end
28
+ run lambda { raise Error }
29
+ end
30
+ end
31
+
32
+ def call(env, args)
33
+ @opts.call(env, args)
34
+ end
35
+
36
+ def pack(env, args)
37
+ env[:shell].say [env, args].inspect, :green
38
+ end
39
+
40
+ def deploy(env, args)
41
+ p [env, args]
42
+ end
43
+
44
+ end
45
+
46
+ packer = CLI.new
47
+ packer.call({}, ['-v', 'pack', 'Gemfile', '1', '2', 'hello'])
48
+ packer.call({}, ['--verbose', 'deploy', 'Gemfile'])
49
+ packer.call({}, ['--verbose=f', '-p', 'example.g', 'pack', 'Gemfile', 'lib', 'app'])
@@ -0,0 +1,17 @@
1
+ module Opts
2
+
3
+ Error = Class.new(RuntimeError)
4
+ UnknownCommandError = Class.new(Error)
5
+ UnknownOptionError = Class.new(Error)
6
+ InvalidOptionError = Class.new(Error)
7
+ InvalidArgumentError = Class.new(Error)
8
+
9
+ require 'opts/version'
10
+ require 'opts/option_parser'
11
+ require 'opts/argument_parser'
12
+ require 'opts/command_parser'
13
+ require 'opts/builder'
14
+ require 'opts/shell'
15
+ require 'opts/dsl'
16
+
17
+ end
@@ -0,0 +1,89 @@
1
+ class Opts::ArgumentParser
2
+
3
+ attr_accessor :app
4
+
5
+ def initialize(app=nil, &block)
6
+ @app = app
7
+ @args = []
8
+
9
+ if block and block.arity == -1
10
+ instance_eval(&block)
11
+ elsif block_given?
12
+ yield(self)
13
+ end
14
+ end
15
+
16
+ def arg(name, options={})
17
+ options = { :type => :string, :required => true, :min => 1, :max => 1 }.merge(options)
18
+ options[:max] = nil if options.delete(:splat)
19
+ @args << [name, options]
20
+ end
21
+
22
+ def call(env, args)
23
+ new_env, new_args = env.dup, args.dup
24
+ parse(new_env, new_args)
25
+ @app.call(new_env, new_args)
26
+ end
27
+
28
+ private
29
+
30
+ def parse(env, args)
31
+ descs = @args.dup
32
+ while pair = descs.shift
33
+ name, desc = *pair
34
+ value = []
35
+
36
+ while arg = args.first
37
+ valid = false
38
+
39
+ case desc[:type]
40
+ when :string
41
+ valid = true
42
+
43
+ when :numeric
44
+ case arg
45
+ when /^[+-]?[0-9]+[.][0-9]+$/
46
+ arg = arg.to_f
47
+ valid = true
48
+ when /^[+-]?[0-9]+$/
49
+ arg = arg.to_i
50
+ valid = true
51
+ else
52
+ valid = false
53
+ end
54
+
55
+ end
56
+
57
+ if valid
58
+ value << arg
59
+ args.shift
60
+ else
61
+ break
62
+ end
63
+
64
+ if desc[:max] and desc[:max] == value.size
65
+ break
66
+ end
67
+ end
68
+
69
+ if value.empty? and desc[:required]
70
+ raise InvalidArgumentError, "#{name} is a required argument"
71
+ end
72
+
73
+ if desc[:min] > value.size and (desc[:required] || value.size > 0)
74
+ raise InvalidArgumentError, "minimum #{desc[:min]} values are required for #{name}"
75
+ end
76
+
77
+ if desc[:max] and desc[:max] < value.size and (desc[:required] || value.size > 0)
78
+ raise InvalidArgumentError, "maximum #{desc[:max]} values are allowed for #{name}"
79
+ end
80
+
81
+ if desc[:max] and desc[:max] == 1 and desc[:max] == 1
82
+ value = value.shift
83
+ end
84
+
85
+ env[name] = value if value
86
+ end
87
+ end
88
+
89
+ end
@@ -0,0 +1,41 @@
1
+ class Opts::Builder
2
+
3
+ attr_accessor :app
4
+
5
+ def initialize(app=nil, &block)
6
+ @app = app
7
+ @ops = []
8
+
9
+ if block and block.arity == -1
10
+ instance_eval(&block)
11
+ elsif block_given?
12
+ yield(self)
13
+ end
14
+ end
15
+
16
+ def use(klass, *args, &block)
17
+ @ops << [klass, args, block]
18
+ end
19
+
20
+ def run(app)
21
+ @app = app
22
+ end
23
+
24
+ def to_app
25
+ @target ||= begin
26
+ @ops.reverse.inject(@app) do |app, (klass, args, block)|
27
+ if Class === klass
28
+ klass.new(app, *args, &block)
29
+ else
30
+ klass.app = app
31
+ klass
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+ def call(env, args)
38
+ to_app.call(env, args)
39
+ end
40
+
41
+ end
@@ -0,0 +1,33 @@
1
+ class Opts::CommandParser
2
+
3
+ def initialize(default_app=nil, &block)
4
+ @default_app = default_app
5
+ @commands = {}
6
+
7
+ if block and block.arity == -1
8
+ instance_eval(&block)
9
+ elsif block_given?
10
+ yield(self)
11
+ end
12
+ end
13
+
14
+ def cmd(name, app=nil, *args, &block)
15
+ if Class === app
16
+ app = app.new(*args, &block)
17
+ end
18
+ @commands[name.to_s] = {
19
+ :app => app || block
20
+ }
21
+ end
22
+
23
+ def call(env, args)
24
+ cmd_name = args.first.to_s
25
+ cmd = @commands[cmd_name]
26
+
27
+ raise Opts::UnknownCommandError, "Unknown command #{cmd_name}" unless cmd
28
+
29
+ (env[:commands] ||= []) << cmd_name
30
+ cmd[:app].call(env, args[1..-1])
31
+ end
32
+
33
+ end
@@ -0,0 +1,74 @@
1
+ module Opts::DSL
2
+
3
+ def self.included(base)
4
+ base.extend ClassMethods
5
+ end
6
+
7
+ def call(env, args)
8
+ (env[:stack] ||= []).push self
9
+ self.class.parser.call(env, args)
10
+ ensure
11
+ env[:stack].pop
12
+ end
13
+
14
+ module ClassMethods
15
+
16
+ def parser
17
+ @parser ||= begin
18
+ @class_builder ||= Opts::Builder.new
19
+ @class_opts ||= Opts::OptionParser.new
20
+ @command_parser ||= Opts::CommandParser.new
21
+
22
+ @class_builder.use @class_opts
23
+ @class_builder.run @command_parser
24
+
25
+ @class_builder.to_app
26
+ end
27
+ end
28
+
29
+ def class_use(klass, *args, &block)
30
+ @class_builder ||= Opts::Builder.new
31
+ @class_builder.use(klass, *args, &block)
32
+ end
33
+
34
+ def class_opt(name, options={})
35
+ @class_opts ||= Opts::OptionParser.new
36
+ @class_opts.opt(name, options)
37
+ end
38
+
39
+ def use(klass, *args, &block)
40
+ @command_builder ||= Opts::Builder.new
41
+ @command_builder.use(klass, *args, &block)
42
+ end
43
+
44
+ def opt(name, options={})
45
+ @command_opts ||= Opts::OptionParser.new
46
+ @command_opts.opt(name, options)
47
+ end
48
+
49
+ def arg(name, options={})
50
+ @command_args ||= Opts::ArgumentParser.new
51
+ @command_args.arg(name, options)
52
+ end
53
+
54
+ def method_added(m)
55
+ @command_parser ||= Opts::CommandParser.new
56
+ @command_builder ||= Opts::Builder.new
57
+ @command_opts ||= Opts::OptionParser.new
58
+ @command_args ||= Opts::ArgumentParser.new
59
+
60
+ @command_builder.use @command_opts
61
+ @command_builder.use @command_args
62
+ @command_builder.run lambda { |env, args|
63
+ env[:stack].last.__send__(m, env, args)
64
+ }
65
+ @command_parser.cmd(m, @command_builder.to_app)
66
+
67
+ @command_builder = nil
68
+ @command_opts = nil
69
+ @command_args = nil
70
+ end
71
+
72
+ end
73
+
74
+ end
@@ -0,0 +1,99 @@
1
+ class Opts::OptionParser
2
+
3
+ attr_accessor :app
4
+
5
+ def initialize(app=nil, &block)
6
+ @options = {}
7
+ @short_names = {}
8
+ @app = app
9
+
10
+ if block and block.arity == -1
11
+ instance_eval(&block)
12
+ elsif block_given?
13
+ yield(self)
14
+ end
15
+ end
16
+
17
+ def call(env, args)
18
+ new_env, new_args = env.dup, args.dup
19
+ parse(new_env, new_args)
20
+ @app.call(new_env, new_args)
21
+ end
22
+
23
+
24
+ def opt(name, options={})
25
+ if short = options[:short]
26
+ @short_names[short.to_s] = name.to_s
27
+ end
28
+
29
+ @options[name.to_s] = {
30
+ :type => :boolean
31
+ }.merge(options)
32
+ end
33
+
34
+ private
35
+
36
+ def parse(env, args)
37
+ while args.first =~ /^(?:--([^-][^=]*)|-([^=-]))(?:=(.+))?$/
38
+ args.shift
39
+
40
+ long_name, short_name = $1, $2
41
+ name = long_name || short_name
42
+ value = $3
43
+ long_name ||= @short_names[short_name]
44
+ option = @options[long_name]
45
+
46
+ unless option
47
+ raise Opts::UnknownOptionError, "Unknown option #{name}"
48
+ end
49
+
50
+ case option[:type]
51
+ when :boolean
52
+ if value
53
+ case value
54
+ when /^t(rue)?|y(es)?|1$/i
55
+ value = true
56
+ when /^f(alse)?|n(o)?|0$/i
57
+ value = false
58
+ else
59
+ raise Opts::InvalidOptionError, "Invalid boolean option --#{long_name}"
60
+ end
61
+ else
62
+
63
+ value = true
64
+ end
65
+
66
+ when :string
67
+ if value.nil? or value.empty?
68
+ value = args.shift
69
+ end
70
+
71
+ when :numeric
72
+ if value.nil? or value.empty?
73
+ value = args.shift
74
+ end
75
+
76
+ case value
77
+ when /^[+-]?[0-9]+[.][0-9]+$/
78
+ value = value.to_f
79
+ when /^[+-]?[0-9]+$/
80
+ value = value.to_i
81
+ else
82
+ raise Opts::InvalidOptionError, "Invalid numeric option --#{long_name}=#{value}"
83
+ end
84
+
85
+ end
86
+
87
+ if NilClass === value and option[:default]
88
+ value = option[:default]
89
+ end
90
+
91
+ if NilClass === value
92
+ raise Opts::InvalidOptionError, "Invalid option --#{name}"
93
+ end
94
+
95
+ env[long_name] = value
96
+ end
97
+ end
98
+
99
+ end
@@ -0,0 +1,102 @@
1
+ class Opts::Shell
2
+
3
+ # Most of this is taken from http://github.com/wycats/thor/blob/master/lib/thor/shell/basic.rb
4
+
5
+ CLEAR = "\e[0m"
6
+ BOLD = "\e[1m"
7
+
8
+ BLACK = "\e[30m"
9
+ RED = "\e[31m"
10
+ GREEN = "\e[32m"
11
+ YELLOW = "\e[33m"
12
+ BLUE = "\e[34m"
13
+ MAGENTA = "\e[35m"
14
+ CYAN = "\e[36m"
15
+ WHITE = "\e[37m"
16
+
17
+ ON_BLACK = "\e[40m"
18
+ ON_RED = "\e[41m"
19
+ ON_GREEN = "\e[42m"
20
+ ON_YELLOW = "\e[43m"
21
+ ON_BLUE = "\e[44m"
22
+ ON_MAGENTA = "\e[45m"
23
+ ON_CYAN = "\e[46m"
24
+ ON_WHITE = "\e[47m"
25
+
26
+ def initialize(app, options={})
27
+ @app = app
28
+ @options = { :color => true }.merge(options)
29
+ @padding = 0
30
+ @quite = false
31
+ end
32
+
33
+ def call(env, args)
34
+ env[:shell] = self
35
+ @app.call(env, args)
36
+ end
37
+
38
+ def with_padding(padding)
39
+ _padding = @padding
40
+ @padding = padding
41
+ yield
42
+ ensure
43
+ @padding = _padding
44
+ end
45
+
46
+ def padding
47
+ @padding
48
+ end
49
+
50
+ def with_quite(quite=true)
51
+ _quite = @quite
52
+ @quite = quite
53
+ yield
54
+ ensure
55
+ @quite = _quite
56
+ end
57
+
58
+ def quite?
59
+ @quite
60
+ end
61
+
62
+ def say(message="", color=nil, force_new_line=(message.to_s !~ /( |\t)$/))
63
+ message = message.to_s
64
+ message = set_color(message, color) if color
65
+
66
+ spaces = " " * padding
67
+
68
+ if force_new_line
69
+ $stdout.puts(spaces + message)
70
+ else
71
+ $stdout.print(spaces + message)
72
+ end
73
+ $stdout.flush
74
+ end
75
+
76
+ def status(type, message, log_status=true)
77
+ return if quiet? || log_status == false
78
+ spaces = " " * (padding + 1)
79
+ color = log_status.is_a?(Symbol) ? log_status : :green
80
+
81
+ status = status.to_s.rjust(12)
82
+ status = set_color status, color, true if color
83
+
84
+ $stdout.puts "#{status}#{spaces}#{message}"
85
+ $stdout.flush
86
+ end
87
+
88
+ def set_color(string, color=false, bold=false)
89
+ if color and @options[:color]
90
+ color = self.class.const_get(color.to_s.upcase) if color.is_a?(Symbol)
91
+ bold = bold ? BOLD : ""
92
+ "#{bold}#{color}#{string}#{CLEAR}"
93
+ else
94
+ string
95
+ end
96
+ end
97
+
98
+ def inspect
99
+ "#<#{self.class} #{@options[:color] ? 'COLORED' : 'BW'}>"
100
+ end
101
+
102
+ end
@@ -0,0 +1,3 @@
1
+ module Opts
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path("../lib/opts/version", __FILE__)
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "opts"
6
+ s.version = Opts::VERSION
7
+ s.platform = Gem::Platform::RUBY
8
+ s.authors = ['Simon Menke']
9
+ s.email = ['simon.menke@gmail.com']
10
+ s.homepage = "http://rubygems.org/gems/opts"
11
+ s.summary = "Rack for command line applications"
12
+ s.description = "Rack for command line applications"
13
+
14
+ s.required_rubygems_version = ">= 1.3.6"
15
+ s.rubyforge_project = "opts"
16
+
17
+ s.add_development_dependency "bundler", ">= 1.0.0"
18
+
19
+ s.files = `git ls-files`.split("\n")
20
+ s.executables = `git ls-files`.split("\n").map{|f| f =~ /^bin\/(.*)/ ? $1 : nil}.compact
21
+ s.require_path = 'lib'
22
+ end
metadata ADDED
@@ -0,0 +1,99 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: opts
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Simon Menke
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-08-30 00:00:00 +02:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: bundler
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 23
30
+ segments:
31
+ - 1
32
+ - 0
33
+ - 0
34
+ version: 1.0.0
35
+ type: :development
36
+ version_requirements: *id001
37
+ description: Rack for command line applications
38
+ email:
39
+ - simon.menke@gmail.com
40
+ executables: []
41
+
42
+ extensions: []
43
+
44
+ extra_rdoc_files: []
45
+
46
+ files:
47
+ - .gitignore
48
+ - Gemfile
49
+ - LICENSE
50
+ - Rakefile
51
+ - examples/dsl.rb
52
+ - examples/simple.rb
53
+ - lib/opts.rb
54
+ - lib/opts/argument_parser.rb
55
+ - lib/opts/builder.rb
56
+ - lib/opts/command_parser.rb
57
+ - lib/opts/dsl.rb
58
+ - lib/opts/option_parser.rb
59
+ - lib/opts/shell.rb
60
+ - lib/opts/version.rb
61
+ - opts.gemspec
62
+ has_rdoc: true
63
+ homepage: http://rubygems.org/gems/opts
64
+ licenses: []
65
+
66
+ post_install_message:
67
+ rdoc_options: []
68
+
69
+ require_paths:
70
+ - lib
71
+ required_ruby_version: !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ hash: 3
77
+ segments:
78
+ - 0
79
+ version: "0"
80
+ required_rubygems_version: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ hash: 23
86
+ segments:
87
+ - 1
88
+ - 3
89
+ - 6
90
+ version: 1.3.6
91
+ requirements: []
92
+
93
+ rubyforge_project: opts
94
+ rubygems_version: 1.3.7
95
+ signing_key:
96
+ specification_version: 3
97
+ summary: Rack for command line applications
98
+ test_files: []
99
+