upoj-rb 0.0.0 → 0.0.1

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/Gemfile CHANGED
@@ -3,6 +3,8 @@ source "http://rubygems.org"
3
3
  # Example:
4
4
  # gem "activesupport", ">= 2.3.5"
5
5
 
6
+ gem 'paint'
7
+
6
8
  # Add dependencies to develop your gem here.
7
9
  # Include everything needed to run rake, tests, features, etc.
8
10
  group :development do
@@ -8,6 +8,7 @@ GEM
8
8
  bundler (~> 1.0)
9
9
  git (>= 1.2.5)
10
10
  rake
11
+ paint (0.8.3)
11
12
  rake (0.9.2)
12
13
  rdoc (3.9.4)
13
14
  rspec (2.6.0)
@@ -27,6 +28,7 @@ DEPENDENCIES
27
28
  bundler (~> 1.0.0)
28
29
  gemcutter
29
30
  jeweler (~> 1.6.4)
31
+ paint
30
32
  rdoc
31
33
  rspec
32
34
  shoulda
data/Rakefile CHANGED
@@ -25,7 +25,6 @@ Jeweler::Tasks.new do |gem|
25
25
  end
26
26
  Jeweler::RubygemsDotOrgTasks.new
27
27
 
28
-
29
28
  require 'rspec/core/rake_task'
30
29
  desc "Run specs"
31
30
  RSpec::Core::RakeTask.new do |t|
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.0
1
+ 0.0.1
@@ -2,4 +2,4 @@ module Upoj
2
2
 
3
3
  end
4
4
 
5
- %w( ext opts ).each{ |dep| require File.join(File.dirname(__FILE__), 'upoj-rb', dep) }
5
+ %w( ext opts signals ).each{ |dep| require File.join(File.dirname(__FILE__), 'upoj-rb', dep) }
@@ -1,12 +1,98 @@
1
1
  require 'optparse'
2
+ require 'paint'
2
3
 
3
4
  module Upoj
4
5
 
5
6
  class Opts < OptionParser
6
7
  attr_accessor :funnel
7
8
 
8
- def on_funnel name, *args
9
- super(*args){ |val| (@funnel ||= Hash.new)[name] = val }
9
+ def self.section_title title
10
+ Paint[title, :bold]
11
+ end
12
+
13
+ def self.section_title_ref ref
14
+ Paint[ref, :underline]
15
+ end
16
+
17
+ def initialize *args
18
+ options = args.extract_options!
19
+
20
+ @funnel = options[:funnel] || {}
21
+ @footer = options[:footer]
22
+ @examples = options[:examples]
23
+
24
+ width = options[:width] || 32
25
+ indent = options[:indent] || (' ' * 2)
26
+ super nil, width, indent
27
+
28
+ @banner = options[:banner].kind_of?(Hash) ? summary_banner_section(options[:banner]) : options[:banner]
29
+ end
30
+
31
+ def on *args
32
+ if block_given?
33
+ super(*args)
34
+ else
35
+ sw = make_switch(args)[0]
36
+ name = sw.long.first.sub /^\-+/, ''
37
+ block = lambda{ |val| @funnel[name] = val }
38
+ super(*args, &block)
39
+ end
40
+ end
41
+
42
+ def summary_banner_section *args
43
+ options = args.extract_options!
44
+ %|#{summary_program_name} #{options[:description]}
45
+
46
+ #{self.class.section_title :USAGE}
47
+ #{@summary_indent}#{summary_program_name} #{options[:usage]}
48
+
49
+ #{self.class.section_title :OPTIONS}
50
+ |
51
+ end
52
+
53
+ def summary_examples_section
54
+ return nil unless @examples
55
+ String.new("\n#{self.class.section_title :EXAMPLES}").tap do |s|
56
+ @examples.each do |example|
57
+ s << "\n#{@summary_indent}#{summary_program_name} #{example}"
58
+ end
59
+ end
60
+ end
61
+
62
+ def program_name
63
+ @program_name || File.basename($0)
64
+ end
65
+
66
+ def summary_program_name
67
+ Paint[program_name, :bold]
68
+ end
69
+
70
+ def parse_or_exit!
71
+ begin
72
+ parse!
73
+ rescue Exception => err
74
+ unless err.kind_of?(SystemExit)
75
+ puts
76
+ Kernel.warn Paint["Error: #{err.message}", :yellow]
77
+ puts
78
+ puts self
79
+ exit 2
80
+ else
81
+ exit err.status
82
+ end
83
+ end
84
+ end
85
+
86
+ def to_s
87
+ "#{super}#{summary_examples_section}#{@footer}"
88
+ end
89
+
90
+ def help!
91
+ self.on('-h', '--help', 'show this help and exit'){ puts self; exit 0 }
92
+ end
93
+
94
+ def usage!
95
+ self.on('-u', '--usage', 'show this help and exit'){ puts self; exit 0 }
10
96
  end
11
97
  end
12
98
  end
@@ -0,0 +1,121 @@
1
+ module Upoj
2
+
3
+ # Register of blocks of code to execute when the script receives a particular signal.
4
+ module Signals
5
+
6
+ # Traps script signals. Blocks registered with Signals.on will be
7
+ # executed when the script receives the corresponding status.
8
+ #
9
+ # Blocks previously trapped without using this class will be replaced.
10
+ #
11
+ # Returns true if any signals were trapped.
12
+ def self.trap
13
+ @@trapped.each_key{ |signal| Signal.trap(signal){ self.run signal } }
14
+ @@trapped.any?
15
+ end
16
+
17
+ # Clears all blocks registered for the given signal.
18
+ # Use nil to clear all blocks for all signals.
19
+ #
20
+ # No error will be raised if the signal is unknown.
21
+ def self.clear signal = nil
22
+ signal ? @@trapped[signal_number(signal, false)].try(:clear) : @@trapped.clear; nil
23
+ end
24
+
25
+ # Registers a block to be executed when the script
26
+ # receives a given signal. This method can be called several
27
+ # times; all registered blocks will be called in the order they
28
+ # were registered when the signal is received. If the signal
29
+ # is not known, ArgumentError is raised (see #known?).
30
+ #
31
+ # Signals.trap must be called after registering the blocks
32
+ # for them to be activated.
33
+ #
34
+ # ==== Arguments
35
+ # * <tt>signal</tt> - The signal to trap.
36
+ # * <tt>&block</tt> - The block to execute when the signal is trapped.
37
+ #
38
+ # ==== Examples
39
+ # Upoj::CLI::Signals.on(:exit){ cleanup_temporary_files }
40
+ # Upoj::CLI::Signals.on("TERM"){ close_connections }
41
+ # Upoj::CLI::Signals.on(2) do
42
+ # puts usage
43
+ # end
44
+ def self.on signal, &block
45
+ (@@trapped[signal_number(signal)] ||= []) << block; block
46
+ end
47
+
48
+ # Blocks registered with this method will be executed if the
49
+ # script exits with any of the following statuses: <tt>QUIT,
50
+ # TERM, KILL, INT</tt> (2, 3, 9, 15).
51
+ #
52
+ # Signals.trap must be called after registering the blocks
53
+ # for them to be activated.
54
+ #
55
+ # ==== Arguments
56
+ # * <tt>&block</tt> - The block to execute when the script
57
+ # fails.
58
+ #
59
+ # ==== Examples
60
+ # Upoj::CLI::Signals.on_failure{ close_connections }
61
+ def self.on_failure &block
62
+ FAILURE_STATUSES.each{ |status| self.on status, &block }; block
63
+ end
64
+
65
+ # Blocks registered with this method will be executed if the
66
+ # script exits successfully with status <tt>EXIT</tt> (0).
67
+ #
68
+ # Signals.trap must be called after registering the blocks
69
+ # for them to be activated.
70
+ #
71
+ # ==== Arguments
72
+ # * <tt>&block</tt> - The block to execute when the script
73
+ # exits successfully.
74
+ #
75
+ # ==== Examples
76
+ # Upoj::CLI::Signals.on_success{ FileUtils.rm_fr tmp }
77
+ def self.on_success &block
78
+ SUCCESS_STATUSES.each{ |status| self.on status, &block }; block
79
+ end
80
+
81
+ # Indicates whether the given signal is valid, i.e. whether
82
+ # it is either a number or one of the text signals returned
83
+ # by Signal#list.
84
+ def self.known? signal
85
+ !!signal_number(signal, false)
86
+ end
87
+
88
+ private
89
+
90
+ # Returns the map of signal names and their numerical value
91
+ # returned by Signal#list.
92
+ def self.signals
93
+ @@signals ||= Signal.list
94
+ end
95
+
96
+ # Returns the numerical value of the given signal.
97
+ # Statuses in Signal#list can be given as text.
98
+ def self.signal_number signal, raise_if_unknown = true
99
+ (signal.kind_of?(Fixnum) ? signal : signals[signal.to_s.upcase]).tap do |n|
100
+ raise ArgumentError, "Unknown signal '#{signal}'. Signal must either be a number or one of the text signals returned by Signal.list." if raise_if_unknown && !n
101
+ end
102
+ end
103
+
104
+ # Runs all the blocks registered for the given signal.
105
+ def self.run signal
106
+ @@trapped[signal].try(:each){ |block| block.try :call, signal }
107
+ end
108
+
109
+ # List of exit statuses considered a termination.
110
+ # 2 = INT, 3 = QUIT, 9 = KILL, 15 = TERM
111
+ FAILURE_STATUSES = [ 2, 3, 9, 15 ]
112
+
113
+ # List of exit statuses considered successful.
114
+ # 0 = EXIT
115
+ SUCCESS_STATUSES = [ 0 ]
116
+
117
+ # Map of numerical exit statuses and the corresponding list of
118
+ # blocks to execute.
119
+ @@trapped = {}
120
+ end
121
+ end
@@ -0,0 +1,152 @@
1
+ require 'helper'
2
+
3
+ describe Upoj::Signals do
4
+ before :each do
5
+ Upoj::Signals.clear
6
+ end
7
+
8
+ describe "#trap" do
9
+ it "should return false if no blocks are registered" do
10
+ Upoj::Signals.trap.should == false
11
+ end
12
+
13
+ it "should return true if any block is registered" do
14
+ Upoj::Signals.on(0){}
15
+ Upoj::Signals.trap.should == true
16
+ end
17
+ end
18
+
19
+ describe "#on" do
20
+ it "should register all given blocks in order" do
21
+ result = String.new
22
+ Upoj::Signals.on(0){ result << 'a' }
23
+ Upoj::Signals.on(0){ result << 'b' }
24
+ Upoj::Signals.on(0){ result << 'c' }
25
+ Upoj::Signals.send :run, 0
26
+ result.should == 'abc'
27
+ end
28
+
29
+ it "should register all given blocks for known signals" do
30
+ result = String.new
31
+ Upoj::Signals.on(1){ result << 'a' }
32
+ Upoj::Signals.on(2){ result << 'b' }
33
+ Upoj::Signals.on(3){ result << 'c' }
34
+ Upoj::Signals.send :run, 1
35
+ Upoj::Signals.send :run, 2
36
+ Upoj::Signals.send :run, 3
37
+ result.should == 'abc'
38
+ end
39
+
40
+ it "should not execute blocks whose signal was not trapped" do
41
+ result = String.new
42
+ Upoj::Signals.on(1){ result << 'abc' }
43
+ Upoj::Signals.on(2){ result << 'def' }
44
+ Upoj::Signals.send :run, 1
45
+ result.should == 'abc'
46
+ end
47
+
48
+ it "should raise an ArgumentError for unknown signals" do
49
+ [ nil, false, true, Object.new, :random, 'foo', 'BAR' ].each do |sig|
50
+ lambda{ Upoj::Signals.on(sig){} }.should raise_error(ArgumentError)
51
+ end
52
+ end
53
+ end
54
+
55
+ describe "#on_success" do
56
+ it "should execute registered blocks on successful exit" do
57
+ result = String.new
58
+ Upoj::Signals.on_success{ result << 'abc' }
59
+ Upoj::Signals.send :run, 0
60
+ result.should == 'abc'
61
+ end
62
+
63
+ it "should not execute registered blocks on failure" do
64
+ result = String.new
65
+ Upoj::Signals.on_success{ result << 'abc' }
66
+ (1..255).each do |sig|
67
+ Upoj::Signals.send :run, sig
68
+ end
69
+ result.should be_empty
70
+ end
71
+ end
72
+
73
+ describe "#on_failure" do
74
+ it "should execute registered blocks on failure" do
75
+ result = String.new
76
+ Upoj::Signals.on_failure{ result << 'a' }
77
+ [ 2, 3, 9, 15 ].each do |sig|
78
+ Upoj::Signals.send :run, sig
79
+ end
80
+ result.should == 'aaaa'
81
+ end
82
+
83
+ it "should not execute registered blocks on successful exit" do
84
+ result = String.new
85
+ Upoj::Signals.on_failure{ result << 'abc' }
86
+ Upoj::Signals.send :run, 0
87
+ result.should be_empty
88
+ end
89
+ end
90
+
91
+ describe "#known?" do
92
+ it "should return true for every known signal name" do
93
+ Signal.list.keys.each do |name|
94
+ Upoj::Signals.known?(name).should == true
95
+ end
96
+ end
97
+
98
+ it "should return true for numbers between 0 and 255" do
99
+ (0..255).each do |n|
100
+ Upoj::Signals.known?(n).should == true
101
+ end
102
+ end
103
+
104
+ it "should return false for unknown signal names" do
105
+ [ nil, false, true, Object.new, :random, 'foo', 'BAR' ].each do |unknown|
106
+ Upoj::Signals.known?(unknown).should == false
107
+ end
108
+ end
109
+ end
110
+
111
+ describe "#clear" do
112
+ it "should clear blocks for a given signal" do
113
+ result = String.new
114
+ Upoj::Signals.on(0){ result << 'abc' }
115
+ Upoj::Signals.clear 0
116
+ Upoj::Signals.send :run, 0
117
+ result.should be_empty
118
+ end
119
+
120
+ it "should only clear blocks for the given signal" do
121
+ result = String.new
122
+ Upoj::Signals.on(1){ result << 'a' }
123
+ Upoj::Signals.on(2){ result << 'b' }
124
+ Upoj::Signals.on(3){ result << 'c' }
125
+ Upoj::Signals.clear 2
126
+ Upoj::Signals.send :run, 1
127
+ Upoj::Signals.send :run, 2
128
+ Upoj::Signals.send :run, 3
129
+ result.should == 'ac'
130
+ end
131
+
132
+ it "should clear all blocks when no signal is given" do
133
+ result = String.new
134
+ Upoj::Signals.on(0){ result << 'a' }
135
+ Upoj::Signals.on(1){ result << 'b' }
136
+ Upoj::Signals.on(2){ result << 'c' }
137
+ Upoj::Signals.on(3){ result << 'd' }
138
+ Upoj::Signals.clear
139
+ Upoj::Signals.send :run, 0
140
+ Upoj::Signals.send :run, 1
141
+ Upoj::Signals.send :run, 2
142
+ Upoj::Signals.send :run, 3
143
+ result.should be_empty
144
+ end
145
+
146
+ it "should not raise an error for unknown signals" do
147
+ [ nil, false, true, Object.new, :random, 'foo', 'BAR' ].each do |sig|
148
+ lambda{ Upoj::Signals.clear(sig){} }.should_not raise_error(ArgumentError)
149
+ end
150
+ end
151
+ end
152
+ end
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "upoj-rb"
8
- s.version = "0.0.0"
8
+ s.version = "0.0.1"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["AlphaHydrae"]
12
- s.date = "2011-09-30"
12
+ s.date = "2011-10-01"
13
13
  s.description = "This contains common ruby extensions mostly taken from Rails, as well as various command-line utilities."
14
14
  s.email = "hydrae.alpha@gmail.com"
15
15
  s.extra_rdoc_files = [
@@ -29,9 +29,11 @@ Gem::Specification.new do |s|
29
29
  "lib/upoj-rb.rb",
30
30
  "lib/upoj-rb/ext.rb",
31
31
  "lib/upoj-rb/opts.rb",
32
+ "lib/upoj-rb/signals.rb",
32
33
  "spec/blank_spec.rb",
33
34
  "spec/ext_spec.rb",
34
35
  "spec/helper.rb",
36
+ "spec/signals_spec.rb",
35
37
  "upoj-rb.gemspec"
36
38
  ]
37
39
  s.homepage = "http://github.com/AlphaHydrae/upoj-rb"
@@ -44,6 +46,7 @@ Gem::Specification.new do |s|
44
46
  s.specification_version = 3
45
47
 
46
48
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
49
+ s.add_runtime_dependency(%q<paint>, [">= 0"])
47
50
  s.add_development_dependency(%q<rspec>, [">= 0"])
48
51
  s.add_development_dependency(%q<shoulda>, [">= 0"])
49
52
  s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
@@ -51,6 +54,7 @@ Gem::Specification.new do |s|
51
54
  s.add_development_dependency(%q<gemcutter>, [">= 0"])
52
55
  s.add_development_dependency(%q<rdoc>, [">= 0"])
53
56
  else
57
+ s.add_dependency(%q<paint>, [">= 0"])
54
58
  s.add_dependency(%q<rspec>, [">= 0"])
55
59
  s.add_dependency(%q<shoulda>, [">= 0"])
56
60
  s.add_dependency(%q<bundler>, ["~> 1.0.0"])
@@ -59,6 +63,7 @@ Gem::Specification.new do |s|
59
63
  s.add_dependency(%q<rdoc>, [">= 0"])
60
64
  end
61
65
  else
66
+ s.add_dependency(%q<paint>, [">= 0"])
62
67
  s.add_dependency(%q<rspec>, [">= 0"])
63
68
  s.add_dependency(%q<shoulda>, [">= 0"])
64
69
  s.add_dependency(%q<bundler>, ["~> 1.0.0"])
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: upoj-rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.0
4
+ version: 0.0.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,22 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-09-30 00:00:00.000000000Z
12
+ date: 2011-10-01 00:00:00.000000000Z
13
13
  dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: paint
16
+ requirement: &2156246260 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *2156246260
14
25
  - !ruby/object:Gem::Dependency
15
26
  name: rspec
16
- requirement: &2154063700 !ruby/object:Gem::Requirement
27
+ requirement: &2156244680 !ruby/object:Gem::Requirement
17
28
  none: false
18
29
  requirements:
19
30
  - - ! '>='
@@ -21,10 +32,10 @@ dependencies:
21
32
  version: '0'
22
33
  type: :development
23
34
  prerelease: false
24
- version_requirements: *2154063700
35
+ version_requirements: *2156244680
25
36
  - !ruby/object:Gem::Dependency
26
37
  name: shoulda
27
- requirement: &2156225260 !ruby/object:Gem::Requirement
38
+ requirement: &2156243180 !ruby/object:Gem::Requirement
28
39
  none: false
29
40
  requirements:
30
41
  - - ! '>='
@@ -32,10 +43,10 @@ dependencies:
32
43
  version: '0'
33
44
  type: :development
34
45
  prerelease: false
35
- version_requirements: *2156225260
46
+ version_requirements: *2156243180
36
47
  - !ruby/object:Gem::Dependency
37
48
  name: bundler
38
- requirement: &2156838500 !ruby/object:Gem::Requirement
49
+ requirement: &2156242180 !ruby/object:Gem::Requirement
39
50
  none: false
40
51
  requirements:
41
52
  - - ~>
@@ -43,10 +54,10 @@ dependencies:
43
54
  version: 1.0.0
44
55
  type: :development
45
56
  prerelease: false
46
- version_requirements: *2156838500
57
+ version_requirements: *2156242180
47
58
  - !ruby/object:Gem::Dependency
48
59
  name: jeweler
49
- requirement: &2156866340 !ruby/object:Gem::Requirement
60
+ requirement: &2156241300 !ruby/object:Gem::Requirement
50
61
  none: false
51
62
  requirements:
52
63
  - - ~>
@@ -54,10 +65,10 @@ dependencies:
54
65
  version: 1.6.4
55
66
  type: :development
56
67
  prerelease: false
57
- version_requirements: *2156866340
68
+ version_requirements: *2156241300
58
69
  - !ruby/object:Gem::Dependency
59
70
  name: gemcutter
60
- requirement: &2156867780 !ruby/object:Gem::Requirement
71
+ requirement: &2156239520 !ruby/object:Gem::Requirement
61
72
  none: false
62
73
  requirements:
63
74
  - - ! '>='
@@ -65,10 +76,10 @@ dependencies:
65
76
  version: '0'
66
77
  type: :development
67
78
  prerelease: false
68
- version_requirements: *2156867780
79
+ version_requirements: *2156239520
69
80
  - !ruby/object:Gem::Dependency
70
81
  name: rdoc
71
- requirement: &2156869220 !ruby/object:Gem::Requirement
82
+ requirement: &2156238460 !ruby/object:Gem::Requirement
72
83
  none: false
73
84
  requirements:
74
85
  - - ! '>='
@@ -76,7 +87,7 @@ dependencies:
76
87
  version: '0'
77
88
  type: :development
78
89
  prerelease: false
79
- version_requirements: *2156869220
90
+ version_requirements: *2156238460
80
91
  description: This contains common ruby extensions mostly taken from Rails, as well
81
92
  as various command-line utilities.
82
93
  email: hydrae.alpha@gmail.com
@@ -98,9 +109,11 @@ files:
98
109
  - lib/upoj-rb.rb
99
110
  - lib/upoj-rb/ext.rb
100
111
  - lib/upoj-rb/opts.rb
112
+ - lib/upoj-rb/signals.rb
101
113
  - spec/blank_spec.rb
102
114
  - spec/ext_spec.rb
103
115
  - spec/helper.rb
116
+ - spec/signals_spec.rb
104
117
  - upoj-rb.gemspec
105
118
  homepage: http://github.com/AlphaHydrae/upoj-rb
106
119
  licenses:
@@ -117,7 +130,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
117
130
  version: '0'
118
131
  segments:
119
132
  - 0
120
- hash: 1617318451971428399
133
+ hash: 2937755540580952162
121
134
  required_rubygems_version: !ruby/object:Gem::Requirement
122
135
  none: false
123
136
  requirements: