Subselector, Moneypenny November 1, 2008
Posted by reidmix in ActiveRecord, Code, Database, Example, Monkey Patch, Plugins & Gems, Rails.Tags: bind variables, named bind variables, Rails, select, sql, subselect
2 comments
Building on Josh and Damon’s idea of Hacking a Subselect in ActiveRecord, I wondered if you could bake this kind of functionality into ActiveRecord. So Doug and I went digging into the rails code, and came up with a plugin that adds subselects to ActiveRecord which we call Subselector.
So far, it only works on the Hash version of conditions.
On a column you wish to perform a subselect, pass a hash with :in, :not_in, :equals, or :not_equals as the only key. The value is any of the options you normally would pass to ActiveRecord find. Notice that we make sure to select a single column with the :select option:
Critic.find(:all, :conditions => { :id => {:in => {:select => :id, :conditions => {:active => true} } } })
Although the example may be contrived, here, we are looking for a critics that are in a set of active critics. The SQL:
select * from critics where id in (select id from critics where active = false)
You can see by default it runs the subselect on the table of outer select. It gets more interesting you want to run a query on another ActiveRecord model:
Critic.find(:all, :conditions => { :id => {:in => {:model => :rankings, :select => :critic_id, :conditions =>
{:week => 39} } } })
Here we set :model to :rankings. Rankings is the ActiveRecord model to perform the find, notice we select the :critic_id column, the SQL is:
select * from critics where id in (select critic_id from rankings where week = 39)
And of course you can always just pass a string as a value to the subselect:
Critic.find(:all, :conditions => { :id => {:not_in => 'select id from critics where active = true' } })
Here’s how subselector can be used with the original example:
Post.find(:all, :conditions => :id => {:in => { :select => :post_id, :conditions => {:blog_id => self.id},
rder => "published_at DESC", :limit => options[:limit] || 10,
ffset => options[:offset])} },
rder => "published_at DESC")
UPDATE: Subselector now likes Condition Arrays and Named Bind Variables.
Just pass the hash as a bind variable and specify the type (in/equals) of subselect in the string, make sure to enclose your ‘?’ inside parentheses:
Critic.find(:all, :conditions => ['id in (?)', {:select => :id, :conditions => 'active = true' }])
Critic.find(:all, :conditions => ['id not in (?)', {:select => :id, :conditions => {:active => false} }])
Critic.find(:all, :conditions => ['id in (?)', {:model => :rankings, :select => :critic_id, :conditions => {:week => 39} }])
As you can see, you can format the subselect hash just as above and can specify another model to run the subselect on. If you prefer to use named bind variable hashes, they still work (yay) as you would expect. And you can assign the subselect using them:
Critic.find(:all, :conditions => ['id in (:subselect)', {:subselect => {:select => :id, :conditions => {:active => false} } }])
UPDATE 2: Now with no ActiveRecord breakage
We’ve run the rails ActiveRecord tests without any problems. Let me know if you find any problems.
Include Health October 29, 2008
Posted by reidmix in Example, Plugins & Gems, Rails.Tags: big brother, check_health, monitoring, nagios, sa, sysadmin
1 comment so far
I created a little plugin to make it easier to offer health check URLs in all our applications called Health. These URLs are a necessity for our SysAdmins to hook into Big Brother or Nagios. These monitoring systems systematically poll an application at this URL to check the status of the application.
To get started, install the health plugin and include it in your Application controller:
class ApplicationController < ActionController::Base include Health end
That’s it. You should be able to navigate to http://localhost:3000/check_heath. Health sets up the named route ‘check_health’ and by default it will check the connection of the database by running a “select 1” SQL statement. If all goes well you should see “SERVERUP”. If there’s a problem with the query you will see “DBDOWN” or an 500 error depending on the level of problem. You can turn off the DB checking (our SysAdmins wanted it):
ApplicationController < ActionController::Base include Health health_check :with_db => false end
One more trick, it does is handle any additional / custom checks that you may require. Pass a block, Proc, or a symbol that represents the name of the method to the health_check directive:
ApplicationController < ActionController::Base
include Health
health_check do |controller|
controller.has_donut? || "no donut"
end
end
In this case, assuming the controller has a method called has_donut? which does not return false or nil, you will see the “SERVERUP” message, otherwise you will see what was last evaluated in a message like “PROCDOWN no donut”
And you can mix and match all three types of blocks, Procs, and symbols — more examples are in the README.
Plug This: css_browser_selector plugin May 22, 2008
Posted by reidmix in Code, Plugins & Gems, Rails.Tags: Announcement, css, plugin, Rails
3 comments
I’m happy to announce my first plugin based on the css_browser_selector javascript which adds the name of the browser and platform as css class selectors that your client is using on the html element in your page. For example:
<html class="gecko mac js">
I’ve released my first version of the plugin (at 0.9.1) and you can download it from on LATimes RubyForge project or from GitHub. You can read how to use the plugin in its README, it offers a way to include the javascript inline into your views and layouts:
<%= javascript_tag css_browser_selector %>
But wait there’s more! What the plugin also does is offer a body (and html) tag helper that will render css selectors into your views from the backend. The real benefit to this is that these selectors will be available within the page without needing javascript enabled:
<% body do %> <%= yield %> <% end %>
And this will add the selectors as you would expect to your body tag:
<body class="gecko mac">
The body helper is currently aware of page caching and will revert to the javascript version if the page is being cached through the caches_page method.
This is my first plugin and, yes, I still have some work to do (complete my tests, better caching awareness, sensitivity to pre-existing body and html tag helpers), but I wanted to share back to the community. Following Giles Bowkett’s mantra to release software (often and early).
There’s plenty of more options as explained by the README. I welcome feedback, suggestions, improvements, bugs, and diffs! I’d also like to thank Rafeal Lima who implemented and maintains the javascript and his help and support with the plugin
IE6 Accept Header is Faulty and Makes format.any Suck May 14, 2008
Posted by reidmix in AbstractRequest, ActionController, Changeset, Code, Example, MimeResponds, Monkey Patch, Plugins & Gems, Rails, acts_as_authenticated.Tags: Accept Header, Any, Format, Format.any, HTTP_ACCEPT, ie, ie bug, ie6, ie6 bug, internet explorer, internet explorer 6
10 comments
I’ve been using the awesome acts_as_authenticated plugin as the basis for user login and authorization. Its access_denied method takes advantage of the ActionController::MimeResponds.any method which will be invoked on a login_required before filter:
def access_denied
respond_to do |format|
format.html do
store_location
redirect_to new_session_path
end
format.any do
request_http_basic_authentication 'Web Password'
end
end
end
This code essentially says, if it’s an html request, store the original request and redirect to the login page, any other requests get the 401 Unauthorized header which the user agent can then use to initiate an HTTP Auth. This bit of code only works correctly with Edge Rail’s 8987 Changeset that allows any to act as a catch-all for any request mime-types not already specified.
Awesome!
Except when Internet Explorer 6 comes into the picture and happily sends this
strange and incomplete ‘Accept:’ header:
image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*
As you can see, it doesn’t specify application/xhtml+xml or even text/html and what’s worse is the subsequent requests to the same page sends the catch-all */*.
What does this all mean?
When IE6 navigates to a page that requires login, instead of redirecting the user to the login page, the user is presented with an HTTP Auth Dialog. What’s worse is that in my application, cookies and other application state for an web-based end-user is set outside of the login_from_basic_auth method — you know — in my login action.

My guess is when the format.any fix goes out in the next rails release, IE6 users on acts_as_authenticated sites are going to be sad-faced. I’m not entirely sure the correct way to fix this problem but this is how I solved it in my application:
class ActionController::AbstractRequest
def accepts_with_faulty_header
@env['HTTP_ACCEPT']='*/*' if @env['HTTP_USER_AGENT'] =~ /msie/i and @env['HTTP_USER_AGENT'] !~ /opera|webtv/i
accepts_without_faulty_header
end
alias_method_chain :accepts, :faulty_header
end
Originally, I thought to patch the Mime::Type.parse function, but decided that I wanted to make sure to look at the user agent for IE6. As you can see, I switch the HTTP_ACCEPT header to the catch-all */* string if the original HTTP_ACCEPT header is a match to the exact (bizarre) IE6 string and the HTTP_USER_AGENT is IE (matches MSIE but not Opera or WebTV). Then I call the original ActionController::AbstractRequest.accepts method.
I’d love to hear your ideas and suggestions — and if others have run into this problem as I didn’t find many users have this exact problem and how they may have solved it.
Update: Based on the conversation in the comments, I’ve re-written the patch to always change the HTTP_ACCEPT header to the catch-all */* if it is an IE Browser.
Use ActiveResource to Consume Simple Objects Over REST January 16, 2008
Posted by reidmix in ActiveResource, Code, Example, Rails.Tags: REST
add a comment
I’m sure that many organizations have services available via REST that are not produced by Rails and do not follow Rails conventions. That doesn’t mean you have to write your own client. For simple objects, use ActiveResource.
To take advantage of ActiveResource in this way use the ActiveResource::Base#find method. You need to find(:all), find(:first), or find(:one) but you need to pass a string of the path to the :from option along with any :params you need. If you pass a symbol as the :from option, this will not work and will employ all of the the ActiveResource::Base mechanics. A string will bypass this, for example:
class RestClient < ActiveResource::Base
self.site = "http://java-based-service.company.com"
def self.invoke(params = {})
find(:one, :from => "/nonconventional/path/to/rest/service/", :params => params)
end
end
object = RestClient.invoke(:irregular_id => 10) #=> service returns <data><name>reid</name><role>blogger</role></data>
object.name #=> reid
object.role #=> blogger
Passing Hashes to Partials September 13, 2007
Posted by reidmix in Code, Example, Hash, Partial, Rails, Ruby, SimpleDelegator.add a comment
Sometimes you need to pass a Hash or collection of Hashes to a partial. In either case, when render tries to determine if you are using the old style method (render_partial) or the new style. When it comes across your hash, rails believes it’s dealing with all your supplied parameters to render. You can see this logic in action_view/partials.rb:
module ActionView
module Partials
def render_partial(partial_path, local_assigns = nil, deprecated_local_assigns = nil) #:nodoc:
path, partial_name = partial_pieces(partial_path)
object = extracting_object(partial_name, local_assigns, deprecated_local_assigns)
local_assigns = extract_local_assigns(local_assigns, deprecated_local_assigns)
local_assigns = local_assigns ? local_assigns.clone : {}
...
end
end
end
You can see the last line shown in the method, where if it can’t determine a local_assigns, it just uses an empty Hash. And so that’s how it surfaces, you pass your Hash to the partial and nothing gets rendered. What we need to do is disguise that we are passing a Hash but have all the functionality of one.
Ruby gives us a few options but the easiest is SimpleDelegator. All we need to do is pass our object upon initialization and SimpleDelegator will delegate all methods to that object:
myhash = { :a => "test1", :b => "test2" }
d = SimpleDelegator.new(myhash)
puts d[:a] # => test1
If sometimes have collections of Hashes (or Hashes mixed in with my collection), I employ the following pattern:
collection = [collection] unless collection.is_a? Array
collection = collection.collect { |i| (i.is_a? Hash) ? SimpleDelegator.new(i) : i }
render :partial => "mypartial",
bject => collection.compat
Patching Capistrano to use CVS over SSH September 11, 2007
Posted by reidmix in CVS, Capistrano, Code, Command Line, Monkey Patch, Ruby, SSH.4 comments
At work we still use CVS as our versioning repository and most of our systems rely on CVS to build and deploy into production. Currently there is no effort to transition to SVN, so we need to develop a patch to Capistrano to allow our projects to deploy to our development lab. We needed to extend Capistrano handle SSH logins to CVS and handle CVS repository directories.
Overall, both changes needed to be applied to the checkout method in Capistrano’s Cvs class.
We use SSH as the underlying communication mechanism for CVS but there is a difference in the password prompt: SSH’s is capitalized. We just need to make the prompt check case insensitive with the i switch:
if out =~ %r{password:}i
When setting up Capistrano (v1.4) for use in our development lab, the :application configuration is not sufficient to identify our rails project the CVS tree. We are not cool enough to setup or use modules to map into our repository directories, so our projects are usually in some form of webdev/project/appname. To handle this, i create an additional configuration parameter called :project in the deploy.rb:
set :project, "cvs/dir/to/myproj"
And we no longer use the application name for the CVS path, we use the project variable:
project = configuration[:project] || actor.application
command = <<-CMD
cd #{configuration.releases_path};
CVS_RSH="#{cvs_rsh}" #{cvs} -d #{configuration.repository} -Q #{op} -D "#{configuration.revision}" #{branch_option} -d #{File.basename(actor.release_path)} #{project};
CMD
The hardest part is patching Capistrano. Capistrano’s code gets loaded well after any library or environment gets loaded. The trick is to use the method_added hook to wait for the checkout method to get loaded and then alias the method to the patched one, we need to keep a variable around to check if we’ve patched the method so we don’t end up in an infinite loop:
@@checkout_patched = false
def Cvs.method_added(id)
if id.id2name == "checkout"
unless @@checkout_patched
@@checkout_patched = true
alias_method :checkout, :checkout_patch
end
end
end
I’ve not checked to see how these changes work or differ in Capistrano 2. But with all these elements together using Capistrano v1.4, I can drop this file in my lib directory or create a plugin with it. Together, here is the full patch which I have in a file called capistrano_cvs_ext.rb:
module Capistrano
module SCM
class Cvs < Base
@@checkout_patched = false
def Cvs.method_added(id)
if id.id2name == "checkout"
unless @@checkout_patched
@@checkout_patched = true
alias_method :checkout, :checkout_patch
end
end
end
def checkout_patch(actor)
cvs = configuration[:cvs] || "cvs"
cvs_rsh = configuration[:cvs_rsh] || ENV['CVS_RSH'] || "ssh"
if "HEAD" == configuration.branch then
branch_option = ""
else
branch_option = "-r #{configuration.branch}"
end
# cvs has a root and repository, repository is not the same as application name as it can be a path
op = configuration[:checkout] || "co"
project = configuration[:project] || actor.application
command = <<-CMD
cd #{configuration.releases_path};
CVS_RSH="#{cvs_rsh}" #{cvs} -d #{configuration.repository} -Q #{op} -D "#{configuration.revision}" #{branch_option} -d #{File.basename(actor.release_path)} #{project};
CMD
run_checkout(actor, command) do |ch, stream, out|
prefix = "#{stream} :: #{ch[:host]}"
actor.logger.info out, prefix
if out =~ %r{password:}i # SSH asks for "Password" with a capital P
actor.logger.info "CVS is asking for a password", prefix
ch.send_data "#{actor.password}n"
elsif out =~ %r{^Enter passphrase}
message = "CVS needs your key's passphrase and cannot proceed"
actor.logger.info message, prefix
raise message
end
end
end
end
end
end