jump to navigation

Subselector, Moneypenny November 1, 2008

Posted by reidmix in ActiveRecord, Code, Database, Example, Monkey Patch, Plugins & Gems, Rails.
Tags: , , , , ,
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}, :order => "published_at DESC", :limit => options[:limit] || 10, :offset => options[:offset])} }, :order => "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.

These Chains of Selects October 29, 2008

Posted by reidmix in Code, Example, Javascript.
Tags: , , , , , ,
3 comments

I was looking for javascript library that would manage select (HTML) elements that are dependent on one another.  These are the drop-downs you often see on online car sites where you select a make and it populates the next drop-down with the models for that car make.

I was hoping that there would be a libary that was publically available, object oriented, small, backed by JSON, and would even like it to be based on the Prototype library.  All I could find was a library that may satisfy one of my requirements but not all.  Libraries I found were either procedural, terribly bulky and complicated, and never backed by JSON.

In my buy it or build it moment, I decided to build it: here is a chained_selects javascript library.  It’s fairly lightweight, it runs in about 85 lines (comments and whitespace included), you can initialize it with javascript objects or JSON, it’s object oriented and relies on just a little bit of Prototype.

You need the heirarchy of data to back a ChainedSelect object, the form, and selects that are assoicated with each “level” in the heirarchy.  It can have as many levels you want in the hierarchy from 2 up.  So, in my car example, imagine you have a two car makes (Acura and Volkswagen) and the Acura make has two models (the RL and the TL, sweet!), and the Volkswagen make has three models (Beetle, Jetta, and the Passat).  Each of the models have a database id.  My JSON for this heirarchy would look like:

var data = {"Acura":{"RL":1, "TL":2},"Volkswagen":{"Beetle":3,"Jetta":4,"Passat":5}}

You can see the first level is the outer-most hash with the makes, and the inner mosts hashes have the models and their db ids.  For rails programmers, this could be a rails hash that you run to_json with.

I set up my HTML form, with a select for each level in the heirarchy, like so:

  <form id="cars" method="post" action="/choose_car">
    <select name="make"></select>
    <select name="model"></select>
  </form>

Then I can setup the ChainedSelect object to manage these selects, using the data above:

new ChainedSelect(data, 'cars', ['make','model']);

This says, created a chained select with my data for the form with id ‘cars’, with the first level select with the name ‘make’ and second level select with the name ‘model’.

The first select will load with three elements:

  • Choose make…
  • Acura
  • Volkswagen

When Volkswagen is selected, the second select will load:

  • Choose model…
  • Beetle
  • Jetta
  • Passat

If Acura is then chose, the second select will refresh with the correct data. If the ‘model’ is chosen let’s say Jetta, then the id for that model is sent in the form (4).  For rails developers, the name of the select needs to be different from the label (“Choose model…”), you can specify a label and a select name separately, with an embedded array, like so:

new ChainedSelect(data, 'cars', ['make',['car[model]','model']]);

Oftentimes, we have an edit page with the values already prefilled on our forms, we can add another array parameter that specifies the path to the selection:

new ChainedSelect(data, 'cars', ['make','model'], ['Volkswagen','Jetta']);

Lastly, when the final select is chosen, sometimes we want to take an action or an AJAX call, we can supply an onComplete function to run which is yielded a the value chosen:

new ChainedSelect(data, 'cars', ['make','model'], null, function(choice) { alert("You chose:"+choice); } );

You can use all of these elements together to get the behavior you want, I also have a little rails form helper to create my javascript for me:

  def chained_selects(tree, form, selects, active, oncomplete=nil)
    %(new ChainedSelects(#{tree}, '#{form}', #{selects.inspect}, #{active.inspect}, #{oncomplete || 'null'});)
  end

I know that the script can be improved in many ways (Doug McInnes added the onComplete feature).  Please feel free to send patches or any bugs you may find.
Enjoy!

Include Health October 29, 2008

Posted by reidmix in Example, Plugins & Gems, Rails.
Tags: , , , , ,
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
&#91;/sourcecode&#93;

That's it.  You should be able to navigate to <a href="http://localhost:3000/check_heath">http://localhost:3000/check_heath</a>. Health sets up the named route '<span class="s1">check_health' and</span> by default it will check the connection of the database by running a "<strong>select 1</strong>" 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 [/sourcecode] 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.

Adding vi keybindings to irb, script/console, and mysql October 19, 2008

Posted by John Dewey in Rails, Ruby.
12 comments

I loves me my vi, and sun keyboard mappings (although it drives people crazy when they peer on my macbook).

Add the following on OS X 10.5:

john@emopop:~> cat .editrc
bind -v

This will work for OS X 10.4 and other operating systems:

john@emopop:~> cat .inputrc 
set editing-mode vi

Hyphenated XML tags in Builder October 13, 2008

Posted by John Dewey in ActiveRecord, Example, Rails, Ruby.
3 comments

I typically use to_xml when building a shallow representation of my model in XML. It becomes hella difficult to maintain the format of my XML when nesting numerous levels deep. Sometimes you gotta use a Builder.

If you would like your tags to be hyphenated (like the to_xml default), here is a nice trick:

xml.__send__('hyphenated-tag-name'.to_sym) do
  xml.tag "data"
end

Coverage Nagging October 10, 2008

Posted by John Dewey in Code, Command Line, Example, Rails, RSpec.
add a comment

I like to keep an eye on my coverage every so often. I run the following in my applications root, for occasional coverage reminders.

while : ; do rake spec:rcov ; open coverage/index.html; sleep 3600 ; done

OpenURI returns two different objects September 24, 2008

Posted by John Dewey in Code, Ruby.
1 comment so far

I am unfortunate in using ImageMagick to watermark images passed into a service. However, I noticed occasional nils when trying to read images returned by OpenURI.

Magick::Image.read(open(url).path).first

Interesting… OpenURI returns a StringIO object rather than Tempfile when data on the other end exceeds 10240 bytes.

StringMax = 10240
def <<(str)
  @io << str
  @size += str.length
  if StringIO === @io && StringMax < @size
    require 'tempfile'
    io = Tempfile.new('open-uri')
    io.binmode
    Meta.init io, @io if @io.respond_to? :meta
    io << @io.string
    @io = io
  end
end

There are several approaches to this problem, but I opted for the easy one. I simply added the following to my model to force a Tempfile.

##
# OpenURI::Buffer::StringMax = 10240
OpenURI::Buffer::StringMax = 0

Since this particular application is not under active development, and Rails _only_ uses OpenURI for ‘script/plugin’ and a couple generators — I didn’t loose too much sleep.

Railsolver – now with wildcard host resolution September 12, 2008

Posted by John Dewey in Monkey Patch, Plugins & Gems, Rails, Ruby.
add a comment

I was watching the Subdomains Railscast. Ryan listed a few ways to point subdomains at your development system, and I thought Railsolver should support wildcards.

Updates are on github, along with an updated README and bugs :). I also added some Railsolver props on the Railscast page.


This plugin will “hijack” Ruby host(s) resolver aka(resolv-replace.rb), allowing programmatic host file resolution (including wildcard host resolution).™

Railsolver – programmatic Rails host resolution September 9, 2008

Posted by John Dewey in Plugins & Gems, Rails, Ruby.
add a comment

Ever want to to programmatically control your applications name resolution? Railsolver takes a stab at doing just that.


This plugin will “hijack” Ruby host(s) resolver aka(resolv-replace.rb),
allowing programmatic host file entries.

Hosts can be added globally or overridden per environment inside a YAML configuration file. You can also embed ERB.

See the README for usage.

controlling memcached with Phusion’s daemon_controller September 6, 2008

Posted by John Dewey in Code, Deployment, Memcached, Plugins & Gems, Rails, Ruby.
add a comment

I am working on an application that utilizes memcached, and want to develop against a locally running instance. Previously, we pointed our development environment to a sandboxed memcached cluster, which can be slow depending on time of day, location, and network. I typically mock external resources, however, I like a living memcache.

This post describes a way to start memcached automatically with script/server, and is a continuation of the daemon_controller memcached configuration post.

As the Phusion guys pointed out. Pulling code — and not worrying about configuration is AWESOME! This is especially true in the development environment.

phusion-daemon-controller.png

Adding this to development.rb will automatically start and stop memcached via ‘script/console’ and ‘script/server’. SAs maintain the daemons in the other environments via SMF, nor am I interested in maintaining those environments (did that … done that).