magni 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,76 @@
1
+ = Magni
2
+
3
+ Magni is a command line flag parser, nothing more, nothing less. It's
4
+ useful for when you want to collect a bunch of flags from your script,
5
+ then do something with them all.
6
+ ==
7
+ === ARGUMENT TYPES
8
+
9
+ Magni accepts four types of arguments, <tt>boolean</tt>, <tt>string</tt>,
10
+ <tt>array</tt> and <tt>integer</tt>.
11
+
12
+ boolean: --foo #=> true
13
+ integer: --foo=4 #=> 4
14
+ string: --foo=theory #=> "theory"
15
+ array: --foo=1,2,3 #=> [1, 2, 3]
16
+
17
+ === MAPPINGS
18
+
19
+ Magni is the son of Thor[http://github.com/wycats/thor] and so he inherited
20
+ some of his father's traits; mapping being one of them.
21
+
22
+ class Runner < Magni
23
+ map "--maxerr" => :integer
24
+ map "--name" => :string
25
+ map "--help" => :boolean
26
+ end
27
+
28
+ There are a couple special keywords that work with mappings too, <tt>:first</tt>
29
+ and <tt>:last</tt>. Each one will place the first or last, respectively,
30
+ argument in an instance variable for you. This is useful when the last
31
+ argument of your script is a file or command for example.
32
+
33
+ map :last => :file
34
+
35
+ > script --hello world.rb
36
+ @file #=> "world.rb"
37
+
38
+ === BIN SCRIPT
39
+
40
+ Tell Magni where to go after it does it's flag parsing magic.
41
+ <tt>delegate_to</tt> takes two arguments. The first is your Magni subclass
42
+ and the second is the method you want to be called when Magni is done:
43
+
44
+ Magni.delegate_to(Runner, :method)
45
+
46
+ === USING THE FLAGS
47
+
48
+ Flags and values are saved in <tt>@options</tt>. From the example above:
49
+
50
+ > script --maxerr=4 --name=Matte
51
+ @options #=> { :maxerr => 4, :name => "Matte" }
52
+
53
+ == License
54
+
55
+ (The MIT License)
56
+
57
+ Copyright (c) 2010 FIXME full name
58
+
59
+ Permission is hereby granted, free of charge, to any person obtaining
60
+ a copy of this software and associated documentation files (the
61
+ 'Software'), to deal in the Software without restriction, including
62
+ without limitation the rights to use, copy, modify, merge, publish,
63
+ distribute, sublicense, and/or sell copies of the Software, and to
64
+ permit persons to whom the Software is furnished to do so, subject to
65
+ the following conditions:
66
+
67
+ The above copyright notice and this permission notice shall be
68
+ included in all copies or substantial portions of the Software.
69
+
70
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
71
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
72
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
73
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
74
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
75
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
76
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,141 @@
1
+ class MagniAttributeError < Exception; end
2
+
3
+ class Magni
4
+ class << self
5
+ attr_accessor :mappings
6
+ attr_accessor :keywords
7
+
8
+ # Maps a flag to a argument type. Flags are then put into the
9
+ # <tt>options</tt> instance variable. You can map two types:
10
+ #
11
+ # Literal Double Dash Flags:
12
+ #
13
+ # map "--maxerrors" => :integer
14
+ #
15
+ # These can be one of four types: <tt>:integer</tt>, <tt>:string</tt>,
16
+ # <tt>:array</tt> and <tt>:boolean</tt>.
17
+ #
18
+ # :integer #=> --count=2
19
+ # :string #=> --word=bird
20
+ # :array #=> --ravens=huginn,munnin
21
+ # :boolean #=> --all
22
+ #
23
+ #
24
+ # Special Keywords are used to pull out a non-flag argument. This is
25
+ # useful when the first or last argument needs to be the target of
26
+ # your script; a file for example.
27
+ #
28
+ # Special keywords map to an instance variable on the instance your
29
+ # class. The following example will pull out the last argument from
30
+ # the list and put it in the <tt>@file</tt> instance variable.
31
+ # Currently there are only two keyword mappings, <tt>:first</tt> and
32
+ # <tt>last</tt>:
33
+ #
34
+ # map :first => :foo
35
+ # map :last => :bar
36
+ #
37
+ #
38
+ # Parameters
39
+ # mapping: Hash[ Flag/Keyword => Type/iVar ]
40
+ #
41
+ def map(mapping={})
42
+ @mappings ||= {}
43
+ @keywords ||= {}
44
+
45
+ mapping.each do |flag, type|
46
+ if is_map_keyword?(flag)
47
+ @keywords[flag] = type
48
+ else
49
+ @mappings[flag.gsub(/[-]+/, "")] = type
50
+ end
51
+ end
52
+ end
53
+
54
+ # Tells Magni where to send the processed flags and arguments.
55
+ #
56
+ # # runner.rb
57
+ # class Runner < Magni
58
+ # map "--help" => :boolean
59
+ #
60
+ # def start
61
+ # ...
62
+ # end
63
+ # end
64
+ #
65
+ # # bin_script.rb
66
+ # Magni.delegate_to(Runner, :start)
67
+ #
68
+ # Parameters
69
+ #
70
+ # klass: Your "runner" class
71
+ # method: The method you want executed after flags are processed
72
+ #
73
+ def delegate_to(klass, method)
74
+ klass.new(ARGV.dup).send(method)
75
+ end
76
+
77
+ private
78
+
79
+ def is_map_keyword?(flag)
80
+ [:first, :last].include?(flag)
81
+ end
82
+ end
83
+
84
+ attr_accessor :options
85
+
86
+ def initialize(options=[])
87
+ self.class.mappings ||= {}
88
+ @options ||= {}
89
+
90
+ options = extract_keywords(options)
91
+ options.each do |opt|
92
+ flag, *values = opt.gsub(/[-]+/, "").split(/[=,]/)
93
+ process(flag, values)
94
+ end
95
+ end
96
+
97
+ # Pulls the keyworded flags out of the ARGV array and puts them
98
+ # in the mapped instance variables.
99
+ #
100
+ def extract_keywords(opts=[])
101
+ self.class.keywords.each do |keyword, ivar|
102
+ Magni.class_eval { attr_accessor ivar }
103
+ instance_variable_set("@#{ ivar }", opts.delete(opts.send(keyword)))
104
+ end
105
+ opts
106
+ end
107
+
108
+ # Pulls the type of argument +flag+ is set to in +mappings+
109
+ # then sets the +options+ key to value "coerced" to the
110
+ # matching argument type.
111
+ #
112
+ def process(flag, values)
113
+ type = self.class.mappings[flag]
114
+ unless type.nil?
115
+ @options[flag.to_sym] = send("to_#{ type }", values)
116
+ end
117
+ end
118
+
119
+ private
120
+
121
+ def to_boolean(values=[])
122
+ raise MagniAttributeError, "Expected boolean." unless values.empty?
123
+ true
124
+ end
125
+
126
+ def to_integer(values=[])
127
+ raise MagniAttributeError, "Expected integer." unless values[0] =~ /\d/
128
+ values.pop.to_i
129
+ end
130
+
131
+ def to_array(values=[])
132
+ raise MagniAttributeError, "Expected array." if values.empty?
133
+ values.map { |value| (value.to_i if value =~ /\d/) || value }
134
+ end
135
+
136
+ def to_string(values=[])
137
+ raise MagniAttributeError, "Expected string." if values.size > 1
138
+ values.pop.to_s
139
+ end
140
+
141
+ end
@@ -0,0 +1,107 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe Magni do
4
+
5
+ before :each do
6
+ @test = Test.new
7
+ end
8
+
9
+ describe "mappings" do
10
+ it "should map to :boolean" do
11
+ Test.mappings['boolean'].should == :boolean
12
+ end
13
+
14
+ it "should map to :integer" do
15
+ Test.mappings['integer'].should == :integer
16
+ end
17
+
18
+ it "should map to :string" do
19
+ Test.mappings['string'].should == :string
20
+ end
21
+
22
+ it "should map to :array" do
23
+ Test.mappings['array'].should == :array
24
+ end
25
+ end
26
+
27
+ describe "booleans" do
28
+ it "should be processes into a boolean" do
29
+ @test.process("boolean", [])
30
+ @test.options[:boolean].should == true
31
+ end
32
+
33
+ it "should raise an error when passed a value" do
34
+ lambda { Test.new(["--boolean=wrong"]) }.should raise_error
35
+ end
36
+ end
37
+
38
+ describe "integers" do
39
+ it "should be processes into a fixnum" do
40
+ @test.process("integer", ["2"])
41
+ @test.options[:integer].should == 2
42
+ end
43
+
44
+ it "should raise an error when passed a string" do
45
+ lambda { Test.new(["--integer=abc"]) }.should raise_error
46
+ end
47
+ end
48
+
49
+ describe "strings" do
50
+ it "should be processes into a string" do
51
+ @test.process("string", ["hello"])
52
+ @test.options[:string].should == "hello"
53
+ end
54
+
55
+ it "should raise an error when passed an array" do
56
+ lambda { Test.new(["--string=a,b,c"]) }.should raise_error
57
+ end
58
+ end
59
+
60
+ describe "arrays" do
61
+ it "should be processes into an array" do
62
+ @test.process("array", ["a", "b", "c"])
63
+ @test.options[:array].should == ["a", "b", "c"]
64
+ end
65
+
66
+ it "should raise an error when no values are received" do
67
+ lambda { Test.new(["--array"]) }.should raise_error
68
+ end
69
+ end
70
+
71
+ describe "keywords" do
72
+ it "should map the last argument to an instance variable" do
73
+ LastKeywordTest.keywords[:last].should == :file
74
+ end
75
+
76
+ it "should populate the instance variable with the :last flag value" do
77
+ test = LastKeywordTest.new(["example.js"])
78
+ test.file.should == "example.js"
79
+ end
80
+ end
81
+
82
+ it "should accept multiple arguments" do
83
+ test = Test.new(["--integer=9", "--string=wing", "--array=1,2,3,4"])
84
+ test.options[:integer].should == 9
85
+ test.options[:string].should == "wing"
86
+ test.options[:array].should == [1, 2, 3, 4]
87
+ end
88
+
89
+ it "should silently ignore non-flag command line arguments" do
90
+ lambda { Test.new(["omgdontfail", "whatthe"]) }.should_not raise_error
91
+ end
92
+
93
+ it "should not set non-flag arguments in options" do
94
+ test = Test.new(["omgdontfail"])
95
+ test.options.should be_empty
96
+ end
97
+
98
+ it "should delegate to the class given" do
99
+ class Controller; def invoke; end; end
100
+ controller = Controller.new
101
+ Controller.stub!(:new).and_return(controller)
102
+
103
+ controller.should_receive(:invoke)
104
+ Magni.delegate_to(Controller, :invoke)
105
+ end
106
+
107
+ end
@@ -0,0 +1,12 @@
1
+ require File.dirname(__FILE__) + '/../lib/magni'
2
+
3
+ class Test < Magni
4
+ map "--boolean" => :boolean
5
+ map "--array" => :array
6
+ map "--integer" => :integer
7
+ map "--string" => :string
8
+ end
9
+
10
+ class LastKeywordTest < Magni
11
+ map :last => :file
12
+ end
metadata ADDED
@@ -0,0 +1,64 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: magni
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 1
9
+ version: 0.1.1
10
+ platform: ruby
11
+ authors:
12
+ - Matte Noble
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-03-23 00:00:00 -04:00
18
+ default_executable:
19
+ dependencies: []
20
+
21
+ description: Simple command line flag parser.
22
+ email: me@mattenoble.com
23
+ executables: []
24
+
25
+ extensions: []
26
+
27
+ extra_rdoc_files:
28
+ - README.rdoc
29
+ files:
30
+ - README.rdoc
31
+ - lib/magni.rb
32
+ has_rdoc: true
33
+ homepage: http://github.com/mnoble/magni
34
+ licenses: []
35
+
36
+ post_install_message:
37
+ rdoc_options:
38
+ - --charset=UTF-8
39
+ require_paths:
40
+ - lib
41
+ required_ruby_version: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ segments:
46
+ - 0
47
+ version: "0"
48
+ required_rubygems_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ segments:
53
+ - 0
54
+ version: "0"
55
+ requirements: []
56
+
57
+ rubyforge_project:
58
+ rubygems_version: 1.3.6
59
+ signing_key:
60
+ specification_version: 3
61
+ summary: Simple command line flag parser.
62
+ test_files:
63
+ - spec/spec_helper.rb
64
+ - spec/magni_spec.rb