Showing posts with label gotchas. Show all posts
Showing posts with label gotchas. Show all posts

Sunday, 2 November 2014

gotcha: running `rake db:migrate:redo` and testing

So... rails *tells* me that rake db:test:prepare is deprecated, and that just running the specs will keep the db up-to-date... but it isn't always so.

If you jigger with a brand new migration and run a rake db:migate:redo - the test db can't deal with that by itself. You either have to run rake db:migate:redo RAILS_ENV=test or rake db:test:prepare to propagate those changes.

Friday, 12 September 2014

Gotchas: rspec expect change gives: "nil is not a symbol"

*sigh* There's a small, but meaningful difference between:

   expect {
     my_thing.do_stuff
   }.to change(my_thing.my_value).to(some_value)

and

   expect {
     my_thing.do_stuff
   }.to change{my_thing.my_value}.to(some_value)

(note the braces on the last line...)

Saturday, 6 September 2014

gotchas: require/permitting an array of ids

This one got me the other day... it seems that sometimes the following won't work:

params.require(:my_thing).permit(:widget_ids)

When the set of ids is expected to be an array, instead use

params.require(:my_thing).permit(:widget_ids => [])

Friday, 2 November 2012

ruby gotcha: beware trailing commas

I had something like the following (though with more values):

 h = OrderedHash.new()
 h[:a] = {:a => 1}
 h[:b] = {:b => 2}, # note the trailing comma - oops
 h[:c] = {:c => 3}

and started getting errors telling me that h[:b] was an array, when I was expecting it to be a hash...

Here's what's actually happening (via irb):

>> a = {:a => 1}
=> {:a=>1}
>> b = {:b => 2},
?> c = {:c => 3}         # still part of the previous statement
=> [{:a=>1}, {:c=>3}] # so we end up with an array

Note to self: beware trailing commas

Saturday, 23 April 2011

Gotcha: NoMethodError: undefined method RuntimeError

So... I wrote some code in a plain-ruby class that raised a RuntimeError somewhat like below:

    class MyClass
      def initialize(opts = {})
         # do stuff
         thing = opts[:thing]
         raise RuntimeError "must have a thing!" unless thing.present? && thing.is_a?(Thing)
         # more stuff
      end
    end

and when I ran my fresh new rspec spec over it -> which looks somewhat like:

    it "should raise an error if we don't pass a thing" do
      lambda {
        my_class = MyClass.new(:thing => nil)
      }.should raise_exception(RuntimeError)
    end

I kept getting something weird:

    expected RuntimeError, got 
    #<NoMethodError: undefined method `RuntimeError' for #<MyClass:0xb5acbf9c>>

You may already have spotted the problem... ah, single-character bugs, doncha love em?

Here it is.

WRONG:

     raise RuntimeError "must have a thing!" unless thing.present? && thing.is_a?(Thing)

RIGHT:

     raise RuntimeError, "must have a thing!" unless thing.present? && thing.is_a?(Thing)

of course, you can also just go ahead and leave out the RuntimeError entirely:

     raise "must have a thing!" unless thing.present? && thing.is_a?(Thing)

because it's the default anyways... which makes it nice and defaultish for you.

Wednesday, 2 March 2011

Gotcha: migrations with trailing commas

Trailing commas are bad m'kay?

I had a pretty standard migration, which included something like the following two lines:

    create_table :my_widgets do |t|
      t.string :name, :colour, :flavour
      t.integer :supplier_id
      t.integer :height, :width, :depth,    # note this extra comma...

      t.timestamps
    end

Looked fine at a casual glance, ran just fine - no hiccups. Been using it on dev for a week or so now... no problems... Only noticed an issue when I glanced at db/schema.rb

  create_table "service_report_stats", :force => true do |t|
    t.string   "name"
    t.string   "colour"
    t.string   "flavour"
    t.integer  "supplier_id"
    t.integer  "height"
    t.integer  "width"
    t.integer  "depth"
    t.datetime "created_at"
    t.datetime "updated_at"
    t.integer  "#<ActiveRecord::ConnectionAdapters::TableDefinition:0xb5c35504>" # WTF???
  end

Something seriously wrong with that last "integer" column... mysql shows that yes, that is actually the column's real name:

+-----------------------------------------------------------------+----------+------+-----+---------+----------------+
| Field                                                           | Type     | Null | Key | Default | Extra          |
+-----------------------------------------------------------------+----------+------+-----+---------+----------------+
| id                                                              | int(11)  | NO   | PRI | NULL    | auto_increment |
| supplier_id                                                     | int(11)  | YES  |     | NULL    |                |
.... etc ...
| created_at                                                      | datetime | YES  |     | NULL    |                |
| updated_at                                                      | datetime | YES  |     | NULL    |                |
| #<ActiveRecord::ConnectionAdapters::TableDefinition:0xb5c35504> | int(11)  | YES  |     | NULL    |                |
+-----------------------------------------------------------------+----------+------+-----+---------+----------------+

Right now I have NFI how to actually remove the column... because trying:
alter table my_widgets drop column #<ActiveRecord::ConnectionAdapters::TableDefinition:0xb5c35504>;
Just results in: ERROR 1064 (42000): You have an error in your SQL syntax; , and it won't accept it if I put it in quotes either... so the column currently remains as is.

Luckily I was playing with a branch of the real system - so it's only my dev-box's code that has been mucked about... and I can change the migration before anybody else uses it... but this is seriously weird behaviour on Rails' part. Not a raised exception or even a raised eyebrow... it sailed right through and is continuing on regardless.

To actually fix it, I had to create a migration with the following:

    remove_column :my_widgets,  "#<ActiveRecord::ConnectionAdapters::TableDefinition:0xb5c35504>"

Seems the Rails itself can tell mysql about a weirdly-named column somehow. I'd still like to know the actual SQL involved...

Sunday, 6 February 2011

gotcha: Unpacked gem in vendor/gems has no specification file.

Ok, this one had me stumped for a little while. When I was running rake, I'd get these warnings:

  config.gem: Unpacked gem difftmp in vendor/gems has no specification file. Run 'rake gems:refresh_specs' to fix this.
  config.gem: Unpacked gem difftmp in vendor/gems not in a versioned directory. Giving up.

I'd seen this one before - when I'd thoughtlessly unpacked a gem into vendor/gems, then patched it. But for some reason rails doesn't like it when you unpack gems without using rake gems:unpack. I've never been in the habit of using that, because I didn't want to unpack everything. Later, of course I discovered that you can pass "GEM=blah" and it'll only unpack a single gem... but I thought I had done I this time around...

So I blindly followed the instructions to run rake gems:refresh_specs

However, that then spewed on me with an equally-familiar exception message:

rake aborted!
undefined method `installed_source_index' for #<Gem::SourceIndex:0xb76aa218>
... much stacktrace garbage follows...

I sighed deeply and googled the exception... to get the usual suspects, which suggest that I need to refresh the specification file. So I went back to the original error message to see which actual gem was causing the problem.

... then stopped.

You see it became pretty clear that I'd never actually *read* the original error message. "difftmp" is obviously a temporary diff-file showing me the patch I'd made on one of the gems. Now, you could argue that rake shouldn't be stupid and assume that a flat text file is a gem (requiring a specification)... but it was then also quite clear what to do to fix the "bug".

rm vendor/gems/difftmp

Problem solved

Wednesday, 19 January 2011

Rails namespacing test gotchas

We have some controllers that are namespaced as Admin controllers eg: Admin::ProductController.

...and this is a legacy system, and never had any functional tests created for those...

So I was trying to get started on adding some functional tests. I created a test/functional/admin folder, then added a product_controller_test.rb under that, with the usual:

class ProductController < ActionController::TestCase

At first, the tests wouldn't even load properly, and I was getting the following error:

uninitialized constant ActionController (NameError)

That, apparently, is solved by explicitly putting:

require 'test_helper'

at the top of the file. After which it loads fine, and the null-test runs. but after that, I kept getting the following error:

RuntimeError: @controller is nil: make sure you set it in your test's setup method

It seems that you need to namespace your tests too, so instead of the class declaration I have above, you need:

class Admin::ProductController < ActionController::TestCase

The admin folder isn't enough.

Friday, 24 December 2010

Gotcha: db index name must be a string

Might just be Rails 2.3.8 - an exception is thrown whenever I use a symbol for an index name. I get:

rake aborted!
An error has occurred, all later migrations canceled:
undefined method `length' for :my_index_name

The code causing this would be something like:

add_index :widgets, [:colour, :weight], :name => :index_by_colour_weight

it’s solved by just using a string instead:

add_index :widgets, [:colour, :weight], :name => 'index_by_colour_weight'

Tuesday, 30 November 2010

Gotcha: heroku with git+svn pushes your working copy

For the MatchFounders website, I've been working with svn on Heroku using git just as a deployment tool... and I came across a bit of a gotcha the other day.

git commit will add your *working copy* to your next deployment.

Luckily it didn't turn out too badly for us - I was just mucking about with some pagination links... and quickly fixed the bug I'd been working on before deploying again... but if I'd been right in the middle of some big hairy code refactor, or something... I'd have had to go find heroku's docs on rollbacks pretty darn quick.

So - just be aware that if you use this method, to add a step to your process-control to make sure you only deploy from a 'clean' working copy.

Thursday, 9 September 2010

gotchas: gem cleanup

Note to future self: do not type "gem cleanup"...

...now reinstalling rails v 2.3.4, 2.3.5, 2.3.8... etc

Friday, 11 December 2009

Getting webistrano to deploy under passenger

What's your problem?

I'd been playing with webistrano on my own machine and using it to make deployments to staging - and that was all ticking along nicely. But then it was time to put webistrano up on our staging server - so as to let our sysadmins use it for deployments.

Downloading and installing it is very easy. Configuring it is a little more annoying, but once it was full of all the hosts/systems/rstages/roles and config_options from my existing setup iit shouldn't need any more updating.

Then I tried to deploy. At which point it promptly all fell over.

The same thing happened over and over. I'd click deploy and it'd start. The "running" spinner would spin... forever, the little ajax refresh constantly refreshing the Log... with nothing.
The deploy didn't actually deploy, and what's worse - didn't ever *stop*[1].

I did a deploy and watched top and a ruby process did appear briefly and then disappear - but no ruby process ever showed up in ps...
Not happy Jan :(

I couldn't even cancel the deploy because the cancel button only ever appears once the deployment is fully underway[2]. To deploy again, I had to break locks and do other unsavoury things and that felt really wrong.

So, what was happening and how did we eventually fix it?

Well, the suspicious thing to me was that nothing showed up in the log-section at all. Not even the "loading stage recipe X" that is the very first line of all the successful deploys on my own system.

Thus I figured on a permissions problem. I checked that the runner-user had access to svn, and to the staging server. This was interesting as staging was deploying on itself, we checked that it could ssh into itself happily... and sure enough it asked me to check the 'authenticity of the host' I was connecting to. Now webistrano is notorious for hanging on an un-expected question, so I figured I'd just have to add this to known_hosts and all would be well.

It probably was something that helped... but the deploys were still failing to spin up.

So I dug into the log and found it was chock full of the AJAX Deployment#show refreshes (they're so frequent!) But I eventually got back to the initial Deployment#create which is what should kick off the real deployment. The log for this shows that it goes along fine until right at the bottom, almost completely hidden amongst the noise is one line that rang alarm bells:
sh: ruby: command not found

So I checked permissions again, checked to be sure that ruby was installed, that we could see it in the $PATH as the deployment user, all those things.
I even did a capfile export and ran it with pure capistrano to make sure - and that worked just fine! So now I was really confused.

Finally digging into the webistrano application code, I discovered that the important LOC is in app/models/deployment.rb under def deploy_in_background. It's of the form: system("sh -c \"cd #{RAILS_ROOT} && ruby script/runner -e... etc etc. So I tried this on the command line. ruby -v worked for the deployment user.

I spun up script/console and tried system("sh -c \"ruby -v\"")
and that spat out the correct version and 'true'... so obviously rails could find ruby ok, but running in during deployment was still not happy

Then I copied the above code into the application layout... and it returned false instead of true. Something was happening when inside the running app that wasn't running from the command-line.

Then I discovered this blogpost claiming they also had the log message: sh: ruby command not found

So it seems that when the app is running - by default you're actually not inside a shell - so it's not loading your settings (such as $PATH) and thus not going to find important things (like ruby).

The solution?

Instead of sh -c we need it run under bash --login -c

This will force the process to load up your bash environment settings. The bad news is that you have to monkey-patch webistrano to get it working[3].

Given webistrano is a rails app, this isn't difficult - just annoying. There's only one spot that you need to change. That's the aforementioned deploy_in_background method. Change it there and touch tmp/restart.txt and your deployments should at least spin up now.

anything more?

There is still problem if your recipes also require some $PATH goodness. For example if you are running 'gem bundle' your shell will need to find gem... which means that the recipes need to run under bash too. Now it's a little easier to convince webistrano to do that.

You can override the shell used here by supplying a configuration option: default_shell to bash --login


Note: it's the --login that gets it to do what you want!

Also - don't forget that if you call sh -c explicitly in your recipes you'll need to change it there too.

Notes for webistrano devs:

[1]You should probably surround the deploy process in a timeout.
[2] The cancel button should appear immediately.
[3] It'd be nice if we could configure the shell-command under which webistrano runs

Tuesday, 28 July 2009

rails gotchas: undefined method `expects'

If you've just installed rails edge and run the tests, they may fail with: NoMethodError: undefined method `expects' for ... etc etc over and over again (along with a few other errors).

Install the mocha gem via rubygems to get rid of a lot of these errors.

It was the line NoMethodError: undefined method `stub' for ... that clued me in.

It seems that rails requires a short list of gems/packages (beyond rails itself) simply to run its own tests... yet there is no rake gem:install task available to help you figure out which ones... and they aren't in the "contributing to rails" guide. I'll be submitting a patch shortly...

Following on from this I got:

MissingSourceFile: no such file to load -- openssl

and farther down the list:

LoadError: no such file to load -- net/https

Unfortunately, these errors occur when Ruby hasn't been installed with openssl-support. If you're running ubuntu, you can just run apt-get install libopenssl-ruby.

Wednesday, 15 July 2009

Gotcha: UTC vs Local TimeZones with ActiveResource

So... your database is filled with datetime data and it's all configured to localtime, not UTC... We also have this you-beat nifty ability to set all our datetime-handling functionality to a given timezone by setting, say: config.time_zone = 'London' in config/environments.rb... or do we?

If you also use ActiveResource (or the new, actually-working HyperactiveResource), you'll find that suddenly you're getting a UTC-local timezone issue once more.

The problem is that the xml that comes back from a remote API is converted into a Date/DateTime using the core-extension to the Hash.from_xml method... which has the following LOC:

"datetime"     => Proc.new  { |time|    ::Time.parse(time).utc rescue ::DateTime.parse(time).utc }

The fix

You need to do two things. Firstly. Hack that line[1] and replace it with:

"datetime"     => Proc.new  { |time|    ::Time.zone.parse(time) rescue ::DateTime.zone.parse(time) },

Secondly... somehow it doesn't pick up the timezone even though it's been helpfully added in via the config... so you need to open up config/environments.rb (or create a rails initializer) and put:
Time.zone = 'London'[2]
in there (outside the rails initialization block).

Notes:
[1]To hack rails, you can either
a) hack on your own rails gem = risky... will be overwritten the next time you sudo gem update or
b) rake rails:freeze:edge - which means you have your rails in your own vendor/rails directory... but means you have to rake rails:update manually... up to you which you hate more.

[2]Obviously substituting your own timeZone as appropriate here. See the TimeZone doc for what you can pass in.

Thursday, 21 May 2009

Testing gotchas: nested logins don't work

*sigh* after a couple of hours futile hacking about I finally realised that my controller tests were failing because login_as cannot be nested. ie. if you have a context with a login_as @user1 in the setup... then put a context inside it where you login_as @user2... it won't actually do the second login.

So just move that nested context outside. Simples neh?

Thursday, 9 April 2009

Rails gotchas: shoulda not_allow_values_for

If you're using should_not_allow_values_for and getting a failing test something in the lines of:

Failure:
test: User should not allow email to be set to "b lah". (UserTest)
    [/usr/lib/ruby/gems/1.8/gems/thoughtbot-shoulda-2.10.1/lib/shoulda/assertions.rb:56:in `assert_rejects'
     /usr/lib/ruby/gems/1.8/gems/thoughtbot-shoulda-2.10.1/lib/shoulda/active_record/macros.rb:174:in `__bind_1239266378_671612'
     /usr/lib/ruby/gems/1.8/gems/thoughtbot-shoulda-2.10.1/lib/shoulda/context.rb:253:in `call'
     /usr/lib/ruby/gems/1.8/gems/thoughtbot-shoulda-2.10.1/lib/shoulda/context.rb:253:in `test: User should not allow email to be set to "b lah". ']:
Expected errors to include "is invalid" when email is set to "b lah", got errors: email is too short (minimum is 6 characters) ("b lah")email should look like an email address. ("b lah")

You can see in the above test that the email does have an error on it - but the error message is not the default. shoulda specifically checks for the error message - and if you don't have the default, then you need to pass it in thus:

should_not_allow_values_for :email, "b lah", :message => Authentication.bad_email_message

rails gotchas: assert_raises a syntax error

I found that assert_raise was causing a syntax error of the form:
syntax error, unexpected '{', expecting kEND (SyntaxError)
For a fairly simple test:

should "remove it from the database" do
  assert_raise ActiveRecord::RecordNotFound { User.find(@uid)}
end

Looks like it's getting confused about nesting. the solution? parenthesise your Error thus:

should "remove it from the database" do
  assert_raise(ActiveRecord::RecordNotFound) { User.find(@uid)}
end

Monday, 6 April 2009

rails gotchas: HttpMock not enough variables

A quickie for my own remembrance. I'm currently setting up HttpMock to test ActiveResource. I'd set up a few extra "routes" for it to mock out and kept getting the error below:

NoMethodError: undefined method `size' for :not_found: Symbol

For the code line:

mock.get    "/users/#{uid}.xml", {}, :not_found

The fix was pretty simple - I'd just accidentally missed out the "nil" for the body - ie it was actually an ArgumentError - but not getting picked up. ie, the code should have been:

mock.get    "/users/#{uid}.xml", {}, nil, :not_found

Thursday, 2 April 2009

git gotchas: the wrong forking repository

So I'm pretty new to git, really, and am learning all the places that I can stumble and fall head-over-arse. Today's escapade is entitled "how to recover from forking the wrong repository in github". It's quite a simple recovery, and you'd think it'd be obvious how to recover from it... you'd also think that github would have a "how to" in the main help page...

This solution is based on having just made the mistake and wanting to just delete it and start over straight away. Obviously this doesn't work if, say, you've started making changes and want to keep them - you're on your own there.

The solution... delete the repository and start again

  1. Go to your version of the repository on github
  2. Choose the "Admin" tab (from the list at the top)
  3. Right down the bottom of the page is a section labelled "Administration". At the bottom of the box is a small, blue link labelled "Delete this repository".
  4. Click that link - then click "Yes" a few times to convince github that you really are sure.

Now you're done. The repository may not be deleted for a few minutes (it's a background process) and your dashboard may be cached so it may appear for a little while longer. Once it's gone you can go back and pick the correct fork of the repository that you want.

Wednesday, 29 October 2008

Rails a little over-protective?

Was busy developing and minding my own business, when suddenly my functional tests all stopped working giving me this error:

ActionController::InvalidAuthenticityToken: No :secret given to the #protect_from_forgery call. Set that or use a session store capable of generating its own keys (Cookie Session Store).

Now we happen to have just switched on the use of the cookie store - and the actions all work just fine from the browser. Checking the forms even shows up the authenticity tokens etc... this only occurs during testing.

Googling gives a few rspec-based solutions, but very few are even possible for Test::Unit. I tried disabling config.action_controller.allow_forgery_protection - but that did a big nothing for me. So I bodgied up a quick-n-dirty workaround just for the test environment as per below.

Right now I still don't know what's causing the bug, but this gets me past it until I can figure out what's actually wrong. YMMV :)

  # See ActionController::RequestForgeryProtection for details
  if RAILS_ENV == 'test'
    # dodgy workaround for Rails 2.0 bug in functional tests - which don't
    # seem to use a cookie store properly. Reference to issue:
    # http://groups.google.com/group/railsspace/browse_thread/thread/fcdbfa4e65bf86de
    protect_from_forgery :secret => 'c1c6ebaee01fecc9aa9bc105d235b2c2'
  else
    # Uncomment the :secret if you're not using the cookie session store
    protect_from_forgery # :secret => 'c1c6ebaee01fecc9aa9bc105d235b2c2'
  end