spinner.rb 0.2.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.
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: []