lolcommits 0.5.7 → 0.5.8.pre1

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 (42) hide show
  1. checksums.yaml +8 -8
  2. data/.rubocop.yml +1 -0
  3. data/.rubocop_todo.yml +16 -161
  4. data/.travis.yml +11 -4
  5. data/Gemfile +2 -2
  6. data/Rakefile +24 -32
  7. data/bin/lolcommits +62 -125
  8. data/features/bugs.feature +24 -9
  9. data/features/lolcommits.feature +136 -145
  10. data/features/plugins.feature +16 -33
  11. data/features/step_definitions/lolcommits_steps.rb +91 -71
  12. data/features/support/env.rb +18 -48
  13. data/features/support/path_helpers.rb +9 -8
  14. data/lib/lolcommits.rb +3 -10
  15. data/lib/lolcommits/capturer.rb +10 -2
  16. data/lib/lolcommits/capturer/capture_linux.rb +1 -1
  17. data/lib/lolcommits/capturer/capture_linux_animated.rb +12 -13
  18. data/lib/lolcommits/capturer/capture_mac_animated.rb +12 -13
  19. data/lib/lolcommits/cli/fatals.rb +77 -0
  20. data/lib/lolcommits/cli/launcher.rb +29 -0
  21. data/lib/lolcommits/cli/process_runner.rb +48 -0
  22. data/lib/lolcommits/cli/timelapse_gif.rb +45 -0
  23. data/lib/lolcommits/configuration.rb +30 -133
  24. data/lib/lolcommits/git_info.rb +58 -28
  25. data/lib/lolcommits/installation.rb +11 -21
  26. data/lib/lolcommits/platform.rb +134 -0
  27. data/lib/lolcommits/plugin.rb +2 -2
  28. data/lib/lolcommits/plugins/dot_com.rb +15 -15
  29. data/lib/lolcommits/plugins/lol_protonet.rb +68 -0
  30. data/lib/lolcommits/plugins/lol_twitter.rb +12 -15
  31. data/lib/lolcommits/plugins/lol_yammer.rb +4 -6
  32. data/lib/lolcommits/plugins/lolsrv.rb +8 -11
  33. data/lib/lolcommits/plugins/loltext.rb +7 -7
  34. data/lib/lolcommits/plugins/tranzlate.rb +70 -70
  35. data/lib/lolcommits/plugins/uploldz.rb +8 -8
  36. data/lib/lolcommits/runner.rb +36 -35
  37. data/lib/lolcommits/version.rb +1 -1
  38. data/lolcommits.gemspec +11 -10
  39. data/test/lolcommits_test.rb +35 -0
  40. data/test/plugins_test.rb +52 -0
  41. metadata +41 -20
  42. data/test/test_lolcommits.rb +0 -78
@@ -12,7 +12,7 @@ module Lolcommits
12
12
  tmpdir = Dir.mktmpdir
13
13
 
14
14
  # Default delay is 1s
15
- delay = if capture_delay != 0 then capture_delay else 1 end
15
+ delay = capture_delay != 0 ? capture_delay : 1
16
16
 
17
17
  # There's no way to give a capture delay in mplayer, but a number of frame
18
18
  # mplayer's "delay" is actually a number of frames at 25 fps
@@ -8,21 +8,20 @@ module Lolcommits
8
8
 
9
9
  # capture the raw video with ffmpeg video4linux2
10
10
  system_call "ffmpeg -v quiet -y -f video4linux2 -video_size 320x240 -i #{capture_device_string} -t #{capture_duration} #{video_location} > /dev/null"
11
- if File.exists?(video_location)
12
- # convert raw video to png frames with ffmpeg
13
- system_call "ffmpeg #{capture_delay_string} -v quiet -i #{video_location} -t #{animated_duration} #{frames_location}/%09d.png > /dev/null"
11
+ return unless File.exist?(video_location)
12
+ # convert raw video to png frames with ffmpeg
13
+ system_call "ffmpeg #{capture_delay_string} -v quiet -i #{video_location} -t #{animated_duration} #{frames_location}/%09d.png > /dev/null"
14
14
 
15
- # use fps to set delay and number of frames to skip (for lower filesized gifs)
16
- fps = video_fps(video_location)
17
- skip = frame_skip(fps)
18
- delay = frame_delay(fps, skip)
19
- debug "Capturer: anaimated gif choosing every #{skip} frames with a frame delay of #{delay}"
15
+ # use fps to set delay and number of frames to skip (for lower filesized gifs)
16
+ fps = video_fps(video_location)
17
+ skip = frame_skip(fps)
18
+ delay = frame_delay(fps, skip)
19
+ debug "Capturer: anaimated gif choosing every #{skip} frames with a frame delay of #{delay}"
20
20
 
21
- # create the looping animated gif from frames (picks nth frame with seq)
22
- seq_command = "seq -f #{frames_location}/%09g.png 1 #{skip} #{Dir["#{frames_location}/*"].length}"
23
- # convert to animated gif with delay and gif optimisation
24
- system_call "convert -layers OptimizeTransparency -delay #{delay} -loop 0 `#{seq_command}` -coalesce #{snapshot_location} > /dev/null"
25
- end
21
+ # create the looping animated gif from frames (picks nth frame with seq)
22
+ seq_command = "seq -f #{frames_location}/%09g.png 1 #{skip} #{Dir["#{frames_location}/*"].length}"
23
+ # convert to animated gif with delay and gif optimisation
24
+ system_call "convert -layers OptimizeTransparency -delay #{delay} -loop 0 `#{seq_command}` -coalesce #{snapshot_location} > /dev/null"
26
25
  end
27
26
 
28
27
  private
@@ -8,21 +8,20 @@ module Lolcommits
8
8
 
9
9
  # capture the raw video with videosnap
10
10
  system_call "#{executable_path} -s 240 #{capture_device_string}#{capture_delay_string}-t #{animated_duration} --no-audio #{video_location} > /dev/null"
11
- if File.exists?(video_location)
12
- # convert raw video to png frames with ffmpeg
13
- system_call "ffmpeg -v quiet -i #{video_location} -t #{animated_duration} #{frames_location}/%09d.png"
11
+ return unless File.exist?(video_location)
12
+ # convert raw video to png frames with ffmpeg
13
+ system_call "ffmpeg -v quiet -i #{video_location} -t #{animated_duration} #{frames_location}/%09d.png"
14
14
 
15
- # use fps to set delay and number of frames to skip (for lower filesized gifs)
16
- fps = video_fps(video_location)
17
- skip = frame_skip(fps)
18
- delay = frame_delay(fps, skip)
19
- debug "Capturer: anaimated gif choosing every #{skip} frames with a frame delay of #{delay}"
15
+ # use fps to set delay and number of frames to skip (for lower filesized gifs)
16
+ fps = video_fps(video_location)
17
+ skip = frame_skip(fps)
18
+ delay = frame_delay(fps, skip)
19
+ debug "Capturer: anaimated gif choosing every #{skip} frames with a frame delay of #{delay}"
20
20
 
21
- # create the looping animated gif from frames (picks nth frame with seq)
22
- seq_command = "seq -f #{frames_location}/%09g.png 1 #{skip} #{Dir["#{frames_location}/*"].length}"
23
- # convert to animated gif with delay and gif optimisation
24
- system_call "convert -layers OptimizeTransparency -delay #{delay} -loop 0 `#{seq_command}` -coalesce #{snapshot_location}"
25
- end
21
+ # create the looping animated gif from frames (picks nth frame with seq)
22
+ seq_command = "seq -f #{frames_location}/%09g.png 1 #{skip} #{Dir["#{frames_location}/*"].length}"
23
+ # convert to animated gif with delay and gif optimisation
24
+ system_call "convert -layers OptimizeTransparency -delay #{delay} -loop 0 `#{seq_command}` -coalesce #{snapshot_location}"
26
25
  end
27
26
 
28
27
  def executable_path
@@ -0,0 +1,77 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require 'lolcommits/platform'
3
+ require 'methadone'
4
+
5
+ module Lolcommits
6
+ module CLI
7
+ # Helper methods for failing on error conditions in the lolcommits CLI.
8
+ module Fatals
9
+ include Lolcommits
10
+ include Methadone::CLILogging
11
+
12
+ # Check for platform related conditions that could prevent lolcommits from
13
+ # working properly.
14
+ #
15
+ # Die with an informative error message if any occur.
16
+ def self.die_on_fatal_platform_conditions!
17
+ # make sure the capture binaries are in a good state
18
+ if Platform.platform_mac?
19
+ %w(imagesnap videosnap).each do |executable|
20
+ next if File.executable? File.join(Configuration::LOLCOMMITS_ROOT, 'vendor', 'ext', executable, executable)
21
+ fatal "Couldn't properly execute #{executable} for some reason, "\
22
+ 'please file a bug?!'
23
+ exit 1
24
+ end
25
+ elsif Platform.platform_linux?
26
+ unless Platform.command_which('mplayer')
27
+ fatal "Couldn't find mplayer in your PATH!"
28
+ exit 1
29
+ end
30
+ end
31
+
32
+ # make sure we can find the Impact truetype font
33
+ unless File.readable? File.join(Configuration::LOLCOMMITS_ROOT, 'vendor', 'fonts', 'Impact.ttf')
34
+ fatal "Couldn't properly read Impact font from gem package, "\
35
+ 'please file a bug?!'
36
+ exit 1
37
+ end
38
+
39
+ # make sure imagemagick is around and good to go
40
+ unless Platform.valid_imagemagick_installed?
41
+ fatal 'FATAL: ImageMagick does not appear to be properly installed!'\
42
+ '(or version is too old)'
43
+ exit 1
44
+ end
45
+
46
+ # check for a error condition with git config affecting ruby-git
47
+ if Platform.git_config_color_always?
48
+ fatal 'Due to a bug in the ruby-git library, git config for color.ui'\
49
+ " cannot be set to 'always'."
50
+ fatal "Try setting it to 'auto' instead!"
51
+ exit 1
52
+ end
53
+ end
54
+
55
+ # Die with an informative error message if ffmpeg is not installed.
56
+ # This is only used for certain functions (such as animation), so only run
57
+ # this when you know the user wants to perform one of them.
58
+ def self.die_if_no_valid_ffmpeg_installed!
59
+ unless Platform.valid_ffmpeg_installed?
60
+ fatal 'FATAL: ffmpeg does not appear to be properly installed!'
61
+ exit 1
62
+ end
63
+ end
64
+
65
+ # If we are not in a git repo, we can't do git related things!
66
+ # Die with an informative error message in that case.
67
+ def self.die_if_not_git_repo!
68
+ debug 'Checking for valid git repo'
69
+ Git.open('.') # FIXME: should be extracted to GitInfo class
70
+ rescue ArgumentError
71
+ # ruby-git throws an argument error if path isnt for a valid git repo.
72
+ fatal "Erm? Can't do that since we're not in a valid git repository!"
73
+ exit 1
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,29 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require 'launchy'
3
+
4
+ module Lolcommits
5
+ module CLI
6
+ # Helper class for wrapping the opening of files on the desktop in a
7
+ # cross-platform way.
8
+ #
9
+ # Right now this is mostly just a wrapper for Launchy, in case we want
10
+ # to factor out it's dependency later or swap it out.
11
+ class Launcher
12
+ def self.open_image(path)
13
+ open_with_launchy(path)
14
+ end
15
+
16
+ def self.open_folder(path)
17
+ open_with_launchy(path)
18
+ end
19
+
20
+ # Opens with Launchy, which knows how to open pretty much anything
21
+ # local files, urls, etc.
22
+ #
23
+ # Private so we replace it later easier if we want.
24
+ def self.open_with_launchy(thing)
25
+ Launchy.open(thing)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,48 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ module Lolcommits
4
+ module CLI
5
+ # Helper class for forking lolcommits process to the background (or not).
6
+ class ProcessRunner
7
+ # Initializes a new process runner.
8
+ #
9
+ # @param config [Lolcommits::Configuration]
10
+ def initialize(config)
11
+ @configuration = config
12
+ end
13
+
14
+ # Forks the lolcommits process if requested.
15
+ #
16
+ # Writes the PID of the lolcommits process to the filesystem when
17
+ # backgrounded, for monitoring purposes.
18
+ #
19
+ # @param please [Boolean] whether or not to fork lolcommits process
20
+ # @yield the main event loop for lolcommits
21
+ def fork_me?(please, &block)
22
+ if please
23
+ $stdout.sync = true
24
+ write_pid fork {
25
+ yield block
26
+ delete_pid
27
+ }
28
+ else
29
+ yield block
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def write_pid(pid)
36
+ File.open(pid_file, 'w') { |f| f.write(pid) }
37
+ end
38
+
39
+ def delete_pid
40
+ File.delete(pid_file) if File.exist?(pid_file)
41
+ end
42
+
43
+ def pid_file
44
+ File.join(@configuration.loldir, 'lolcommits.pid')
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,45 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require 'lolcommits/cli/fatals'
3
+
4
+ require 'mini_magick'
5
+
6
+ module Lolcommits
7
+ module CLI
8
+ # Creates an animated timeline GIF of lolcommits history.
9
+ class TimelapseGif
10
+ # param config [Lolcommits::Configuration]
11
+ def initialize(config)
12
+ @configuration = config
13
+ end
14
+
15
+ # Runs the history timeline animator task thingy
16
+ # param args [String] the arg passed to the gif command on CLI (optional)
17
+ def run(args = nil)
18
+ Fatals.die_if_not_git_repo!
19
+
20
+ case args
21
+ when 'today'
22
+ lolimages = @configuration.jpg_images_today
23
+ filename = "#{Date.today}.gif"
24
+ else
25
+ lolimages = @configuration.jpg_images
26
+ filename = 'archive.gif'
27
+ end
28
+
29
+ if lolimages.empty?
30
+ warn 'No lolcommits have been captured for this time yet.'
31
+ exit 1
32
+ end
33
+
34
+ puts '*** Generating animated gif.'
35
+
36
+ gif = MiniMagick::Image.new File.join @configuration.archivedir, filename
37
+
38
+ # This is for ruby 1.8.7, *lolimages just doesn't work with ruby 187
39
+ gif.run_command('convert', *['-delay', '50', '-loop', '0', lolimages, "#{gif.path}"].flatten)
40
+
41
+ puts "*** #{gif.path} generated."
42
+ end
43
+ end
44
+ end
45
+ end
@@ -1,4 +1,5 @@
1
1
  # -*- encoding : utf-8 -*-
2
+
2
3
  module Lolcommits
3
4
  class Configuration
4
5
  LOLCOMMITS_BASE = ENV['LOLCOMMITS_DIR'] || File.join(ENV['HOME'], '.lolcommits')
@@ -7,20 +8,17 @@ module Lolcommits
7
8
 
8
9
  def initialize(attributes = {})
9
10
  attributes.each do |attr, val|
10
- self.send("#{attr}=", val)
11
+ send("#{attr}=", val)
11
12
  end
12
13
  end
13
14
 
14
15
  def read_configuration
15
- if File.exists?(configuration_file)
16
- YAML.load(File.open(configuration_file))
17
- else
18
- nil
19
- end
16
+ return unless File.exist?(configuration_file)
17
+ YAML.load(File.open(configuration_file))
20
18
  end
21
19
 
22
20
  def configuration_file
23
- "#{self.loldir}/config.yml"
21
+ "#{loldir}/config.yml"
24
22
  end
25
23
 
26
24
  def loldir
@@ -32,51 +30,40 @@ module Lolcommits
32
30
 
33
31
  def archivedir
34
32
  dir = File.join(loldir, 'archive')
35
- if not File.directory? dir
36
- FileUtils.mkdir_p dir
37
- end
33
+ FileUtils.mkdir_p dir unless File.directory? dir
38
34
  dir
39
35
  end
40
36
 
41
37
  def most_recent
42
- Dir.glob(File.join self.loldir, '*.{jpg,gif}').max_by { |f| File.mtime(f) }
38
+ Dir.glob(File.join loldir, '*.{jpg,gif}').max_by { |f| File.mtime(f) }
43
39
  end
44
40
 
45
41
  def jpg_images
46
- Dir.glob(File.join self.loldir, '*.jpg').sort_by { |f| File.mtime(f) }
42
+ Dir.glob(File.join loldir, '*.jpg').sort_by { |f| File.mtime(f) }
47
43
  end
48
44
 
49
45
  def jpg_images_today
50
- jpg_images.select { |f| Date.parse(File.mtime(f).to_s) === Date.today }
46
+ jpg_images.select { |f| Date.parse(File.mtime(f).to_s) == Date.today }
51
47
  end
52
48
 
53
49
  def raw_image(image_file_type = 'jpg')
54
- File.join self.loldir, "tmp_snapshot.#{image_file_type}"
50
+ File.join loldir, "tmp_snapshot.#{image_file_type}"
55
51
  end
56
52
 
57
53
  def main_image(commit_sha, image_file_type = 'jpg')
58
- File.join self.loldir, "#{commit_sha}.#{image_file_type}"
54
+ File.join loldir, "#{commit_sha}.#{image_file_type}"
59
55
  end
60
56
 
61
57
  def video_loc
62
- File.join(self.loldir, 'tmp_video.mov')
58
+ File.join(loldir, 'tmp_video.mov')
63
59
  end
64
60
 
65
61
  def frames_loc
66
- File.join(self.loldir, 'tmp_frames')
67
- end
68
-
69
- def puts_devices
70
- # TODO: handle other platforms here (linux/windows)
71
- if self.class.platform_mac?
72
- capturer = Lolcommits::CaptureMacAnimated.new
73
- puts `#{capturer.executable_path} -l`
74
- puts "Specify a device with --device=\"{device name}\" or set the LOLCOMMITS_DEVICE env variable"
75
- end
62
+ File.join(loldir, 'tmp_frames')
76
63
  end
77
64
 
78
65
  def puts_plugins
79
- puts "Available plugins: #{Lolcommits::PLUGINS.map(&:name).join(', ')}"
66
+ puts "Available plugins: #{Lolcommits::Runner.plugins.map(&:name).join(', ')}"
80
67
  end
81
68
 
82
69
  def ask_for_plugin_name
@@ -86,10 +73,8 @@ module Lolcommits
86
73
  end
87
74
 
88
75
  def find_plugin(plugin_name)
89
- Lolcommits::PLUGINS.each do |plugin|
90
- if plugin.name == plugin_name
91
- return plugin.new(nil)
92
- end
76
+ Lolcommits::Runner.plugins.each do |plugin|
77
+ return plugin.new(nil) if plugin.name == plugin_name
93
78
  end
94
79
 
95
80
  puts "Unable to find plugin: '#{plugin_name}'"
@@ -97,30 +82,27 @@ module Lolcommits
97
82
  end
98
83
 
99
84
  def do_configure!(plugin_name)
100
- if plugin_name.to_s.strip.empty?
101
- plugin_name = ask_for_plugin_name
102
- end
85
+ plugin_name = ask_for_plugin_name if plugin_name.to_s.strip.empty?
103
86
 
104
87
  plugin = find_plugin(plugin_name)
105
- if plugin
106
- config = self.read_configuration || {}
107
- plugin_config = plugin.configure_options!
108
- # having a plugin_config, means configuring went OK
109
- if plugin_config
110
- # save plugin and print config
111
- config[plugin_name] = plugin_config
112
- save(config)
113
- puts self
114
- puts "\nSuccessfully configured plugin: #{plugin_name}"
115
- else
116
- puts "\nAborted plugin configuration for: #{plugin_name}"
117
- end
88
+ return unless plugin
89
+ config = read_configuration || {}
90
+ plugin_config = plugin.configure_options!
91
+ # having a plugin_config, means configuring went OK
92
+ if plugin_config
93
+ # save plugin and print config
94
+ config[plugin_name] = plugin_config
95
+ save(config)
96
+ puts self
97
+ puts "\nSuccessfully configured plugin: #{plugin_name}"
98
+ else
99
+ puts "\nAborted plugin configuration for: #{plugin_name}"
118
100
  end
119
101
  end
120
102
 
121
103
  def save(config)
122
104
  config_file_contents = config.to_yaml
123
- File.open(self.configuration_file, 'w') do |f|
105
+ File.open(configuration_file, 'w') do |f|
124
106
  f.write(config_file_contents)
125
107
  end
126
108
  end
@@ -131,24 +113,6 @@ module Lolcommits
131
113
 
132
114
  # class methods
133
115
 
134
- def self.platform
135
- if platform_fakeplatform?
136
- ENV['LOLCOMMITS_FAKEPLATFORM']
137
- elsif platform_fakecapture?
138
- 'Fake'
139
- elsif platform_mac?
140
- 'Mac'
141
- elsif platform_linux?
142
- 'Linux'
143
- elsif platform_windows?
144
- 'Windows'
145
- elsif platform_cygwin?
146
- 'Cygwin'
147
- else
148
- fail 'Unknown / Unsupported Platform.'
149
- end
150
- end
151
-
152
116
  def self.loldir_for(basename)
153
117
  loldir = File.join(LOLCOMMITS_BASE, basename)
154
118
 
@@ -167,72 +131,5 @@ module Lolcommits
167
131
  end
168
132
  loldir
169
133
  end
170
-
171
- def self.platform_mac?
172
- RUBY_PLATFORM.to_s.downcase.include?('darwin')
173
- end
174
-
175
- def self.platform_linux?
176
- RUBY_PLATFORM.to_s.downcase.include?('linux')
177
- end
178
-
179
- def self.platform_windows?
180
- !!RUBY_PLATFORM.match(/(win|w)32/)
181
- end
182
-
183
- def self.platform_cygwin?
184
- RUBY_PLATFORM.to_s.downcase.include?('cygwin')
185
- end
186
-
187
- def self.platform_fakecapture?
188
- (ENV['LOLCOMMITS_FAKECAPTURE'] == '1' || false)
189
- end
190
-
191
- def self.platform_fakeplatform?
192
- ENV['LOLCOMMITS_FAKEPLATFORM']
193
- end
194
-
195
- def self.valid_imagemagick_installed?
196
- return false unless self.command_which('identify')
197
- return false unless self.command_which('mogrify')
198
- # you'd expect the below to work on its own, but it only handles old versions
199
- # and will throw an exception if IM is not installed in PATH
200
- MiniMagick.valid_version_installed?
201
- rescue
202
- puts "FATAL: ImageMagick >= #{MiniMagick.minimum_image_magick_version} required (http://imagemagick.org)"
203
- puts 'Please check the following command works and returns a valid version number'
204
- puts '=> mogrify --version'
205
- exit 1
206
- end
207
-
208
- def self.valid_ffmpeg_installed?
209
- self.command_which('ffmpeg')
210
- end
211
-
212
- def self.git_config_color_always?
213
- `git config color.ui`.chomp =~ /always/
214
- end
215
-
216
- def self.can_animate?
217
- ['Mac', 'Linux'].include? platform
218
- end
219
-
220
- # Cross-platform way of finding an executable in the $PATH.
221
- # idea taken from http://bit.ly/qDaTbY, if only_path is true, only the path
222
- # is returned (not the path and command)
223
- #
224
- # which('ruby') #=> /usr/bin/ruby
225
- def self.command_which(cmd, only_path = false)
226
- exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
227
- ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
228
- exts.each do |ext|
229
- exe = "#{path}/#{cmd}#{ext}"
230
- if File.executable? exe
231
- return only_path ? path : exe
232
- end
233
- end
234
- end
235
- nil
236
- end
237
134
  end
238
135
  end