Getting Fast File Finder Functionality Out of Vim

Problem:

All of these other editors have handy functionality to quickly find a file

Solution:

ctrlp.vim is a plugin that allows you to do that.

The plugin’s page has full instructions, but the most important detail after work done in yesterday’s post to declutter my vim windows:

After pressing Ctrl-P, use <c-t> or <c-v>, <c-x> to open the selected entry in a new tab or in a new split.

Otherwise, you’ll end up replacing your file in the currently open window with the file you’re opening.

Avoiding Scattered Vim Windows in MacVim

Problem:

I open too many windows in vim, which get buried in the clutter on my Mac OS X desktop.

Solution:

Open files in MacVim in an existing window using a tab

mvim --remote-tab new_file_to_open.rb

Open files in MacVim in an existing window using a split

mvim --remote-send ":split `pwd`/new_file_to_open.rb<CR>"

I packaged this in a zsh script called spvim. It will only handle relative paths for now, which in 99% of what I’m trying to open, anyway.

for i in $*
do
  mvim --remote-send ":split `pwd`/$i<CR>"
done

Open files in MacVim in an existing windows using a vertical split:

mvim --remote-send ":vsplit `pwd`/new_file_to_open.rb<CR>"

I packaged this in a zsh script called vspvim:

for i in $*
do
  mvim --remote-send ":vsplit `pwd`/$i<CR>"
done

Sources/Learning More:

(I believe most of these options are available in vim as well… I actually have gvim aliased to mvim because I'm still in the habit of calling gvim).

usage: vim [arguments] [file ..]       edit specified file(s)
   or: vim [arguments] -               read text from stdin
   or: vim [arguments] -t tag          edit file where tag is defined
   or: vim [arguments] -q [errorfile]  edit file with first error

Arguments:
   --           Only file names after this
   -g           Run using GUI (like "gvim")
   -f  or  --nofork Foreground: Don&#039;t fork when starting GUI
   -v           Vi mode (like "vi")
   -e           Ex mode (like "ex")
   -E           Improved Ex mode
   -s           Silent (batch) mode (only for "ex")
   -d           Diff mode (like "vimdiff")
   -y           Easy mode (like "evim", modeless)
   -R           Readonly mode (like "view")
   -Z           Restricted mode (like "rvim")
   -m           Modifications (writing files) not allowed
   -M           Modifications in text not allowed
   -b           Binary mode
   -l           Lisp mode
   -C           Compatible with Vi: &#039;compatible&#039;
   -N           Not fully Vi compatible: &#039;nocompatible&#039;
   -V[N][fname]     Be verbose [level N] [log messages to fname]
   -D           Debugging mode
   -n           No swap file, use memory only
   -r           List swap files and exit
   -r (with file name)  Recover crashed session
   -L           Same as -r
   -A           start in Arabic mode
   -H           Start in Hebrew mode
   -F           Start in Farsi mode
   -T <terminal>    Set terminal type to <terminal>
   -u <vimrc>       Use <vimrc> instead of any .vimrc
   -U <gvimrc>      Use <gvimrc> instead of any .gvimrc
   --noplugin       Don&#039;t load plugin scripts
   -p[N]        Open N tab pages (default: one for each file)
   -o[N]        Open N windows (default: one for each file)
   -O[N]        Like -o but split vertically
   +            Start at end of file
   +<lnum>      Start at line <lnum>
   --cmd <command>  Execute <command> before loading any vimrc file
   -c <command>     Execute <command> after loading the first file
   -S <session>     Source file <session> after loading the first file
   -s <scriptin>    Read Normal mode commands from file <scriptin>
   -w <scriptout>   Append all typed commands to file <scriptout>
   -W <scriptout>   Write all typed commands to file <scriptout>
   -x           Edit encrypted files
   --remote <files> Edit <files> in a Vim server if possible
   --remote-silent <files>  Same, don&#039;t complain if there is no server
   --remote-wait <files>  As --remote but wait for files to have been edited
   --remote-wait-silent <files>  Same, don&#039;t complain if there is no server
   --remote-tab[-wait][-silent] <files>  As --remote but use tab page per file
   --remote-send <keys> Send <keys> to a Vim server and exit
   --remote-expr <expr> Evaluate <expr> in a Vim server and print result
   --serverlist     List available Vim server names and exit
   --servername <name>  Send to/become the Vim server <name>
   --startuptime <file> Write startup timing messages to <file>
   -i <viminfo>     Use <viminfo> instead of .viminfo
   -h  or  --help   Print Help (this message) and exit
   --version        Print version information and exit

Thanks for Making it Unnecessarily Hard to Get a Harry Potter Book on My Daughter’s Kindle

Exclusive Dealers

Apparently, JK Rowling made a deal with Sony to exclusively sell Harry Potter e-books on the Sony e-book through the Pottermore site, through January 2012.

What I found out today was that I still had to go to that site in order to buy any e-book version of the Harry Potter books. That posed a major inconvenience in itself because my daughter got $35 in Amazon gift cards to buy Kindle books for her new Kindle that her grandfather bought her. She said she wanted to buy the Harry Potter books with those. The (first) problem with the Pottermore Shop site? Those gift cards would be useless.

User Unfriendliness

I went ahead and went through the motions to set up a Pottermore Shop account to buy the boxed set and then decided, just before checkout but after creating an account, to bail out of the process and go and buy the individual books at Amazon instead. If you go to the page for Harry Potter on the Kindle Store, you’ll find that all links go back to the Pottermore store.

Screen Shot 2013-12-26 at 2.03.49 PMSo, I resigned myself to going to the Pottermore.com site (manually typing it in.) I found that the site no longer recognized me as signed in. So, I used the username and password that I had previously created and stored (of course, the initial sign-up wouldn’t allow copy-and-paste) and found that my login wasn’t accepted. I tried copying and pasting and manually typing to no avail, until I was locked out of that “account”. In between attempts, I even tried a password reset. I was able to “reset” my password, but not able to use it to login.

I decided to sign my daughter up for an account and try again. It was then that I discovered that Pottermore.com apparently doesn’t use emails as usernames, but instead, gives you an option to pick one of five randomly generated [two-word + number] usernames. So, the login process for the shop and the main site are different.

Sony’s Half-Baked Download Control

Once I had purchased the boxed set, I still had to download the individual books separately. For that I had to link an account. Fortunately, Kindle was one of the options available, and I could authorize downloads to that account. Weird part of the download process is that you’re given 8 downloads per book, so you have some limitation as to how many places you can send it. Weirder still is that “direct download” is one of those options (I’m assuming that means a DRM-less copy). Why bother with the limitation? If someone is going to steal, they just have to do the unrestricted download once, but if someone legitimately chooses crappy e-book services that shut down, then they might (amazingly) run out of downloads.

Kudos for Sticking it to Amazon or Whomever, JK Rowling, But…

I can’t really fault JK Rowling for finding a way to break free of Amazon’s e-book cost structure. There are many smaller authors wary of getting sucked into Amazon’s vise grip. However, I can imagine that there are many young fans out there that got a Kindle for Christmas or their birthday along with an Amazon gift card, and they just wanted to have Harry Potter on their Kindle. Their gift card is no good outside of Amazon.

I go shopping on The Pragmatic Bookshelf for many of my technical books, so I can appreciate the value that can come from an independent publisher of books, but those books have no download restrictions on them. The only copyright enforcement on the e-book version of Pragmatic Bookshelf is the honor system (and the intended recipient’s name at the bottom.) I jump through hoops to download those books, but the audience of a Pragmatic is highly technical, so a few technical hurdles isn’t unreasonable.

Maybe the only kids who ever get e-book readers are kids whose dads are willing to contribute cash when the gift cards are of no use and willing to jump through hurdles to get the books downloaded and set up. I hope so, because then the minor inconveniences of 1000s of fans are worth the sweetened deal you got, and your fans will never know the difference.

Monkey patching if you need to force an error in delayed_job

I wanted to force an error condition in my cucumber tests, but the code ran through a DelayedJob, so I couldn’t redefine the code running under the .delay chain, because the DelayedJob worker will reload the normal code base for its processing. I finally realized that simply patching the delay instance method to return self for the class in question would bypass the delayed job.

def force_query_error
  ObjectDelayed.class_eval do
    alias :old_delay :delay
    alias :old_query :query
 
    def delay
      self
    end
 
    def query
      raise 'monkey'
    end
  end
end
 
def restore_query
  ObjectDelayed.class_eval do
    remove_method :query
    remove_method :delay
    alias :query :old_query
    alias :delay :old_delay
  end
end

This is not to be confused will actually allowing DelayedJob to do its thing:

 
When(/^I wait for processing of jobs$/) do
  Delayed::Worker.new(:quiet => false).work_off
  Timeout::timeout(10) do
    until Delayed::Job.count == 0 do
    end
  end
end

On Mac OSX Lion: “ERROR: Error installing ruby-oci8: ERROR: Failed to build gem native extension”

Running Mac OSX Lion, trying to bundle install a project that included the ruby-oci8 gem, I received the following error:

Building native extensions.  This could take a while...
ERROR:  Error installing ruby-oci8:
	ERROR: Failed to build gem native extension.

The fix:

Fortunately, the 64-bit client for Oracle has now been updated for Lion/Mountain Lion. Download them from Instant Client Downloads for Mac OSX (Intel x86). Downloading “Basic Lite” and the “SDK” under Version 11.2… (64-bit) worked for me.

Unzip the files to ~/instantclient_11_2 so that the sdk directory and libs are directory beneath it:

$ ls ~/instantclient_11_2 
BASIC_LITE_README    genezi               libclntsh.dylib.11.1 libocci.dylib.11.1   libocijdbc11.dylib   ojdbc6.jar           uidrvci
adrci                libclntsh.dylib      libnnz11.dylib       libociicus.dylib     ojdbc5.jar           sdk                  xstreams.jar

Add the following to my .zlogin or .bash_login

export DYLD_LIBRARY_PATH=~/instantclient_11_2

Rerun the login shell (exec $SHELL -l) or restart your shell.

Link the libclntsh.dylib. in ~/instantclient_11_2 to a versionless lib file name:

ln -s libclntsh.dylib.11.1 libclntsh.dylib

Your ruby-oci8 should install now.

Fun with the Ruby & (unary ampersand) and String#to_proc

In trying to grasp the unary & operator in ruby, I ran across [Stupid Ruby Tricks: String#to_proc].

Therefore, I decided I had to further twist the language features to my own will.

Like many things in Ruby with duck-typing, the following syntax:

('a'..'z').map &:upcase

…makes use of the fact that Symbol has a #to_proc method, which means that the following call is an explicit version of the above call.

('a'..'z').map &:upcase.to_proc

If you wanted to allow for a shorthand notation for map, then you could add #to_proc to String, as follows:

class String
  def to_proc
    eval "Proc.new { |*args| args.first#{self} }"
  end
end

This allows for the following call:

[1,2,3].map &'*2'                     # [2, 4, 6]

…which is equivalent to:

[1,2,3].map { |*args| args.first*2 } # note: no space

…But what if I wanted to specify a library function to have the Array members passed to? (Certainly, this is venturing deeper into solving a problem that doesn’t exist.) Also, what if I want to pass an Array for the arguments?

class String
  def to_proc
    # assume an initial word character is a library function, otherwise evaluate as before
    if /^w/ =~ self
      eval "Proc.new { |*args| #{self}(*args.first) }"
    else
      eval "Proc.new { |*args| args.first#{self} }"
    end
  end

The above code will expand an embedded Array into an argument list:

[[2,2],[9,3],[64,4]].map &'Math.log'  # [1.0, 2.0, 3.0]
[1.0, 2.0, 4.0].map &'Math.sqrt'      # [1.0, 1.4142135623730951, 1.7320508075688772]
[1,2,3].map &'*2'                     # [2, 4, 6]
[[2,2],[9,3],[64,4], 4].map &'Math.log' # [1.0, 2.0, 3.0, 1.3862943611198906]

Twitter Bootstrap Modal Won’t Load Content from Another (“Remote”) Page

Short answer

You apparently need a skeleton [modal ... modal-body ... /modal] for your data-target, not just an empty modal with the data-target.

A Programmer’s False Assumptions – The API is Broken or Old

Initially, I was concerned that my Rails plugin wasn’t using a version of Bootstrap that didn’t include the remote loading of a page into the modal (via href attribute). That functionality has only existed since 2.0.4.

However, when I checked the script being loaded into the project against the current bootstrap-modal.js on github, the MODAL DATA-API section was the same. Repeated checks of the javascript being loaded into my project confirmed that the code was up-to-date enough.

Maybe a Little Too Helpful

The forums discussing how to load remote content into a Bootstrap Modal suggested that the data-target div could be empty:
Original calling page sample:
The contents of the calling page div never changed.

<a class="btn small" href="/stuff/1/edit" data-toggle="modal" data-target="#editstuff">Settings...</a>
.
.
.
<div id="editstuff" class="modal hide">This should not display</div>

“Remote” page sample

...extra code here...
<div id="editstuff" class="modal hide">
<div class="modal-header">
<button class="close" type="button" data-dismiss="modal">×</button>
<h3 id="myModalLabel">Remote header</h3></div>
<div class="modal-body">Actual edit stuff</div>
<div class="modal-footer"><button class="btn" data-dismiss="modal">Cancel</button> <!-- just a sample --> 
<button class="btn" data-dismiss="modal">Close</button> 
<button id="save_btn" class="btn btn-primary">Save changes</button></div>
</div>
...extra code here...

What Was Really Happening

The documentation states that, “If a remote url is provided, content will be loaded via jQuery’s load method and injected into the .modal-body.”

From there, I put a stub modal-body in my calling page’s modal and discovered that modal-header, modal-body, and modal-footer appeared to be getting replaced now!
New calling page:

<div id="editstuff" class="modal hide">
<div class="modal-body"><!-- content will be loaded here --></div>
</div>

Experimenting with Rails’ String Manipulation from ActiveSupport::CoreExtensions::String::Inflections

This is my experimentation with the string manipulation methods used [largely internally] in Rails. This is largely a repeat of the documentation here, but I did dig into pluralize/singularize and a couple of other examples more in depth. Look at [path to active_support gem version]/lib/active_support/inflections.rb for the full list of plural, singular, irregular, and uncountable regular expressions and words.

require 'active_support/core_ext/string/inflections'
# camelcase, camelize 
"separate_words".camelize # SeparateWords
"active_record".camelcase(:lower) # separateWords
 
# classify - converts to first upper CamelCase, last word made singular
"this_and_thats".classify # ThisAndThat
 
# constantize - looks for declared constant with the name specified
"Integer".constantize # returns constant [class] Integer
"integer".constantize # results in NameError: wrong constant name integer
"Boo".constantize # results in NameError: uninitialized constnat Boo (assuming it isn't declared)
 
# dasherize - underscores to dashes
"a_b_c".dasherize # a-b-c
 
# demodulize - removes module namespace from module in string.
ModuleA::SubModule::Module".demodulize # Module
 
# foreign_key - create name of a foreign key to class name, optional parameter to separate class from id
"Integer".foreign_key # integer_id
"Integer".foreign_key(false) # integerid
 
# humanize - capitalizes first word, down cases rest, turns underscores into space.
"McLean".humanize # Mclean
"Apple_Banana".humanize # Apple banana
 
# parameterize - replaces special characters in a string, default separator is '-'
#   - whitespace is a special character here, underscore is not
#   - consecutive special characters become a single separator
#   - converts to lowercase
"Apple@!Banana".parameterize # "apple banana"
 
# pluralize 
"raman".pluralize # "ramen" -- apparently, "man" --> "men" even on a made-up word
"jazz".pluralize # "jazzs" 
"box".pluralize # "boxes"
"beep".pluralize # "beeps"
"moose".pluralize # "mooses"
"rhombus".pluralize # "rhombuses"
"octopus".pluralize # "octopi"
 
# singularize
"ramen".singularize # "raman"
"jazzes".singularize # "jazze"
"boxen".singularize # "boxen" -- yeah, not a real word again
 
# tableize - snake_case string and pluralize last word
"RedHeadedStepchild".tableize # "red_headed_stepchildren"
 
# titlecase / titleize - capitalize all words, 
"two_three-four.five:six".titleize # "Two Three Four.Five:Six"
 
# underscore - snake_case words (opposite of CamelCase) and covert :: to /
"Number::OneTwo::Integer".underscore # "number/one_two/integer"