beans 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/Rakefile +38 -0
- data/bin/beans +71 -0
- data/bin/beans-server +9 -0
- data/bin/beansd +7 -0
- data/lib/beans.rb +9 -0
- data/lib/beans/bean_counter.rb +26 -0
- data/lib/beans/bean_server.rb +47 -0
- data/lib/beans/config.rb +51 -0
- data/lib/beans/dispatcher.rb +13 -0
- data/lib/beans/images/bean-icon.png +0 -0
- data/lib/beans/notification.rb +17 -0
- data/lib/beans/stopwatch.rb +113 -0
- data/lib/beans/version.rb +10 -0
- metadata +101 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
Yjg4NWM1NmM0OGIzNDRiMjc1NWNlNzg0YjY3NWMwOGM3MGYxMDBmNw==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
NmY4OTIzNzJjZDIwNTU3MTM1MDU0YzcxZDRiM2U2Y2IxYjIyYzM0Yg==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
ZjI0NjUwODEwMTdlYjQ2ZDBhMmU4NTY5ZGUwZDZiMmY5OTdiYzM1MWQxOTRh
|
10
|
+
YTRjNDVmOWM2ZDA0ODhmNmE3ZmNhYWRkZWVhY2Y1NDQ2MWQwMWIxMzE1Nzdi
|
11
|
+
YTVmODFkYWQzNTQyM2RhMzgyMDlmMjQ2YzM1ZDE2ODliYzA1NDc=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
NTQzNzQ1MDg4YzZjYWRhOTBiZTM5M2RhOGJjZWFiNzY1ZWE2NTQzNTJiZDk3
|
14
|
+
OTU3N2RiMDJhNDNiYTNjZTEyN2NjYWFhZjUyYjc2OTllYTBjOWZjZTRhNDFm
|
15
|
+
YzlhNDYwNjE5ZDgzZmZmZDFmOTYyYTVlNTg5MGNiMDQ1ZDAzNTA=
|
data/Rakefile
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
begin
|
3
|
+
require 'bundler/setup'
|
4
|
+
rescue LoadError
|
5
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
6
|
+
end
|
7
|
+
begin
|
8
|
+
require 'rdoc/task'
|
9
|
+
rescue LoadError
|
10
|
+
require 'rdoc/rdoc'
|
11
|
+
require 'rake/rdoctask'
|
12
|
+
RDoc::Task = Rake::RDocTask
|
13
|
+
end
|
14
|
+
|
15
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
16
|
+
rdoc.rdoc_dir = 'rdoc'
|
17
|
+
rdoc.title = 'beans'
|
18
|
+
rdoc.options << '--line-numbers'
|
19
|
+
rdoc.rdoc_files.include('README.rdoc')
|
20
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
|
25
|
+
|
26
|
+
Bundler::GemHelper.install_tasks
|
27
|
+
|
28
|
+
require 'rake/testtask'
|
29
|
+
|
30
|
+
Rake::TestTask.new(:test) do |t|
|
31
|
+
t.libs << 'lib'
|
32
|
+
t.libs << 'test'
|
33
|
+
t.pattern = 'test/**/*_test.rb'
|
34
|
+
t.verbose = false
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
task :default => :test
|
data/bin/beans
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'beans'
|
4
|
+
|
5
|
+
if ARGV.include? '-n'
|
6
|
+
multiplier = ARGV[ARGV.index('-n')+1].to_f
|
7
|
+
end
|
8
|
+
multiplier ||= 1.0
|
9
|
+
|
10
|
+
if ARGV.include? '-i'
|
11
|
+
interval = ARGV[ARGV.index('-i')+1].to_f
|
12
|
+
end
|
13
|
+
interval ||= 0.25
|
14
|
+
|
15
|
+
|
16
|
+
if ARGV.include? '-t'
|
17
|
+
|
18
|
+
begin
|
19
|
+
|
20
|
+
s = Beans::BeanCounter.new
|
21
|
+
|
22
|
+
while true do
|
23
|
+
$stdout.printf "\r$%.02f", s.query.to_f*multiplier
|
24
|
+
sleep(interval)
|
25
|
+
end
|
26
|
+
|
27
|
+
rescue Interrupt
|
28
|
+
$stdout.printf "\n"
|
29
|
+
|
30
|
+
rescue Errno::ECONNREFUSED
|
31
|
+
$stderr.puts "Unable to connect to beans server! Are you sure it's running on port #{Beans::Config.port}?"
|
32
|
+
|
33
|
+
rescue Errno::EPIPE
|
34
|
+
$stderr.printf "\nLost connection with beans server!\n"
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
elsif ARGV.include? 'start' or ARGV.include? 'stop' or ARGV.include? 'reset'
|
39
|
+
|
40
|
+
s = Beans::BeanCounter.new
|
41
|
+
case ARGV[0].strip.downcase
|
42
|
+
when 'start'
|
43
|
+
s.query('start')
|
44
|
+
when 'stop'
|
45
|
+
s.query('stop')
|
46
|
+
when 'reset'
|
47
|
+
s.query('reset')
|
48
|
+
end
|
49
|
+
|
50
|
+
else
|
51
|
+
|
52
|
+
begin
|
53
|
+
s = Beans::BeanCounter.new
|
54
|
+
$stdout.printf "$%.02f\n", s.query.to_f
|
55
|
+
|
56
|
+
rescue Errno::ECONNREFUSED
|
57
|
+
begin
|
58
|
+
# Presumably there's no server running, so let's just count some beans.
|
59
|
+
s = Beans::Stopwatch.new
|
60
|
+
s.start
|
61
|
+
|
62
|
+
while true do
|
63
|
+
$stdout.printf "\r$%.02f", s.query('dollars')*multiplier
|
64
|
+
sleep(interval)
|
65
|
+
end
|
66
|
+
rescue Interrupt
|
67
|
+
$stdout.printf "\n"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
data/bin/beans-server
ADDED
data/bin/beansd
ADDED
data/lib/beans.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
module Beans
|
2
|
+
class BeanCounter
|
3
|
+
|
4
|
+
def initialize(opts={})
|
5
|
+
opts[:port] ||= Config.port
|
6
|
+
@port = opts[:port]
|
7
|
+
end
|
8
|
+
|
9
|
+
def open
|
10
|
+
@socket = TCPSocket.new('127.0.0.1', @port)
|
11
|
+
end
|
12
|
+
|
13
|
+
def query( query='dollars' )
|
14
|
+
self.open
|
15
|
+
@socket.puts query
|
16
|
+
response = @socket.gets
|
17
|
+
self.close
|
18
|
+
response
|
19
|
+
end
|
20
|
+
|
21
|
+
def close
|
22
|
+
@socket.close
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'socket'
|
2
|
+
|
3
|
+
module Beans
|
4
|
+
class BeanServer
|
5
|
+
|
6
|
+
def initialize(opts={})
|
7
|
+
opts[:port] ||= Config.port
|
8
|
+
@server = TCPServer.new(opts[:port])
|
9
|
+
end
|
10
|
+
|
11
|
+
=begin
|
12
|
+
def in_background_loop &block
|
13
|
+
Thread.start do
|
14
|
+
loop do
|
15
|
+
begin
|
16
|
+
block.call
|
17
|
+
rescue StandardError => e
|
18
|
+
$stderr.puts "#{e.exception}: #{e.message}"
|
19
|
+
$stderr.puts e.backtrace
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
=end
|
25
|
+
|
26
|
+
def respond &block
|
27
|
+
|
28
|
+
#puts "Listening for bean counters on port #{opts[:port]}..."
|
29
|
+
|
30
|
+
loop do
|
31
|
+
Thread.start(@server.accept) do |client|
|
32
|
+
begin
|
33
|
+
response = block.call( client.gets )
|
34
|
+
client.puts response
|
35
|
+
client.close
|
36
|
+
rescue StandardError => e
|
37
|
+
$stderr.puts "#{e.exception}: #{e.message}"
|
38
|
+
$stderr.puts e.backtrace
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
data/lib/beans/config.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module Beans
|
4
|
+
class Config
|
5
|
+
|
6
|
+
class <<self
|
7
|
+
def config
|
8
|
+
@config ||= YAML.load(open(ENV['HOME']+'/.beans.yml'))
|
9
|
+
end
|
10
|
+
|
11
|
+
def salary
|
12
|
+
config['salary']
|
13
|
+
end
|
14
|
+
|
15
|
+
def per
|
16
|
+
config['per']
|
17
|
+
end
|
18
|
+
|
19
|
+
def salary_per_year
|
20
|
+
case per.downcase
|
21
|
+
when 'year'
|
22
|
+
salary
|
23
|
+
when 'month'
|
24
|
+
salary*12
|
25
|
+
when 'hour'
|
26
|
+
salary*hours_per_week*52
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def hours_per_week
|
31
|
+
config['hours_per_week']
|
32
|
+
end
|
33
|
+
|
34
|
+
def port
|
35
|
+
config['port']
|
36
|
+
end
|
37
|
+
|
38
|
+
def dollars_per_second
|
39
|
+
seconds_per_week = hours_per_week*3600.0
|
40
|
+
weeks_per_year = 52.0
|
41
|
+
salary_per_year / weeks_per_year / seconds_per_week
|
42
|
+
end
|
43
|
+
|
44
|
+
def seconds_between_notifications
|
45
|
+
config['notify_every'] || 1800
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
Binary file
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'ruby-growl'
|
2
|
+
|
3
|
+
LIBDIR=File.dirname(__FILE__)
|
4
|
+
|
5
|
+
module Beans
|
6
|
+
class Notification
|
7
|
+
|
8
|
+
def initialize( m1, m2 )
|
9
|
+
|
10
|
+
@growl = Growl.new('localhost', 'beans')
|
11
|
+
|
12
|
+
@growl.add_notification 'beans', 'beans', File.read(File.join(LIBDIR,'/images/bean-icon.png'))
|
13
|
+
@growl.notify 'beans', m1, m2
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
module Beans
|
2
|
+
class Stopwatch
|
3
|
+
|
4
|
+
def initialize
|
5
|
+
self.reset
|
6
|
+
end
|
7
|
+
|
8
|
+
def reset
|
9
|
+
t = Time.now
|
10
|
+
@first_started_at = nil
|
11
|
+
@last_notification_at = t
|
12
|
+
@previously_elapsed_time = 0.0
|
13
|
+
@last_stopped_at = t
|
14
|
+
@last_started_at = nil
|
15
|
+
@running = false
|
16
|
+
end
|
17
|
+
|
18
|
+
def start
|
19
|
+
if stopped?
|
20
|
+
t = Time.now
|
21
|
+
|
22
|
+
@first_started_at ||= t
|
23
|
+
|
24
|
+
@last_started_at = t
|
25
|
+
@running = true
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def stop
|
31
|
+
if running?
|
32
|
+
t = Time.now
|
33
|
+
|
34
|
+
@previously_elapsed_time ||= 0
|
35
|
+
@previously_elapsed_time += (t-@last_started_at).to_f
|
36
|
+
|
37
|
+
@last_stopped_at = t
|
38
|
+
@running = false
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
def stopped?
|
45
|
+
!@running
|
46
|
+
end
|
47
|
+
|
48
|
+
def running?
|
49
|
+
@running
|
50
|
+
end
|
51
|
+
|
52
|
+
def seconds_elapsed
|
53
|
+
if @last_started_at.nil?
|
54
|
+
0.0
|
55
|
+
else
|
56
|
+
s = @previously_elapsed_time
|
57
|
+
s += (Time.now-@last_started_at).to_f unless stopped?
|
58
|
+
s
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def minutes_elapsed
|
63
|
+
seconds_elapsed / 60.0
|
64
|
+
end
|
65
|
+
|
66
|
+
def hours_elapsed
|
67
|
+
minutes_elapsed / 60.0
|
68
|
+
end
|
69
|
+
|
70
|
+
def days_elapsed
|
71
|
+
hours_elapsed/24.0
|
72
|
+
end
|
73
|
+
|
74
|
+
def dollars_elapsed
|
75
|
+
seconds_elapsed * Config.dollars_per_second
|
76
|
+
end
|
77
|
+
|
78
|
+
def query(unit)
|
79
|
+
self.send( "#{unit.strip}_elapsed" )
|
80
|
+
end
|
81
|
+
|
82
|
+
def next_notification_at
|
83
|
+
@last_notification_at + Config.seconds_between_notifications
|
84
|
+
end
|
85
|
+
|
86
|
+
def seconds_to_next_notification
|
87
|
+
next_notification_at - Time.now
|
88
|
+
end
|
89
|
+
|
90
|
+
def nice_elapsed_time
|
91
|
+
if (s=seconds_elapsed) < 60
|
92
|
+
"#{sprintf("%i",s)} seconds"
|
93
|
+
elsif (m=minutes_elapsed) < 60
|
94
|
+
"#{sprintf("%i",m)} minutes"
|
95
|
+
elsif (h=hours_elapsed) < 24
|
96
|
+
"#{sprintf("%.1f",h)} hours"
|
97
|
+
else
|
98
|
+
"#{sprintf("%.1f",days_elapsed)} days"
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
|
103
|
+
def notify
|
104
|
+
@last_notification_at = Time.now
|
105
|
+
return false if @first_started_at.nil?
|
106
|
+
Beans::Notification.new(
|
107
|
+
sprintf( "$%.02f", dollars_elapsed),
|
108
|
+
"over #{nice_elapsed_time} since #{@first_started_at.strftime( "%-I:%M %p")}."
|
109
|
+
)
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
end
|
metadata
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: beans
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ricky Reusser
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-11-11 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: daemons
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ! '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
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: ruby-growl
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ! '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ! '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: sqlite3
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ! '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
description: A daemonized stopwatch with growl notifications so you don't forget.
|
56
|
+
email:
|
57
|
+
- rsreusser@gmail.com
|
58
|
+
executables:
|
59
|
+
- beans
|
60
|
+
- beans-server
|
61
|
+
- beansd
|
62
|
+
extensions: []
|
63
|
+
extra_rdoc_files: []
|
64
|
+
files:
|
65
|
+
- lib/beans/bean_counter.rb
|
66
|
+
- lib/beans/bean_server.rb
|
67
|
+
- lib/beans/config.rb
|
68
|
+
- lib/beans/dispatcher.rb
|
69
|
+
- lib/beans/images/bean-icon.png
|
70
|
+
- lib/beans/notification.rb
|
71
|
+
- lib/beans/stopwatch.rb
|
72
|
+
- lib/beans/version.rb
|
73
|
+
- lib/beans.rb
|
74
|
+
- Rakefile
|
75
|
+
- bin/beans
|
76
|
+
- bin/beans-server
|
77
|
+
- bin/beansd
|
78
|
+
homepage: https://github.com/rreusser/beans
|
79
|
+
licenses: []
|
80
|
+
metadata: {}
|
81
|
+
post_install_message:
|
82
|
+
rdoc_options: []
|
83
|
+
require_paths:
|
84
|
+
- lib
|
85
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ! '>='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - ! '>='
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '0'
|
95
|
+
requirements: []
|
96
|
+
rubyforge_project:
|
97
|
+
rubygems_version: 2.1.10
|
98
|
+
signing_key:
|
99
|
+
specification_version: 4
|
100
|
+
summary: A small utility to convert time (yours) to money (theirs)
|
101
|
+
test_files: []
|