spinner.rb 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (7) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +2 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.md +45 -0
  5. data/Rakefile +6 -0
  6. data/lib/spinner.rb +147 -0
  7. metadata +75 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 7ae85912762746269f1904e657622d271850b2cb
4
+ data.tar.gz: 4ab07fad82243fd0ea66517bf78b0e28d1e1cf24
5
+ SHA512:
6
+ metadata.gz: fc3d11b44f065a65dfdc8f9fb8da5a3bfd8c99518345dd043d9a26a637cca7d1a2320b13f709aff69eec9347e0d44ff9ef4e2406511effa99191f371d9ffb4a1
7
+ data.tar.gz: 1d18ccfb5db6ca8f503fac6ff8582d355b6a86cb998d54be5ff06bc28960e267503734bfcd8ec68ffbb8b3a582698adb94c2afb363626d3ab533f52c323dc664
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source "https://rubygems.org"
2
+ gemspec
@@ -0,0 +1,20 @@
1
+ Copyright 2011 Mike Fulcher
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,45 @@
1
+ # Spinner
2
+
3
+ ```ruby
4
+ # Create a new spinner instance
5
+ spinner = Spinner.new
6
+
7
+ # Add a task block
8
+ spinner.task("Number 1") do
9
+ sleep(5) # simulate taking a while to do something awesome
10
+ end
11
+
12
+ # Add another task
13
+ Spinner.task("Number 2") do
14
+ sleep(2)
15
+ end
16
+
17
+ # Run the tasks
18
+ spinner.spin!
19
+ ```
20
+
21
+ ## Spinner can call rake tasks, too
22
+
23
+ ```ruby
24
+ # Example: completely reconstruct a Rails database using migrations
25
+
26
+ spinner = Spinner.new
27
+ spinner.task("Dropping", 'db:drop')
28
+ spinner.task("Creating", 'db:create')
29
+ spinner.task("Migrating", 'db:migrate')
30
+ spinner.task("Loading", 'db:test:load')
31
+ spinner.task("Seeding", 'db:seed')
32
+ spinner.spin!
33
+ ```
34
+
35
+ ## Initialize with tasks
36
+
37
+ ```ruby
38
+ # Tasks are just arrays with two items: the name of the task (defaults to "Executing") and the task (either a string representing a rake task, or a block that responds to "call")
39
+
40
+ tasks = []
41
+ tasks << [ "Drop database", "db:drop" ]
42
+ tasks << [ "Sleep 2 seconds", lambda { sleep(2) } ]
43
+ spinner = Spinner.new(*tasks)
44
+ spinner.spin!
45
+ ```
@@ -0,0 +1,6 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.pattern = "test/*_test.rb"
6
+ end
@@ -0,0 +1,147 @@
1
+ require 'bundler/setup'
2
+ require 'rake'
3
+ require 'stringio'
4
+
5
+ class Spinner
6
+ attr_reader :queue
7
+ def initialize(*tasks)
8
+ @queue = tasks
9
+ @width = 0
10
+ @chars = %w{ | / - \\ }
11
+ end
12
+
13
+ # Injects a new task into the queue.
14
+ def task(title=nil, task_name=nil, &block)
15
+ # Handle no task being supplied.
16
+ return @queue.map(&:title) unless block_given? || task_name
17
+
18
+ # Set default title.
19
+ title = 'Executing' unless title
20
+
21
+ # Tasks can be given a block to evaluate or
22
+ # a rake task name to invoke.
23
+ if block_given?
24
+ task_block = block
25
+ elsif task_name
26
+ task_block = Rake::Task[task_name]
27
+ end
28
+
29
+ # Inject the task into the queue.
30
+ @queue << [ title, task_block ]
31
+
32
+ # Amend the output width if necessary.
33
+ if @width < title.length
34
+ @width = title.length
35
+ end
36
+
37
+ # Return the new list of tasks.
38
+ @queue.map(&:first)
39
+ end
40
+
41
+ # Starts executing the queued tasks.
42
+ def spin!
43
+ # Handle no tasks in the queue.
44
+ return unless @queue.any?
45
+
46
+ # Mark the current time in order to calculate the total duration.
47
+ start_time = Time.now
48
+
49
+ # Pluralize the number of tasks in the queue.
50
+ task_counter = "#{@queue.size} task" << (@queue.size == 1 ? '' : 's')
51
+
52
+ # Update the print width.
53
+ @width = (@width + (@queue.size.to_s.length + 2) * 2) + 1
54
+
55
+ # Execute each task in sequence.
56
+ @queue.each_with_index do |task, i|
57
+ run_task(task, i+1)
58
+ end
59
+
60
+ # Reset this spinner instance so that it can be reused.
61
+ reset!
62
+
63
+ # Mark the completion time and calculate the duration.
64
+ end_time = Time.now
65
+ time_taken = distance_of_time_in_words(start_time, end_time)
66
+
67
+ # Print the completion message.
68
+ print("Done! #{task_counter} completed in #{time_taken} :-)\n")
69
+ end
70
+
71
+ private
72
+
73
+ # Outputs to the console.
74
+ def print(*args)
75
+ STDOUT.print(*args)
76
+ end
77
+
78
+ # Clears the current printed output.
79
+ def clear
80
+ print("\r")
81
+ print(" ".ljust(@width + 5))
82
+ print("\r")
83
+ end
84
+
85
+ # Reset this spinner instance to defaults.
86
+ def reset!
87
+ @width = 0
88
+ @queue = []
89
+ end
90
+
91
+ # Executes a single task.
92
+ def run_task(item, counter)
93
+ # Extract the title & task block.
94
+ title, task = *item
95
+
96
+ # Print the task counter and title.
97
+ print("#{counter}/#{queue.size}: #{title}".ljust(@width, '.') + '... ')
98
+
99
+ # Begin a new thread to update the printed output while
100
+ # the task runs.
101
+ t = Thread.new {
102
+ # Suppress $stdout during the task's execution.
103
+ $stdout = StringIO.new
104
+ if task.respond_to?(:invoke)
105
+ task.invoke
106
+ else
107
+ task.call
108
+ end
109
+ }
110
+
111
+ # Run the spinner for the duration of the task,
112
+ # then clear the output.
113
+ spin while t.alive?
114
+ t.join
115
+ clear
116
+ end
117
+
118
+ # Update the position of the spinner.
119
+ def spin
120
+ print(@chars[0]) # Print the next character...
121
+ sleep(0.1) # ...wait 100ms...
122
+ print("\b") # ...move the cursor back by one...
123
+ @chars.push(@chars.shift) # ...rotate the characters array.
124
+ end
125
+
126
+ # Simplified extraction from ActionView.
127
+ def distance_of_time_in_words(from_time, to_time)
128
+ distance_in_minutes = ((to_time - from_time) / 60.0).round
129
+ distance_in_seconds = (to_time - from_time).round
130
+
131
+ case distance_in_minutes
132
+ when 0..1
133
+ case distance_in_seconds
134
+ when 0..4 then "less than 5 seconds"
135
+ when 5..9 then "less than 10 seconds"
136
+ when 10..19 then "less than 20 seconds"
137
+ when 20..39 then "about half a minute"
138
+ when 40..59 then "less than one minute"
139
+ else "one minute"
140
+ end
141
+
142
+ when 2...45 then "about #{distance_in_minutes} minutes"
143
+ when 45...90 then "about an hour"
144
+ else "over an hour"
145
+ end
146
+ end
147
+ end
metadata ADDED
@@ -0,0 +1,75 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: spinner.rb
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Mike Fulcher
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-07-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: minitest
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description: Ruby progress spinner for tasks with an unknown duration.
42
+ email:
43
+ executables: []
44
+ extensions: []
45
+ extra_rdoc_files: []
46
+ files:
47
+ - lib/spinner.rb
48
+ - MIT-LICENSE
49
+ - Rakefile
50
+ - Gemfile
51
+ - README.md
52
+ homepage:
53
+ licenses: []
54
+ metadata: {}
55
+ post_install_message:
56
+ rdoc_options: []
57
+ require_paths:
58
+ - lib
59
+ required_ruby_version: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - '>='
62
+ - !ruby/object:Gem::Version
63
+ version: '0'
64
+ required_rubygems_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ requirements: []
70
+ rubyforge_project:
71
+ rubygems_version: 2.0.3
72
+ signing_key:
73
+ specification_version: 4
74
+ summary: Ruby progress spinner for tasks with an unknown duration
75
+ test_files: []