April 17th, 2013
Often after generating a Rails migration I want to edit it, but it’s annoying to have to type in those dang timestamps. So I wrote a little script that just opens the newest one:
#!/bin/sh
s db/migrate/`ls db/migrate | tail -n 1`
Put that in ~/bin/latest-migration, chmod u+x it, and add ~/bin to your PATH and you can just run latest-migration from within any Rails folder. Of course you have to replace ’s’ with the command for invoking your text editor of choice.
Tags: bash, migrations, rails
Posted in Uncategorized | No Comments »
September 20th, 2012
I spent bunch of time scratching my head, trying to figure out why my ruby executables (rails, heroku, etc) weren’t working. I thought it was because I had an old custom shebang in my .gemrc, but that didn’t seem to change anything. I ran gem regenerate binstubs and that didn’t help either. I still to this day don’t even know what package provides ruby_bundler_wrapper, or even where it lives.
BUT, I figured out the problem for me was that I had my gems stored in a global rvm gemset, so I had to regenerate the binstubs in there:
rvm @global do rvm regenerate binstubs
And that seemed to do the trick. Long morning of system administration today.
Tags: bash, bundler, ruby, rvm
Posted in Uncategorized | 2 Comments »
September 4th, 2012
I’m not 100% sure what did the trick for me, but the last thing I did was symlink my gcc binary to gcc-4.2, which for some reason is hardcoded in Nokogiri:
sudo ln -s /usr/bin/gcc /usr/bin/gcc-4.2
As suggested here. The Nokogiri gem is totally unhelpful with the error messages. It just tells you that libxml2 can’t be found, no matter what the build problem is. If you are having trouble getting Nokogiri to install, your friend is the mkmf.log. On an RVM sytem, that’ll be in ~/.rvm/gems/[your gemset]/gems/nokogiri-1.5.5/ext/nokogiri/mkmf.log. Without RVM it’ll be wherever gem sticks unbuilt gems by default.
Posted in Uncategorized | No Comments »
June 21st, 2012
Cucumber makes heavy use of quotes to wrap data:
When I purchase "Oryx and Crake"
Then I should receive an email with the subject "Thank you for your purchase!"
I sometimes need to to programmatically generate these strings, when invoking steps-within-steps:
When /^I purchase "([^""]*)"$/ do |title|
step "Search for \"#{title}\""
...
end
Thankfully, Ruby has a metric shit ton of ways to quote strings. But what I haven’t seen mention of is the fact that you can just use the % operator with an arbitrary character to get a fully interpolated string:
# These are all equivalent:
step %~Search for "#{title}"~
step %(Search for "#{title}")
step %<Search for "#{title}">
# I doesn't matter which symbol you use. Just use whatever
# symbol is not present in your string.
All the references I’ve seen tell you to use %Q[whatever] for interpolated strings. The Q seems unnecessary.
But if you want an uninterpolated string you do still need to do %q?whatever?.
Tags: ruby
Posted in Uncategorized | No Comments »
June 1st, 2012
Just found out that you can inject an operator into a collection. For example:
[[1,2],[3,4]].inject([], :+)
#=> [1, 2, 3, 4]
You don’t even need the starting value. Rad.
Edit: If you leave out the starting value, injecting an empty array will return nil. Include an empty array as the first parameter if you want an empty array back in that case.
Previous trick: Using find and find_all on ActiveRecord collections
Tags: ruby
Posted in Uncategorized | No Comments »
May 22nd, 2012
I’ve been working on code that delays a bunch of work using Delayed Job’s :delay method. Stuff like this:
class Album
after_create :send_emails
def send_emails
recipients.each do |recipient|
recipient.delay.send_email
end
end
end
I tried a number of testing strategies:
- checking that the Delayed::Job.count changed
- testing for properties on Delayed::Job.last
- working off the jobs and testing that the work got done.
But they all seemed too indirect. In the end I decided to just write a custom matcher that lets me do this:
expect {
Album.create :recipients => [janelle_monáe]
}.to delay_method(janelle_monáe, :send_email)
Another reasonable route would be to mock :delay on the appropriate object, and then do a :should_receive on the mock. That only works if the object that’s getting the method delayed exists in your test scope. It won’t work if the object is created or looked up inside the method you’re testing…. unless you stub out the relevant :create and :find methods.
I don’t know. I’m still (3+ years in) learning how to decide how intensively to mock my unit tests. Some part of me thinks I’m just not using mocks enough. But the part that likes unit tests to be a little more flexible in the face of refactoring resists. We’ll see how it goes.
Use it like this:
# On a specific object:
apple = Apple.new
expect {...}.to delay_method(apple, :core)
# On any instance of a class:
expect {...}.to delay_method(Apple.any_instance, :core)
# On whatever object the code returns:
expect { Apple.create }.to delay_method(returned_object, :core)
And here’s the code:
# spec/support/delayed_job.rb
module RspecDelayedJobMatcher
class ReturnedObject
end
end
def returned_object
RspecDelayedJobMatcher::ReturnedObject
end
RSpec::Matchers.define :delay_method do |object, method_name|
def jobs
Delayed::Job.all.select do |job|
(payload = job.payload_object) && equivalent_object?(payload.object) && payload.method_name == @method_name
end.count
end
def equivalent_object?(payload)
if @object == returned_object
payload == @response
elsif @object.is_a? RSpec::Mocks::AnyInstance::Recorder
payload.is_a? @object.instance_variable_get("@klass")
else
payload == @object
end
end
def description
if @object == returned_object
"the returned object (#{@response.inspect})"
elsif @object.is_a? RSpec::Mocks::AnyInstance::Recorder
"some instance of #{@object.instance_variable_get("@klass").to_s}"
else
@object.inspect
end
end
match do |proc|
@method_name = method_name
@object = object
count_before = jobs
@response = proc.call
(@count = jobs - count_before) == 1
end
failure_message_for_should do |actual|
"Expected block to create a job to invoke #{@method_name} on #{description}, but it created #{@count}"
end
failure_message_for_should_not do |actual|
"Block created #{@count} job(s) to invoke #{@method_name} on #{description}, but expected none"
end
description do
"expect block to delay #{@method_name} on a #{@object.class.to_s}"
end
end
Tags: delayed-job, rspec
Posted in Uncategorized | 2 Comments »
May 18th, 2012
Lately I’ve been using pretty heavily an extension I wrote to Ruby’s Object class:
# lib/core_extensions/object.rb
class Object
def defined_and(key=nil)
return nil if nil?
block_given? ? yield(self) : self[key]
end
end
This lets me invoke methods on objects that may or may not exist. It’s really useful for the frequent situations where an optional child object has a method that also applies to it’s parent:
class ZipCode
def start_for(vegetable)
planting_specs.for(vegetable).defined_and &:start
end
end
As opposed to the ugly alternative:
def start_for(vegetable)
spec = planting_specs.for(vegetable)
spec.start if spec
end
That will either return a start, or it’ll return nil if no planting_spec was found. It works with a block (&:start is a shortcut for {|x| x.start}) or an enumerable key:
{ :a => 1}.defined_and(:a) #=> 1
nil.defined_and(:a) #=> nil
To use stuff in the lib/core_extensions folder in your Rails app, as defined above, you need an initializer:
# config/initializers/core_extensions.rb
Dir[Rails.root.join("lib/core_extensions/*")].each {|f| require f}
Tags: rails, ruby
Posted in Uncategorized | No Comments »
May 15th, 2012
I keep missing this in Google somehow, but if you want ActiveRecord to explain your queries in the console, do:
ActiveRecord::Base.logger = Logger.new(STDOUT)
via shime on StackOverflow
Tags: activerecord, rails
Posted in Uncategorized | No Comments »
May 11th, 2012
As mentioned, if you have a loop that allocates a bunch of memory tied to an object, destroying the current object after each iteration can help save on memory. I found myself reusing this pattern, so I just extended Array:
class Array
def destructive_each
while item = shift
yield(item)
end
end
end
It’s called “destructive” because after using it your array will be empty.
Tags: ruby
Posted in Uncategorized | No Comments »
March 23rd, 2012
In cucumber, you often want to be able to use a step in both it’s positive and negative form. For example, in different universes I might want to ensure that “pigs can fly”, or that “pigs can not fly”. Typically I’ve either written two step matchers for this, or added an awkward conditional:
Then /^pigs can (not |)fly$/ do |negated|
if negated.blank?
Pig.new.should have_the_power_of_flight
else
Pig.new.should_not have_the_power_of_flight
end
end
But today I decided to factor that out into a little helper method in my cucumber config:
# features/support/env.rb
class Object
def should_unless(missing, matcher)
missing.match(/not/) ? should_not(matcher) : should(matcher)
end
end
This allows me to write my matcher much more simply:
Then /^pigs can (not |)fly$/ do |negated|
Pig.new.should_unless negated, have_the_power_of_flight
end
So fresh and so DRY!
Tags: cucumber
Posted in Uncategorized | No Comments »