Passing Additional Arguments to FactoryGirl Factories

I wanted to create a factory that would link various states to my model, but from a specified list instead of automatically generated. I discovered that these are transient attributes:

FactoryGirl.define do
  factory :business do
    name "MyString"
#
#
#
    trait :with_states do
      ignore do
        state_list []
      end
      # add association equal to state code
      after(:create) do |business, evaluator|
        evaluator.state_list.each do |state_code|
          business.states << State.find_by(state_code: state_code)
        end
      end
    end
  end
end

This factory can now be called with:

  FactoryGirl.create(:business, :with_states. state_list: %w(KY IN))

Of course, now that I’m passing in the attributes, I can probably just use:

FactoryGirl.define do
  factory :business do
    name "MyString"
#
#
#
    ignore do
      with_states []
    end
    # add association equal to state code
    after(:create) do |business, evaluator|
      evaluator.with_states.each do |state_code|
        business.states << State.find_by(state_code: state_code)
      end
    end
  end
end

and then the call is even simpler:

  FactoryGirl.create(:business, with_states: %w(KY IN))

Rails 4, Empty Hashes, Strong Parameters, exception_notification and filtering out my new ActionController::ParameterMissing errors

I’m finally coming around to the “strong parameters” pattern encouraged (required?) in Rails 4.

One thing that I did notice was that missing the base parameter raised an ActionController::ParameterMissing error. I discovered this via controllers tests generated by rspec-rails when I created a controller scaffold, which generates tests for “Invalid Params”:

In the controller spec:

describe "with invalid params" do
  it "re-renders the &#039;new&#039; template" do
    # Trigger the behavior that occurs when invalid params are submitted
    User.any_instance.stub(:save).and_return(false)
    post :create, {:user => {  }}, valid_session
    response.should render_template("new")
  end 
end

In the controller:

  def user_params
    params.require(:licensee).permit(:parameter1, :parameter2)
  end

Despite the fact that params[:licensee] exists, the empty hash triggers the ActionController::ParameterMissing error. That’s fine. I can expect { post :create, {:user => { }}, valid_session }.to raise_error(ActionController::ParameterMissing), or eliminate the test altogether.

However, with exception_notifier, I don’t really want to be notified about these errors, so I configured my environment files to filter this exception (and also CanCan::AccessDenied).

  config.middleware.use ExceptionNotification::Rack,
    :ignore_exceptions => %w(CanCan::AccessDenied ActionController::ParameterMissing) + ExceptionNotifier.ignored_exceptions,
    :email => {
      :email_prefix => "[#{Rails.env}] project name exception: ",
      :sender_address => %{"Exception Notifier" <exceptions@example.com>},
      :exception_recipients => [ `git config user.email`.chomp ]
   }

Bundler reports errors across several different projects when running `bundle install`

When running bundle install, I was getting the following for several projects:

Bundler gave the error "Could not find {gem_name_version} in any of the sources" while processing "{project_path}/Gemfile". Perhaps you forgot to run "bundle install"?
.
.
.

I found someone else was having the same problem too.

In my case, I had uninstalled and reinstalled a version of ruby using rbenv and began seeing this error reported for every project using the same ruby version as the one I had uninstalled and reinstalled. I haven’t exactly gotten to the bottom of why this happens, but going around and running bundle install in each of the project directories got rid of the error.

method_missing: undefined method `increment_open_transactions` database_cleaner

When using database_cleaner (0.9.1) with rails (4.1.0)

{my home path}/.rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/gems/activerecord-4.1.0/lib/active_record/dynamic_matchers.rb:26:in `method_missing': undefined method `increment_open_transactions' for ActiveRecord::Base:Class (NoMethodError)
    from {my home path}/.rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/gems/database_cleaner-0.9.1/lib/database_cleaner/active_record/transaction.rb:13:in `start'
    from {my home path}/.rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/gems/database_cleaner-0.9.1/lib/database_cleaner/base.rb:73:in `start'
    from {my home path}/.rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/gems/database_cleaner-0.9.1/lib/database_cleaner/configuration.rb:75:in `block in start'
    from {my home path}/.rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/gems/database_cleaner-0.9.1/lib/database_cleaner/configuration.rb:75:in `each'
    from {my home path}/.rbenv/versions/2.1.1/lib/ruby/gems/2.1.0/gems/database_cleaner-0.9.1/lib/database_cleaner/configuration.rb:75:in `start'

Somehow we were using an old version of database_cleaner,

gem "database_cleaner",   "~> 0.9.1"

because the fix was authored a year ago.

gem "database_cleaner", "~> 1.2.0"

I guess that will teach me to dig too deeply before simply trying the latest version of a gem.

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.