I don't think I've ever seen this solution anywhere else (perhaps because it's obvious), but it seems to work well for us. You want to speed up your page loading by putting all the javascript at the end, no? However you don't always know ahead of time, in Rails, when you need to call certain functions, etc. So this is what we do:
In my partial template that is included in every layout,
In any template where we want to call some specific function in my numerous javascripts, we do as in the following example:
Gotchas:
===> Be sure to restrict this tactic to main page templates, and avoid putting it in partial templates unless you absolutely have to, where you know for a fact it won't be included multiple times on the page.
===> Also note that this code will NEVER be executed in an AJAX response, because it always comes with the footer, i.e. only with a full page load.
NB: this post triggered a code review where I realized we had way too much of this in partial templates.
In my partial template that is included in every layout,
views/shared/_footer.html.haml, we have the following, last:= yield :extra_javascript
In any template where we want to call some specific function in my numerous javascripts, we do as in the following example:
- content_for :extra_javascript do
:javascript
Dudada.Users.initializeEvents();
Dudada.ProductGroups.initializeEvents();
Dudada.Reviews.initializeForms();
//(etc.)
Gotchas:
===> Be sure to restrict this tactic to main page templates, and avoid putting it in partial templates unless you absolutely have to, where you know for a fact it won't be included multiple times on the page.
===> Also note that this code will NEVER be executed in an AJAX response, because it always comes with the footer, i.e. only with a full page load.
NB: this post triggered a code review where I realized we had way too much of this in partial templates.
I just wrote the_missing_spec this afternoon. It's a really, really basic missing RSpec test finder and generator Rakefile, hosted on Github. Posted here because I couldn't find something similar (okay, I only skimmed the first handful of links on a couple of web searches).
Never fully explained in Stack Overflow or adequately by RSpec or Rails documentation, aside from "index."
From routes.rb, noting that announcements are not routed outside of this block:
The syntactically correct way to test nested routes is this:
For checking that the route does what you want:
For checking that you can accomplish this action successfully or not:
What you CANNOT do is directly quote the output of rake routes, i.e. you cannot do the following:
This will raise ActionController::RoutingError. Why, I don't know, but the routing refuses to correctly interpret the explicit path in this case, despite the fact that the route test above works!
From routes.rb, noting that announcements are not routed outside of this block:
resources :users,:product_groups do
resources :announcements
end
The syntactically correct way to test nested routes is this:
For checking that the route does what you want:
it 'should route properly' do
pg = FactoryGirl.create(:product_group)
{:post =>"/product_groups/#{pg.id}/announcements",
:product_group_id => pg.id.to_s}.should route_to('announcements#create',
:product_group_id => pg.id.to_s)
end
For checking that you can accomplish this action successfully or not:
it 'fails if not logged in' do pg = FactoryGirl.create(:product_group) post :create, :product_group_id => pg.id, :format => :js response.status.should eql(401) end it 'creates a new announcement with good params' do user = FactoryGirl.create(:user) sign_in user post :create, :user_id => user.id, :announcement => { :content => Faker::Lorem.sentence }, :format => :js response.should be_success end
What you CANNOT do is directly quote the output of rake routes, i.e. you cannot do the following:
pg = FactoryGirl.create(:product_group)
post "/product_groups/#{pg.id}/announcements"
:product_group_id => pg.id,
:format => :js
response.status.should eql(401)
This will raise ActionController::RoutingError. Why, I don't know, but the routing refuses to correctly interpret the explicit path in this case, despite the fact that the route test above works!
This is my own fault and is a reminder to myself for the next time I have this problem.
As documented on their front page, which I should have read before upgrading :P, jquery-rails for Rails 3.1 automatically inserts
and
into your application.js.
I couldn't figure out why two of my extensions stopped working. I couldn't figure out why all of a sudden I was loading two copies of jQuery. Plus, I couldn't figure out who was asking for a version of jQuery I didn't want yet, and where it was getting it from - Chrome insisted it was localhost, but I did not see a copy lying around in my source tree.
This is what I did to (successfully) debug it:
First I blew everything away in #{Rails.root}/tmp.
In the Chrome developer window, I put a breakpoint at the beginning of both jquerys, and one after Datepicker is loaded. This is how I found out that the 2nd loaded jQuery was blowing away all my previously loaded jQuery extensions.
In my source tree I did
When I looked at the second file that was when I discovered that the offending version was being loaded from jquery-rails gem. Oh, huh:
So of course, I was getting the version I wanted from Google's CDN site, adding the extensions we were using, and then the require in application.js was "helpfully" overwriting jQuery namespace with the one from jquery-rails, thus blowing away all the extensions we were using. Lesson: always read release notes before you upgrade :/
The confusing and most misleading part to all this is this has been working for quite some time. We switched to jquery-rails/Rails 3.1 long ago and yet these extensions stopped working only last week, so I'm still not sure what triggered the failure.
Also: thanks to bundler 1.1's addition of the 'outdated' command, I intend to lock down all production versions of gems we are using so that we can mindfully choose to upgrade next time. No more "~>" for us.
Last note for use by search engines: One of the symptoms was, "nimbleLoader has no method 'showLoading'" .
As documented on their front page, which I should have read before upgrading :P, jquery-rails for Rails 3.1 automatically inserts
//= require jquery and
//=require jquery_ujs into your application.js.
I couldn't figure out why two of my extensions stopped working. I couldn't figure out why all of a sudden I was loading two copies of jQuery. Plus, I couldn't figure out who was asking for a version of jQuery I didn't want yet, and where it was getting it from - Chrome insisted it was localhost, but I did not see a copy lying around in my source tree.
This is what I did to (successfully) debug it:
First I blew everything away in #{Rails.root}/tmp.
In the Chrome developer window, I put a breakpoint at the beginning of both jquerys, and one after Datepicker is loaded. This is how I found out that the 2nd loaded jQuery was blowing away all my previously loaded jQuery extensions.
In my source tree I did
grep -R jquery\.js ./* and got the following:
Binary file ./tmp/cache/assets/D4E/1B0/sprockets%2Ff 7cbd26ba1d28d48de824f0e94586655 matches
Binary file ./tmp/cache/assets/D9D/A50/sprockets%2F1 abc6eced0f709e935bada8961f05750 matches
Binary file ./tmp/cache/assets/DC7/AF0/sprockets%2Fc ec0e140d887c53da709b8befad2315d matches
When I looked at the second file that was when I discovered that the offending version was being loaded from jquery-rails gem. Oh, huh:
^F;^@FI"^Vdependency_digest^F;^@F"%29738 eb6ce64d47e73f841bbdd9ecad8I"^Srequired_ paths^F;^@F[^FI"j/Users/squeedle/.rvm/ge ms/ruby-1.9.2-p318/gems/jquery-rails-2.0.2/v endor/assets/javascripts/jquery.js^F;^@F I"^Udependency_paths^F;^@F[^F{^HI" path^F;^@FI"j/Users/squeedle/.rvm/gems/r uby-1.9.2-p318/gems/jquery-rails-2.0.2/v endor/as sets/javascripts/jquery.js^F;^@FI"
9409 mtime^F;^@FI"^^2012-04-06T12:41:40-07:00^F; ^@FI"^Kdigest^F;^@F"%aeef0ce303adf226ff9 7e8c6608baf57I"^M_version^F;^@F"%aa7d0db 7619379e13b08335dee027df2
So of course, I was getting the version I wanted from Google's CDN site, adding the extensions we were using, and then the require in application.js was "helpfully" overwriting jQuery namespace with the one from jquery-rails, thus blowing away all the extensions we were using. Lesson: always read release notes before you upgrade :/
The confusing and most misleading part to all this is this has been working for quite some time. We switched to jquery-rails/Rails 3.1 long ago and yet these extensions stopped working only last week, so I'm still not sure what triggered the failure.
Also: thanks to bundler 1.1's addition of the 'outdated' command, I intend to lock down all production versions of gems we are using so that we can mindfully choose to upgrade next time. No more "~>" for us.
Last note for use by search engines: One of the symptoms was, "nimbleLoader has no method 'showLoading'" .
After upgrading from Snow Leopard to Lion, and getting
I looked at the relevant code in sunspot:
It seems that the upgrade install of Lion may have changed permissions on the file or something. A permissions error isn't caught by this area of code so it just barfs and doesn't tell you why.
Solution: Just remove all your old pid files in test and development:
unknown0026bb1de3ec:dudada-alt squeedle$ bundle exec rake sunspot:solr:start --trace
** Invoke sunspot:solr:start (first_time)
** Invoke environment (first_time)
** Execute environment
** Execute sunspot:solr:start
rake aborted!
Operation not permitted
/Users/squeedle/.rvm/gems/ruby-1.9.2-p31 8/gems/sunspot_solr-1.3.0/lib/sunspot/so lr/server.rb:61:in `kill'
/Users/squeedle/.rvm/gems/ruby-1.9.2-p31 8/gems/sunspot_solr-1.3.0/lib/sunspot/so lr/server.rb:61:in `start'
/Users/squeedle/.rvm/gems/ruby-1.9.2-p31 8/gems/sunspot_solr-1.3.0/lib/sunspot/so lr/tasks.rb:12:in `block (3 levels) in '
/Users/squeedle/.rvm/gems/ruby-1.9.2-p31 8/gems/rake-0.9.2.2/lib/rake/task.rb:205:i n `call'
/Users/squeedle/.rvm/gems/ruby-1.9.2-p31 8/gems/rake-0.9.2.2/lib/rake/task.rb:205:i n `block in execute'
/Users/squeedle/.rvm/gems/ruby-1.9.2-p31 8/gems/rake-0.9.2.2/lib/rake/task.rb:200:i n `each'
/Users/squeedle/.rvm/gems/ruby-1.9.2-p31 8/gems/rake-0.9.2.2/lib/rake/task.rb:200:i n `execute'
/Users/squeedle/.rvm/gems/ruby-1.9.2-p31 8/gems/rake-0.9.2.2/lib/rake/task.rb:158:i n `block in invoke_with_call_chain'
I looked at the relevant code in sunspot:
begin
Process.kill(0, existing_pid)
raise(AlreadyRunningError, "Server is already running with PID #{existing_pid}")
rescue Errno::ESRCH
It seems that the upgrade install of Lion may have changed permissions on the file or something. A permissions error isn't caught by this area of code so it just barfs and doesn't tell you why.
Solution: Just remove all your old pid files in test and development:
rm solr/pids/development/sunspot-solr-devel opment.pid
- Current Location:San Francisco, CA
- Current Music:Bebop
ActionView::Template::Error (PG::Error: ERROR: for SELECT DISTINCT, ORDER BY expressions must appear in select list
: SELECT DISTINCT "reviews".id FROM "reviews" WHERE "reviews"."reviewable_id" = 1 AND "reviews"."reviewable_type" = 'User' ORDER BY created_at DESC):
1: =link_to "+", url_for( :controller => 'reviews', :action => 'index', :user_id => @user.id ), :id=>'tabz-plus'
2: .tabz
3: =link_to t('dudada.review.reviews_link') + " (#{@user.review_ids.count})", :controller => 'reviews', :action => 'index', :user_id => @user.id
4: .reviews_arrow_s
app/views/users/_reviews_about_user.html.h aml:3:in `_app_views_users__reviews_about_user_ht ml_haml___1211197703835889122_2520414200'Found this today. I changed line 3 to use
user.reviews.count and the error went away. This worked before the upgrade, and magically broke afterward. Hope this helps.Ok this one took me an hour to figure out. In RSpec 2 they change the way you should write view specs. I was writing a spec for 'users/show.html.haml," I followed the short examples here and this is what I would get:
What? I knew darn well there was indeed a route because it's fine in the browser. I finally made a guess, that since the view test was using routes, somehow the test needed the id request parameter and wasn't getting it. Frankly I'm not sure why the controller is involved at all; I thought this was purely a render test and all I had to do was provide values for all the variables inside my chosen template. But no. I did this:
And that fixed it. Yep. Commenting it out makes the error return. Says right there in the documentation that you shouldn't have to provide these things. Nobody else seemed to have the same problem, so here's the post.
No route matches {:controller=>"users", :action=>"show"}
What? I knew darn well there was indeed a route because it's fine in the browser. I finally made a guess, that since the view test was using routes, somehow the test needed the id request parameter and wasn't getting it. Frankly I'm not sure why the controller is involved at all; I thought this was purely a render test and all I had to do was provide values for all the variables inside my chosen template. But no. I did this:
controller.request.path_parameters[:id] = @john.id
And that fixed it. Yep. Commenting it out makes the error return. Says right there in the documentation that you shouldn't have to provide these things. Nobody else seemed to have the same problem, so here's the post.
- Current Mood:annoyed
Now that I've written my make-up post I can write about what I'm doing now. We're still kind of in stealth mode so I can't really point you to anything. I'm hoping for an open beta around February. Hope is not a strategy, so yes, we have some actual plans.
First, tools. I've been wanting to get Cucumber testing going for some time. This is what I've been working on this week in addition to shoring up our current features. Cucumber has given me a few problems but I'm still a fan. I am seeing a lot of table lock problems and timeouts when running Cucumber and I'm not sure why. They're intermittent, and Cucumber test output does not always pinpoint the source of problems.
While the quantity of documentation for Cucumber is great, the quality is not. It's hard for me to find things, and I usually end up getting my information from people's examples on other websites. The explanations tend to be at a programmatic level and don't explain well the process of writing a feature and running a test from start to finish.
There are also features of Cucumber that I felt like I had to discover through trial and error. For example, if you write a "Before" hook with no tag, Cucumber will load it and run it before every test, no matter what file it's in.
Also, I got sick of trying to help Dušan deal with Aptana/Eclipse issues on Ubuntu (entirely not his fault). The last straw was when the return key suddenly stopped working, but only in .rb and .haml editors. So early this week, I found RubyMine. I can't say enough how pleased I am with it. I'll just paste my post from elsewhere:
First, tools. I've been wanting to get Cucumber testing going for some time. This is what I've been working on this week in addition to shoring up our current features. Cucumber has given me a few problems but I'm still a fan. I am seeing a lot of table lock problems and timeouts when running Cucumber and I'm not sure why. They're intermittent, and Cucumber test output does not always pinpoint the source of problems.
While the quantity of documentation for Cucumber is great, the quality is not. It's hard for me to find things, and I usually end up getting my information from people's examples on other websites. The explanations tend to be at a programmatic level and don't explain well the process of writing a feature and running a test from start to finish.
There are also features of Cucumber that I felt like I had to discover through trial and error. For example, if you write a "Before" hook with no tag, Cucumber will load it and run it before every test, no matter what file it's in.
Also, I got sick of trying to help Dušan deal with Aptana/Eclipse issues on Ubuntu (entirely not his fault). The last straw was when the return key suddenly stopped working, but only in .rb and .haml editors. So early this week, I found RubyMine. I can't say enough how pleased I am with it. I'll just paste my post from elsewhere:
Once I got past the apparently common "3 pink error messages about can't find the ruby gem blah blah blah" problem (hint: gem update --system) I was very pleased. Real code completion, actual refactoring, it really indexes all the ruby/rails source you have referenced, so the declaration keyboard shortcut actually takes you to the declaration instead of just looking at you stupidly.
And things like this:
oh hay, seems like you need some gems installed that aren't there. how 'bout I do it for you? Y/N
oh hay, seems like you need to install ruby-debug-ide19, I'll just do that right now, ok? Y/N
Translations are automatically inserted for you in your views and helpers, when they exist, so that when they don't exist it's obvious, and when you start typing t('...'), it shows you possible completions from your translation files.
And full featured debugging, thank god - watch expressions actually work, you can set breakpoints for exceptions, and subversion support and Git support are installed by default.
I hope I'm as satisfied once the shiny wears off. Will let you know.
Background: awhile ago, the Mu back end/protocol team realized that MongoDB was not working for us due to the way it does updates, because of the needs of our complex, memory- and CPU-intensive testing algorithms. We had already abandoned PostgreSQL after several years of using it, for other reasons (e.g. problems with hibernate). We wanted to move everything to using the same database, including our web front end, in part so we wouldn't have to support more than one db, and so everyone learned the same thing. One reason for choosing CouchDB was for its powerful replication; we could move resources easily from the front end to the back end, from user accounts on the web app to be used in our sophisticated App-aware testing.*
Even in our web app, we had run into some intractable problems using MongoDB and a couple of the gems I chose for use with Devise, particularly once we upgraded to Rails 3.
The first lesson I got from this migration was, I had far underestimated how different CouchDB is from MongoDB. I figured it couldn't be too hard of a transition because they are both document-based databases, but I hadn't recognized how dependent our web app was on the relational-like features of MongoDB. Advanced views are the replacement for advanced queries and there is no such thing as a table join. Further, if you are not facile with map-reduce than you had better get working on it.
Also contributing to the underestimation of time and effort was the fact that Rails and Ruby support for CouchDB is far and away less mature than that for SQL databases, and this means in real terms that the gems you'll be usingmay probably have some major, glaring bugs that you will not discover until you've already built them into your app. For example, the mongodb interface gem I chose provides a sensible clone replacement. It was a set-and-forget moment, because months down the road I assumed (we know what happens when we assume) that the couch gem we picked would, too. Well, it didn't. As a result any time somebody copied a test resource into their own space, they essentially took it away from someone else. Glad we caught that one pretty early. I was only very embarrassed, not exceptionally embarrassed and job hunting. Other fun bugs included bizarre delete behavior and failure to actually paginate.
This may simply be that I don't know couch and map-reduce well enough to figure out how to do everything, but I found CouchDB incredibly lacking once we got into advanced queries, particularly multi-column, I mean multi-property sorting and selection. In one case we had to denormalize our data - make sure that when permissions were changed for documents with one 'type' (formerly known as a table) then the same change had to be made for a different 'type' (which had formerly been in a one-to-many relationship). This was required because of our multi-tiered permissions structure which I won't detail.
If I had this to do over again, I would move the objects that are used only in the web app to a SQL db, and use CouchDB only for the assets we needed to replicate and use in our testing services.
I suppose you might be thinking that if only I had had proper unit test coverage, many of these problems would have been found right away. You would be correct, but I counter that 1) one can't anticipate all problems 2) there is only so much time in the day and 3) there is no such thing as an ideal world. That's why it's called "IDEA-l." So plan for the unexpected and never assume anything will be easy.
* Yes, I am plugging my former company! Mu Dynamics has some really awesome tools and the interface alone for the App-aware stuff is really slick. Also check out blitz.io. No, it is not just because of my stock options! :P
** Note that as of July I'm not working for Mu Dynamics any more, so all of this is from memory. Any of my former cohorts are welcome to correct my reckoning and I'll amend the post and/or unscreen your comments.
Even in our web app, we had run into some intractable problems using MongoDB and a couple of the gems I chose for use with Devise, particularly once we upgraded to Rails 3.
The first lesson I got from this migration was, I had far underestimated how different CouchDB is from MongoDB. I figured it couldn't be too hard of a transition because they are both document-based databases, but I hadn't recognized how dependent our web app was on the relational-like features of MongoDB. Advanced views are the replacement for advanced queries and there is no such thing as a table join. Further, if you are not facile with map-reduce than you had better get working on it.
Also contributing to the underestimation of time and effort was the fact that Rails and Ruby support for CouchDB is far and away less mature than that for SQL databases, and this means in real terms that the gems you'll be using
This may simply be that I don't know couch and map-reduce well enough to figure out how to do everything, but I found CouchDB incredibly lacking once we got into advanced queries, particularly multi-column, I mean multi-property sorting and selection. In one case we had to denormalize our data - make sure that when permissions were changed for documents with one 'type' (formerly known as a table) then the same change had to be made for a different 'type' (which had formerly been in a one-to-many relationship). This was required because of our multi-tiered permissions structure which I won't detail.
If I had this to do over again, I would move the objects that are used only in the web app to a SQL db, and use CouchDB only for the assets we needed to replicate and use in our testing services.
I suppose you might be thinking that if only I had had proper unit test coverage, many of these problems would have been found right away. You would be correct, but I counter that 1) one can't anticipate all problems 2) there is only so much time in the day and 3) there is no such thing as an ideal world. That's why it's called "IDEA-l." So plan for the unexpected and never assume anything will be easy.
* Yes, I am plugging my former company! Mu Dynamics has some really awesome tools and the interface alone for the App-aware stuff is really slick. Also check out blitz.io. No, it is not just because of my stock options! :P
** Note that as of July I'm not working for Mu Dynamics any more, so all of this is from memory. Any of my former cohorts are welcome to correct my reckoning and I'll amend the post and/or unscreen your comments.