Brady Love

Commanding Tmux With Emacs

A few days ago I was pair programming with my good friend Evan. Evan is a Vim user, so naturally as an Emacs lover I am always giving him crap about his precious Vim. However while we were pairing he showed me an awesome tool he had under his belt with Vim. Evan was using the power of Vimux and tslime to send commands to his Tmux session from within Vim. I was instantly jeleous and the next day I began to investigate. As it turns out Tmux makes it super simple to sand a command to any pane from any terminal. Here is what I discovered.

The Details On Tmux

Tmux comes with the send-keys command that you can send along with passing in the session name, window name and pane number and the keys that you would like to send to the specified pane. Try it out, start up a new Tmux session, you can do this by giving the session a name or allowing Tmux to assign the session a number. To startup a new Tmux session with a name you can run the following.

$ tmux new -s sample-session

Or to have Tmux assign a session number you can send tmux new without the -s sample-session argument. Then you can run tmux ls or tmux list-sessions to get a list of running Tmux sessions so that you can get the session number which is 0 in the following example.

$ tmux new
$ tmux ls
0: 1 windows (created Fri May 24 20:43:18 2013) [105x129] (attached)

Now that you have your Tmux session running, go ahead and open up another terminal. From this new terminal we will send the tmux send-keys command, passing in the -t argument with the session-name:window-name.pane-number followed by the keys we want to send to the Tmux pane, window-name can also be replaced by the windows number. Here are some examples of what this would look like.

# Executes 'ls' on pane 1 of window 1 of sample-session
$ tmux send-keys -t sample-session:1.1 "ls" Enter

# Executes 'bundle exec rake routes' on pane 2 of window 3 of session 0
$ tmux send-keys -t 0:3.2

Lets Add Some Emacs

Now that we know how to send a command to a Tmux session from another terminal its pretty easy to make this happen from Emacs. Emacs provides a function named shell-command that will execute any shell command you pass it. If you have read my blog post Controlling Spotify From Within Emacs on Linux, you may recognize this. Here is an example of how I used this command to run Cucumber in my terminal from within Emacs.

1
2
3
4
(defun tmux-exec-cucumber ()
  "Execute cucumber in tmux pane"
  (interactive)
  (shell-command "tmux send-keys -t 0:1.1 'bundle exec cucumber' Enter"))

Once this this function has been evaluated you can run it by pressing M-x then typing in the function name tmux-exec-cucumber and pressing enter. When you do it will kick off Cucumber in pane 1 of window 1 of session 0.

This is cool, howerver pretty impractical considering every time we want to send the command to a different pane we will have to edit this file, and if we add more commands, that adds more editing for different panes… What a major pain in the ass. So lets go ahead and define some variables with some defaults for the sesssion-name, window-name and pane number, then we will define a function to set these variables without having to edit any files.

1
2
3
4
5
6
7
8
9
10
11
(setq tmux-session-name 0)
(setq tmux-window-name 1)
(setq tmux-pane-number 1)

(defun tmux-setup (x y z)
  "Setup global variables for tmux session, window, and pane"
  (interactive "sEnter tmux session name: \nsEnter tmux window name: \nsEnter tmux pane number: ")
  (setq tmux-session-name x)
  (setq tmux-window-name y)
  (setq tmux-pane-number z)
  (message "Tmux Setup, session name: %s, window name: %s, pane number: %s" tmux-session-name tmux-window-name tmux-pane-number))

When we run this new function with M-x tmux-setup, it will ask us what Tmux session, window and pane we want to send our commands too, then assigns our input to the variables that we setup. Next we need to modify our tmux-exec-cucumber function to use these new variables. We can do that by modifying it to look like this

1
2
3
4
5
(defun tmux-exec-cucumber ()
  "Execute cucumber in tmux pane"
  (interactive)
  (shell-command
    (format "tmux send-keys -t %s:%s.%s 'bundle exec cucumber' Enter" tmux-session-name tmux-window-name tmux-pane-number)))

With these changes in place when we run M-x tmux-exec-cucumber it will send bundle exec cucumber to the pane we specified when we ran M-x tmux-setup.

Now this is a bit better, however the function isn’t very reusable so lets go ahead and change the tmux-exec-cucumber function to just tmux-exec and accept the command as a parameter and pass that command instead of bundle exec cucumber.

1
2
3
4
5
(defun tmux-exec (command)
  "Execute command in tmux pane"
  (interactive)
  (shell-command
    (format "tmux send-keys -t %s:%s.%s '%s' Enter" tmux-session-name tmux-window-name tmux-pane-number command)))

Then we will define a new function for executing the cucumber command.

1
2
3
4
(defun tmux-exec-cucumber ()
  "Execute 'bundle exec cucumber' in tmux pane"
  (interactive)
  (tmux-exec "bundle exec cucumber"))

And there you have it, M-x tmux-exec-cucumber will kick off cucumber in your Tmux pane still, but this function is a little easier to duplicate for adding extra commands, such as tmux-rake-routes.

1
2
3
4
(defun tmux-exec-rake-routes ()
  "Execute 'bundle exec rake routes' in tmux pane"
  (interactive)
  (tmux-exec "bundle exec rake rouets"))

I plan on doing some more exploration around Tmux and Emacs to see what other cool tricks I can find and to see how I can expand on this subject. Until then you can checkout my tmux.el file I have on my .emacs.d Github repo. Enjoy :)

Controlling Spotify From Within Emacs on Linux

A while back I switched from VIM to Emacs and I have been meaning to write some blog posts on some cool things I have been able to do within Emacs, so here is one on how I control spotify from within Emacs.

The Problem

So recently I built a new computer, I was debating between getting a System76 laptop, a new Apple MacBook Pro or building a desktop and running Linux on it. I ended up going for building a desktop and running Linux on it. Originally I started out with Linux Mint 14, then I switched to Ubuntu, then I wanted something more powerful and switched to ArchLinux. A common problem I had across all three of these distributions was out of the box, I could not use the media keys on my keyboard to control Spotify. They would work in with other media players such as Rhythmbox or Audacious but not Spotify.

The Solution

99% of the time that I am listening to music on this computer I have Emacs open, so I decided to fix this in Emacs rather than solving this problem through the system where a solution may not work from one desktop environment to the next.

What I did to fix this problem was described an Elisp function to send a command to spotify via dbus-send, then created functions for toggling play/pause, previous, and next. Then I created some key bindings for those functions. Here is that code on how I did that.

spotify.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
(defun spotify-linux-command (command-name) "Execute command for Spotify" (interactive)
  (setq command-text (format "%s%s" "dbus-send --print-reply --dest=org.mpris.MediaPlayer2.spotify /org/mpris/MediaPlayer2 org.mpris.MediaPlayer2.Player." command-name))
  (shell-command command-text))

(defun spotify-toggle () "Play/Pause Spotify" (interactive)
  (spotify-linux-command "PlayPause"))

(defun spotify-previous () "Starts the song over in Spotify" (interactive)
  (spotify-linux-command "Previous"))

(defun spotify-next () "Next song in Spotify" (interactive)
  (spotify-linux-command "Next"))

(global-set-key (kbd "<f7>") 'spotify-previous)
(global-set-key (kbd "<f8>") 'spotify-toggle)
(global-set-key (kbd "<f9>") 'spotify-next)

Thats all it takes, I am getting closer and closer to never having to leave Emacs. At work I still use OS X so I also made this work when under OS X, however its not a big deal in that situation since the media keys work fine with Spotify there, however if your interested on how I did that please checkout spotify.el in my .emacs.d repository. Enjoy.

Dead Simple Time Based OTP With Google Authenticator

If you have ever been curious on how to add two factor authentication to your Rails/Sinatra app then today I will feed your curiosity. I am just going to show you the basics of creating a TOTP (Time based OTP) token, and creating a QRCode based on that token that can be used to add the token to your Google Authenticator on your favorite smart phone.

Requirements

To be able to do this with the method I am showing here you will need to have Ruby, I tested this with Ruby 1.9.3-p385. You will also need to have libqrencode installed on your system. To do this visit http://fukuchi.org/works/qrencode/index.html.en and download qrencode-3.4.1.tar.gz. I installed the library on Linux Mint 14 with the following instructions. This process may vary for your OS but these should work for most people.

$ tar xzvf qrencode-3.4.1.tar.gz
$ cd qrencode-3.4.1
$ ./configure
$ make
$ sudo make install

Next you will need to grab a couple gems, these gems are awesome and make this process very painless. While we are at it we will also create totp.rb so we have somewhere to put our code.

$ gem install rotp qrencoder
$ touch totp.rb

On with the code!

Then open totp.rb in your favourite text editor and enter in the following code. Once you have done that we will go through it and explain what each bit does.

totp.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
require 'rotp'
require 'qrencoder'

random = ROTP::Base32.random_base32
totp   = ROTP::TOTP.new(random)

qr_string = totp.provisioning_uri("bradylove.com-love.brady@gmail.com")
qr_code   = QREncoder.encode(qr_string)
qr_code.png(pixels_per_module: 4, margin: 1).save("qr_code.png")

loop do
  printf("What is your most excelent OTP? ")

  otp = gets.chop
  puts totp.verify_with_drift(otp, 5)
end

Break it down

Now that you have entered that in lets go through it. The first two lines require the rotp and qrencoder gems.

1
2
require 'rotp'
require 'qrencoder'

The next creates a 16 character base32 encoded random string (If you are using this with a Rails or Sinatra app you will want to save this random seed in your database associated with the user, and it would also be a good idea to encrypt it, maybe that will be another blog post soon). The next line creates our TOTP object which will handle the algorithms for creating and verifying the OTP.

1
2
random = ROTP::Base32.random_base32
totp   = ROTP::TOTP.new(random)

Next we use our TOTP object to create the URI with an otpauth:// schema which ends up looking something like otpauth://totp/bradylove.com-love.brady@gmail.com?secret=JBSWY3DPEHPK3PXP, the bradylove.com-love.brady@gmail.com parameter we are passing it is just an identifier that will be displayed on the Google Authenticator as a label for the OTP.

1
qr_string = totp.provisioning_uri("bradylove.com-love.brady@gmail.com")

Once we have that string we want to use it to create a QRCode. The QREncoder gem gives you plenty of options choices of ways to create the QRCode for this example we just go ahead and save it as a PNG image on the local directory.

1
2
qr_code   = QREncoder.encode(qr_string)
qr_code.png(pixels_per_module: 4, margin: 1).save("qr_code.png")

Finally to test the lines of code above we create an endless loop that will ask for the OTP from the user in the terminal and verify the given OTP. Where we verify the OTP we are calling verify_with_drift this will check the OTP based on Time.now +/– 5 seconds. This will make up for any timing issues between your computer and your phone.

1
2
3
4
5
6
loop do
  printf("What is your most excelent OTP? ")

  otp = gets.chop
  puts totp.verify_with_drift(otp, 5)
end

Run it

So now that we have that all covered lets go ahead and test it. If you followed the code in this example, when we execute this script it will create a QRCode and save it to qr_code.png in the current directory. You can then open that image in your file browser, and scan it with your Google Authenticator, Google Authenticator will then display a new OTP every 30 seconds, check it out!

$ ruby totp.rb
What is your most excelent OTP? 707918
true
What is your most excelent OTP? 707918
true
What is your most excelent OTP? 707918
false
What is your most excelent OTP? 773428
true

When you are done having OTP fun you can hit Ctrl-c to kill the process. This example is also available on my GitHub at https://github.com/bradylove/blog-totp. Now get back to work and smile!

Rounded Corners With Ruby Motion

Another trick I learned with Objective-C/Ruby Motion is giving a view is rounded corners. When I first set out to learn how to do rounded corners with Ruby Motion, I quickly found a one liner calling layer.cornerRadius= on a view, which I thought was awesome! I remember thinking to myself “that was too easy but awesome!”. I soon found out I was right, it was too easy. So I set out again to find a better solution and found a similar question on stack overflow for someone trying to accomplish the same thing with Objective-C. I find myself using this alot now so I thought I would share it and hope it helps someone else as well. Before I get started with the details. I have already created a project with “motion create AppName”, created a window and initilized the RootViewController in my app_delegate.rb. And here is the starting code for the RootViewController which just has a single UIView, 200x200 in size, centered and colored green.

root_view_controller.rb
1
2
3
4
5
6
7
8
9
10
11
class RootViewController < UIViewController
  def viewDidLoad
    view.backgroundColor = UIColor.lightGrayColor

    @rounded_rect = UIView.alloc.initWithFrame([[view.size.width / 2 - 100, view.size.height / 2 - 100], [200, 200]])
    @rounded_rect.backgroundColor = UIColor.greenColor


    view.addSubview @rounded_rect
  end
end

So you should have something that looks like this.

The Easy Way

Okay so real quick I thought I would show you the easy way and show you the problem I found with it. To quickly achieve this we just need to set the cornerRadius on the UIView’s layer. So after setting the backgroundColor for @rounded_rect I am going to add this line.

root_view_controller.rb
1
@rounded_rect.layer.cornerRadius = 30.0

Awesome! We now have some rounded corners!

The Problem

Okay so this works out great for us if we need to quickly add rounded corners to a view. But the problem comes when we add subviews that are going to be in the area of those rounded corners. To show you this I am going to add another UIView as a subview to our @rounded_rect view. We will call this @non_rounded_subview, I am going to make this subview the same size as its parent and color it red by adding the following.

root_view_controller.rb
1
2
3
4
@non_rounded_subview = UIView.alloc.initWithFrame(@rounded_rect.bounds)
@non_rounded_subview.backgroundColor = UIColor.redColor

@rounded_rect.addSubview @non_rounded_subview

OH NOES! Where did our rounded corners go?

Our rounded corners are still there, however the parent views rounded corners do not clip the child view. To show this better I will set the backgroundColor of @non_rounded_subview to have a slight transparent to better show this.

root_view_controller.rb
1
@non_rounded_subview.backgroundColor = UIColor.colorWithRed(1.0, green: 0.0, blue: 0.0, alpha: 0.5)

The Solution

So the easy way works great if you just need some simple rounded corners and arent going to be placing anything over them. However in my case I needed the rounded corners of the parent view to clip the child view. The solution I came up with ended up being even greater then I expected, not only cliping child views but giving me alot more control over the corners. To accomplish this I used UIBezierPath and CAShapeLayer to create a layer mask and then applied the layer mask to the parent view (@rounded_rect). To do this I removed the following line

root_view_controller.rb
1
@rounded_rect.layer.cornerRadius = 30.0

and replaced it with this

root_view_controller.rb
1
2
3
4
5
6
7
mask_path = UIBezierPath.bezierPathWithRoundedRect(@rounded_rect.bounds,
                                                   byRoundingCorners: UIRectCornerAllCorners,
                                                   cornerRadii:       CGSizeMake(30.0, 30.0))
mask_layer = CAShapeLayer.layer
mask_layer.frame = @rounded_rect.bounds
mask_layer.path = mask_path.CGPath
@rounded_rect.layer.mask = mask_layer

And our rounded corners are back and better than ever!!!

The cornerRadii for the UIBezierPath is where we set how much extreme we want our corners rounded. The first value of the CGSizeMake is a float for the width of your corner radius, the other being a float for the height of your corner radius. So you can change those values to something like

root_view_controller
1
cornerRadii:       CGSizeMake(40.0, 150.0))

And you will end up with something a little different.

You also have more control in which corners actually get the rounding by editing the “byRoundingCorners” value. I currently have that set to UIRectCornerAllCorners. Which we can set to UIRectCornerAllCorners, UIRectCornerTopLeft, UIRectCornerTopRight, UIRectCornerBottomLeft, UIRectCornerBottomRight or any combonation of these seperated by a pipe ( | ). For example to round only the top left and bottom right corners I would change the following.

root_view_controlelr.rb
1
byRoundingCorners: UIRectCornerAllCorners,

to

root_view_controlelr.rb
1
byRoundingCorners: UIRectCornerTopLeft | UIRectCornerBottomRight,

Ending up with,

Well thats about it, in the end my code ended up looking like this incase you missed something. Or you can check it out on my Github at https://github.com/bradylove/RoundedCorners-RubyMotion

root_view_controller.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class RootViewController < UIViewController
  def viewDidLoad
    view.backgroundColor = UIColor.lightGrayColor

    @rounded_rect = UIView.alloc.initWithFrame(
      [[view.size.width / 2 - 100, view.size.height / 2 - 100], [200, 200]])
    @rounded_rect.backgroundColor = UIColor.greenColor

    mask_path = UIBezierPath.bezierPathWithRoundedRect(@rounded_rect.bounds,
                                                       byRoundingCorners: UIRectCornerTopLeft | UIRectCornerBottomRight,
                                                       cornerRadii:       CGSizeMake(40.0, 100.0))
    mask_layer = CAShapeLayer.layer
    mask_layer.frame = @rounded_rect.bounds
    mask_layer.path = mask_path.CGPath
    @rounded_rect.layer.mask = mask_layer

    @non_rounded_subview = UIView.alloc.initWithFrame(@rounded_rect.bounds)
    @non_rounded_subview.backgroundColor = UIColor.colorWithRed(1.0, green: 0.0, blue: 0.0, alpha: 0.5)

    @rounded_rect.addSubview @non_rounded_subview
    view.addSubview @rounded_rect
  end
end

Hope you enjoyed. I have not done a whole lot of documentation or blogging but it is something I want to get better at and do more of so any input is more than welcome. Also if anyone has a good suggestion for a screenshot app that does uploads that would be helpful too. Thank you.

Create Gradients With Ease in Ruby Motion

Lately I have been messing around with Ruby Motion and have been learning alot about the way things are done in iOS. Along the way there have been many things I wanted to do but had no clue how (Having very little expirience with Objective-C). One of the things I wanted to figure out how to do was to create gradients programatically so I would not have to images. After about 3 hours of trying to figure it out and failing miserably I gave up and used images anyway. But the next day it suddenly hit me and I was able to figure it out and it was alot easier then what I was trying to do the night before. So I figured someone else might find this useful.

In the first part of this example I am going to show you how add a gradient background on a UIView. So first thing we need to do is create our Ruby Motion project and setup our view controller.

1
2
motion create GradientExample
cd GradientExample

Then open up the Rakefile created by the generator and add the QuartzCore framework.

Rakefile
1
2
3
4
5
6
7
8
$:.unshift("/Library/RubyMotion/lib")
require 'motion/project'

Motion::Project::App.setup do |app|
  # Use `rake config' to see complete project settings.
  app.name = 'EasyGradients'
  app.frameworks << "QuartzCore"
end

Next we will create our UIWindow and add a view controller to it. So open up your app_delegate.rb and make it look like this.

app/app_delegate.rb
1
2
3
4
5
6
7
8
9
10
class AppDelegate
  def application(application, didFinishLaunchingWithOptions:launchOptions)
    @window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds)
    @window.rootViewController = GradientViewController.new
    @window.rootViewController.wantsFullScreenLayout = true
    @window.makeKeyAndVisible

    true
  end
end

Then create the file for our GradientViewController.

1
touch app/gradient_view_controller.rb

Open up the gradient_view_controller.rb. This is were we are going to work our gradient magic.

app/gradient_view_controller.rb
1
2
3
4
5
6
7
8
9
class GradientViewController < UIViewController
  def viewDidLoad
    gradient = CAGradientLayer.layer
    gradient.frame = view.bounds
    gradient.colors = [UIColor.blackColor.CGColor, UIColor.whiteColor.CGColor]

    view.layer.addSublayer(gradient)
  end
end

Then back in your console you can go ahead and run the rake command to launch the app in the iOS Simulator and should have something like this.

You may notice that gradient.colors just accepts an array so we can easily add more colors to the array.

app/gradient_view_controller.rb
1
2
gradient.colors = [UIColor.blackColor.CGColor, UIColor.redColor.CGColor,
                   UIColor.whiteColor.CGColor, UIColor.blueColor.CGColor]

And you will end up with this

For more control over the locations of the gradients you can add an array to gradient locations with a Float making their location. 0 being top 1 being bottom.

app/gradient_view_controller.rb
1
gradient.locations = [0.1, 0.2, 0.8, 0.9]

Which will end up someting like this

Well thats about it, you can easily use these gradients for backgrounds for almost all UI objects. Hope this helps someone that is as lost as me. There are some more iOS/Ruby Motion tricks I have been learning so check back soon on write ups for them.

Refining My Vim: Better Rakefile

So tonight I am taking a closer look at my Vim config to see what I can change to help make my vim experience better. A few months ago I got away from using pre-configured setups of Vim (such as janus) and setup my own Vim config from scratch. I figured this would make a good blog series focusing on one thing at a time, so here we go.

Better Rakefile

One of the things I did when creating my own Vim configuration was created a Rakefile in my ~/.vim directory that makes my setup more mobile. My current rakefile basically has a list of plugins that I want installed, then checks if a folder already exists with a name matching the plugins name, if it doesn’t then it will download the plugin via git. There is some functionality missing from this that I would like to have, and there is some repetition that bothers me. Before we begin, here is what my current rakefile looks like.

rakefile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
task :default do
  clone "https://github.com/scrooloose/nerdtree.git", "NERDTree"
  clone "https://github.com/vim-scripts/The-NERD-Commenter.git", "NERD-Commenter"
  clone "https://github.com/vim-scripts/L9.git", "L9"
  clone "https://github.com/wincent/Command-T.git", "Command-T"
  clone "https://github.com/mileszs/ack.vim.git", "Ack"
  clone "https://github.com/tpope/vim-rails.git", "Rails"
  clone "https://github.com/vim-ruby/vim-ruby.git", "Ruby"
  clone "https://github.com/mattn/gist-vim.git", "Gist"
  clone "https://github.com/davidoc/taskpaper.vim.git", "TaskPaper"
  clone "https://github.com/vim-scripts/vimwiki.git", "VimWiki"
  clone "https://github.com/tpope/vim-endwise.git", "Endwise"
  clone "https://github.com/ervandew/supertab", "SuperTab"
  clone "https://github.com/lucapette/vim-ruby-doc.git", "Ruby-Doc"
  clone "https://github.com/rbgrouleff/bclose.vim.git", "BClose"
  clone "https://github.com/bartekd/better-snipmate-snippets.git", "betterSnipMate"
  clone "https://github.com/tpope/vim-surround", "surround"

  # Syntax Files
  clone "https://github.com/tpope/vim-markdown.git", "Markdown"
  clone "https://github.com/leshill/vim-json.git", "JSON"
  clone "https://github.com/tpope/vim-haml.git", "HAML"
  clone "https://github.com/kchmck/vim-coffee-script.git", "CoffeeScript"
  clone "https://github.com/pangloss/vim-javascript.git", "Javascript"
  clone "https://github.com/vim-scripts/eruby.vim.git", "eruby"
  clone "https://github.com/groenewege/vim-less.git", "Less"

  # Colors
  clone "https://github.com/altercation/vim-colors-solarized.git", "Color-Solarized"
  clone "https://github.com/squil/vim_colors.git", "Squil-Colors"
  clone "https://github.com/tomasr/molokai.git", "Molokai"
end

def clone(url, name)
  if File.exists?("bundle/#{name}")
  puts "[Exists] #{name}"
  else
  puts "[Installing] #{name}"
  system("git clone #{url} bundle/#{name}")
  puts "[Installed] #{name}"
  end
end

Lets identify the problems I have with this.

  • “clone” is getting called over and over, wouldn’t it be better if we could just have an object of the git repos and their names, then grab them as needed?
  • I cannot list installed plugins.
  • I cannot update a single plugin, in order to update a plugin I have to remove the folder that the plugin is installing into, and run the rake command.
  • I cannot easily remove a single plugin using the rake command

Lets fix those problems

Lets start at the top and see what we can do to refactor this code to remove the repetition. The simplest thing I can think of is to create a method called plugins that returns a hash with the plugin name as a key and the git url as its value. So we end up with something like this. (Shortened for times sake)

rakefile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def plugins
  { "NERDTree"       => "https://github.com/scrooloose/nerdtree.git",
  "NERD-Commenter"  => "https://github.com/vim-scripts/The-NERD-Commenter.git",
  "L9"            => "https://github.com/vim-scripts/L9.git",
  "Ack"          => "https://github.com/mileszs/ack.vim.git",
  "Rails"          => "https://github.com/tpope/vim-rails.git",
  "Ruby"            => "https://github.com/vim-ruby/vim-ruby.git",

  # Syntax Plugins
  "Markdown"        => "https://github.com/tpope/vim-markdown.git",
  "JSON"            => "https://github.com/leshill/vim-json.git",
  "HAML"            => "https://github.com/tpope/vim-haml.git",

  # Colors
  "Solarized"      => "https://github.com/altercation/vim-colors-solarized.git",
  "Squil-Colors"    => "https://github.com/squil/vim_colors.git",
  "Molokai"      => "https://github.com/tomasr/molokai.git"
  }
end

In order to make use of this new hash method we will need to update our default rake task like so.

rake
1
2
3
task :default do
  plugins.each_pair { |key, value| clone key, value}
end

To me this is much more pleasing for a couple reasons, one reason being I am not repeatedly calling having to type clone, can just add the name and url of the plugin to the hash. Secondly and more importantly is the fact that the plugins method is reusable to fix my other issues.

One down three to go

So another feature I want in my rakefile is the ability to call rake list and getting a list of installed plugins. This is really easy to do, we are going to create 2 methods, the rake task and then a method to get a list of installed apps. First we will make the method that gets the list for us.

rakefile
1
2
3
def installed_plugins
  Dir.foreach("bundle").drop(2)
end

This is a very simple method that just gets a list of of files/directories in the “bundle” directory. However when you use Dir.foreach() it also returns 2 unnecessary entries, “.” and “..”. Luckily they are at the top of the list so we easily get rid of them with .drop(2). We now have our list so lets put it to use and create our rake task.

rakefile
1
2
3
4
task :list do
  puts "Plugins installed in ~/.vim/bundle"
  installed_plugins.each { |plugin| puts plugin }
end

We can now use this rake task by running rake list in terminal.

Halfway there

We have two problems out of the way and now we are going to tackle updating single plugins. Before we do that we need to consider how we want to handle updating. Do we want to check the git repository and see if there are updates? Or do we want to just delete the directory and re-install the plugin? I personally do not store any configuration in my plugin folders, so I have no issues with taking the easy route and just deleting the directory and re-installing the plugin, however if this is not the case for you, you might want to find a better way to handle this.

Before we try and delete the existing copy of the plugin we want to make sure it exists with this simple method.

rakefile
1
2
3
4
5
6
7
8
def plugin_installed?(name)
  if installed_plugins.include? name
    return true
  else
    puts "Plugin not found in bundle direcotry"
    return false
  end
end

We also want to make sure that plugin is in our list of plugins before we delete the local copy. We can easily do that by checking our hash that we created earlier includes the name of the plugin.

rakefile
1
2
3
4
5
6
7
8
def plugin_in_plugins?(name)
  if plugins.include? name
    return true
  else
    puts "Plugin not in hash of plugins"
    return false
  end
end

Once we have verified that the plugin exists in our hash and locally in the “bundle” directory we can remove it.

rakefile
1
2
3
4
5
6
7
def delete_plugin(name)
  if plugin_installed?(name)
    puts "[Removing] #{name}"
    FileUtils.rm_rf("bundle/#{name}")
    puts "[Removed] #{name}"
  end
end

We will then use our existing clone method to re-install the plugin. With all our building blocks in place we will now put them together in a update method to handle the logic of verifying that we can in fact update the plugin and then execute the methods to update it.

rakefile
1
2
3
4
5
6
def update(name)
  if plugin_installed?(name) && plugin_in_plugins?(name)
    delete_plugin(name)
    clone(name, plugins.values_at(name)[0])
  end
end

Finally we need to create the rake task to put all this code to use!

rakefile
1
2
3
task :update, [:name] do |t, args|
  update(args.name)
end

This rake task will accept an argument for the name of the plugin and can be used by running rake update[pluginName] in terminal. (If you are using Zsh you will need to add \ in front of the square brackets like rake update\[pluginName\])

Last but not least

The last item on my wish list is to be able to be able to remove a plugin by running rake destroy[pluginName]. Luckily by trying to make our methods as reusable as possible we already developed the “delete_plugin” method and just need to add the rake task.

rakefile
1
2
3
task :destroy, [:name] do |t, args|
  delete_plugin(args.name)
end

After running rake destroy[pluginName] of course we will need to open up our rakefile and remove the plugin from our hash.

Wrap up

After we have gone through all the steps here is what we end up with.

rakefile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
task :default do
  plugins.each_pair { |key, value| clone key, value}
end

task :list do
  puts "Plugins installed in ~/.vim/bundle"
  installed_plugins.each { |plugin| puts plugin }
end

task :update, [:name] do |t, args|
  update(args.name)
end

task :destroy, [:name] do |t, args|
  delete_plugin(args.name)
end

def plugins
  {
    "NERDTree"        => "https://github.com/scrooloose/nerdtree.git",
    "NERD-Commenter"  => "https://github.com/vim-scripts/The-NERD-Commenter.git",
    "L9"              => "https://github.com/vim-scripts/L9.git",
    "Ack"             => "https://github.com/mileszs/ack.vim.git",
    "Rails"           => "https://github.com/tpope/vim-rails.git",
    "Ruby"            => "https://github.com/vim-ruby/vim-ruby.git",
    "Gist"            => "https://github.com/mattn/gist-vim.git",
    "TaskPaper"       => "https://github.com/davidoc/taskpaper.vim.git",
    "VimWiki"         => "https://github.com/vim-scripts/vimwiki.git",
    "Endwise"         => "https://github.com/tpope/vim-endwise.git",
    "SuperTab"        => "https://github.com/ervandew/supertab",
    "Ruby-Doc"        => "https://github.com/lucapette/vim-ruby-doc.git",
    "BClose"          => "https://github.com/rbgrouleff/bclose.vim.git",
    "BetterSnipMate"  => "https://github.com/bartekd/better-snipmate-snippets.git",
    "Surround"        => "https://github.com/tpope/vim-surround",

    # Syntax Plugins
    "Markdown"        => "https://github.com/tpope/vim-markdown.git",
    "JSON"            => "https://github.com/leshill/vim-json.git",
    "HAML"            => "https://github.com/tpope/vim-haml.git",
    "CoffeeScript"    => "https://github.com/kchmck/vim-coffee-script.git",
    "Javascript"      => "https://github.com/pangloss/vim-javascript.git",
    "Eruby"           => "https://github.com/vim-scripts/eruby.vim.git",
    "Less"            => "https://github.com/groenewege/vim-less.git",

    # Colors
    "Solarized"       => "https://github.com/altercation/vim-colors-solarized.git",
    "Squil-Colors"    => "https://github.com/squil/vim_colors.git",
    "Molokai"         => "https://github.com/tomasr/molokai.git"
  }
end

def clone(name, url)
  if File.exists?("bundle/#{name}")
    puts "[Exists] #{name}"
  else
    puts "[Installing] #{name}"
    system("git clone #{url} bundle/#{name}")
    puts "[Installed] #{name}"
  end
end

def update(name)
  if plugin_installed?(name) && plugin_in_plugins?(name)
    delete_plugin(name)
    clone(name, plugins.values_at(name)[0])
  end
end

def installed_plugins
  Dir.foreach("bundle").drop(2)
end

def plugin_installed?(name)
  if installed_plugins.include? name
    return true
  else
    puts "Plugin not found in bundle direcotry"
    return false
  end
end

def plugin_in_plugins?(name)
  if plugins.include? name
    return true
  else
    puts "Plugin not in hash of plugins"
    return false
  end
end

def delete_plugin(name)
  if plugin_installed?(name)
    puts "[Removing] #{name}"
    FileUtils.rm_rf("bundle/#{name}")
    puts "[Removed] #{name}"
  end
end

There is a lot you can do with a rake file to help make your Vim setup easier to add and remove to. You don’t have to limit rake files to your Ruby apps or Vim setups either, there are many problems where a nice rake file is a viable solution.

vim

New Blog!

So it has been a while since I gave the whole blog thing a try and I was thinking now would be a good time to start writing blog posts again. So for now I have some configuration to do. Check back soon to see whats new.