tag:blogger.com,1999:blog-83807975805616829232024-03-14T03:27:10.319-07:00Got CHUNK?broughhttp://www.blogger.com/profile/12822885617897320670noreply@blogger.comBlogger5125tag:blogger.com,1999:blog-8380797580561682923.post-4071384323618670582008-05-02T18:55:00.001-07:002008-05-08T00:53:16.744-07:00Rorbby #rubyonrails api bot<a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjUDCLl0KEQSE2dN6vOdEpp0L-H4pYVhH2COJlTfw0ep1sTZWK2VSC4cKTqee8fOHUjoO47lMFldvoH3Z7sezTzpODZ4QdrfbNxdlnWgdNNeZIcZYZGAsbK5oTZwRoGTb-QP5Sfie6PjGY/s1600-h/robby_alta.jpg"><img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer;" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjUDCLl0KEQSE2dN6vOdEpp0L-H4pYVhH2COJlTfw0ep1sTZWK2VSC4cKTqee8fOHUjoO47lMFldvoH3Z7sezTzpODZ4QdrfbNxdlnWgdNNeZIcZYZGAsbK5oTZwRoGTb-QP5Sfie6PjGY/s320/robby_alta.jpg" alt="" id="BLOGGER_PHOTO_ID_5197911962879975186" border="0" /></a><br /><br />An irc bot written in ruby to fuzzy search every method in the combined ruby and rails docs, returning definition, example and tinyurl (and currently working pretty well under ruby 1.9 without a database).<br /><br />basic usage:<br /><br />?api_method:args:args:args [nick]<br />??faq_keyword [nick]<br /><br />You can also query rorbby via private message (although faq topics can only be defined in open channel)<br /><br /><span style="font-weight: bold;">Populating the faq</span><br /><br />if the phrase begins with the keyword:<br /><br />03:05 <@brough> rorbby, select is broken.<br />03:05 <@brough> ??select<br />03:05 <@rorbby> select is broken.<br /><br /><span>alternatively, define the keyword first:</span><br /><br />03:11 <@brough> rorbby, scaling: Rails can instantaneously project solid matter to any point on the internet, in any shape or color you might imagine. For *any* purpose! Creation by mere thought. (Question about the 8,000 cubic miles of klystron relays? See: scaling_performance)<br />03:11 <@brough> ??scaling<br />03:11 <@rorbby> Rails can instantaneously project solid matter to any point on the internet, in any shape or color you might imagine. For *any* purpose! Creation by mere thought. (Question about the 8,000 cubic miles of klystron relays? See: scaling_performance)<br /><br /><span style="font-weight: bold;"><br />to overwrite an existing definition, append a splat:</span><br /><br />03:12 <@brough> rorbby, select is fixed. *<br />03:12 <@brough> ??select<br />03:12 <@rorrby> select is fixed.<br /><br />FAQ definitions can be created as an array:<br /><br />05:17 <> ??vps<br />05:18 <> rorbby, vps: <<> rorbby, vps: <<> ??vps<br />05:18 <> slicehost.com, bytemark.co.uk, linode.com<br /><br />You can remove items from the list with '>>'<br /><br /><span style="font-weight: bold;">API definitions</span> (for Rails api queries just omit the first ':ruby' param)<br /><br />for example, targeting the following methods in the ruby api:<br /><br /><pre><br />alive? (Thread)<br />alive? (DRb::DRbTCPSocket)<br />alive? (DRb::ExtServ)<br />alive? (Rinda::TupleEntry)<br />alive? (DRb::DRbServer)<br /></pre><br /><br /><span>return all methods that contain the query string:</span><br /><br />02:59 <@brough> ?ali:ruby:fuzzy<br />02:59 <@rorbby> add_alias RUBY:RDoc; add_alias RUBY:RDoc; add_finalizer RUBY; alias_command<br /> RUBY:She; alias_command RUBY; alias_extension RUBY:RDoc; alias_map RUBY:She;<br /> alias_method RUBY; aliases RUBY:Gen; alive? RUBY (10 of 105)<br /><br /><span>return the first method name exactly matching the query string:</span><br /><br />02:58 <@brough> <span>?alive?:ruby</span><br />02:59 <@rorbby> alive? (DRb::DRbServer) Is this server alive? ...<br /><br />drill it down:<br /><br />03:01 <@brough> ?alive?:ruby:th<br />03:01 <@rorbby> alive? (RUBY::Thread) Returns true if thr is running or sleeping. ... thr =<br /> Thread.new { }; thr.join #=> #<thread:0x401b3fb0>...<br /></thread:0x401b3fb0><br /><br />03:01 <@brough> ?alive?:ruby:drb<br />03:01 <@rorbby> alive? (DRb::DRbServer) Is this server alive? ...<br /><br />03:02 <@brough> ?alive?:ruby:drb:tcp<br />03:02 <@rorbby> alive? (DRb::DRbTCPSocket) Check to see if this connection is alive. ...<br /><br /><span>private queries contain fuller descriptions and more usage examples:</span><br /><br />02:55 <@brough> ?>>:ruby:proc brough<br />02:55 <@rorbby> >> (Process::Status) Shift the bits in stat right num places. fork { exit 99 } #=><br /> 26563; Process.wait #=> 26563; $?.to_i #=> 25344; $? >> 8 #=> 99<br /><br /><a href="http://github.com/broughcut/rorapi">http://github.com/broughcut/rorapi</a>broughhttp://www.blogger.com/profile/12822885617897320670noreply@blogger.com0tag:blogger.com,1999:blog-8380797580561682923.post-85631104507462060682008-03-07T16:49:00.000-08:002008-03-07T21:47:09.999-08:00Date ranges from a list of datesYou need date <span style="font-style: italic;">ranges</span> before you can use <a href="http://gotchunk.blogspot.com/2008/03/crisscrossing-date-range-queries-in.html">acts_as_line</a>. Often you will just have a list of booked dates.<br /><br />I've pluginized my date range method, which takes a collection of dates and finds the ranges: <a href="http://github.com/broughcut/date_ranger/tree/master">date_ranger</a><br /><pre><br />DateRanger<br />==========<br /><br />Identify start/end dates in a set.<br /><br />Example<br />=======<br /><br />require 'date_ranger'<br />require 'date'<br /><br />dates = %w(2008-04-26 2008-04-27 2008-04-28 2008-04-29 2008-04-30 2008-05-01 2008-05-02 2008-05-24 2008-05-25 2008-05-26 2008-05-27 2008-05-28 2008-05-29 2008-05-30 2008-07-12 2008-07-13 2008-07-14 2008-07-15 2008-07-16 2008-07-17 2008-07-18 2008-07-26 2008-07-27 2008-07-28 2008-07-29 2008-07-30 2008-07-31 2008-08-01 2008-08-09 2008-08-10 2008-08-11 2008-08-12 2008-08-13 2008-08-14 2008-08-15 2008-08-16 2008-08-17 2008-04-21 2008-04-21 2008-08-18 2008-08-19 2008-04-23 2008-08-20 2008-04-22 2008-08-21 2008-08-22 2008-12-27 2008-12-28 2008-12-29 2008-12-30 2008-12-31 2008-03-29 2008-03-30 2008-03-31 2008-04-01 2008-04-02 2008-04-03 2008-04-04 2008-04-05 2008-04-06 2008-04-07 2008-04-08 2008-04-09 2008-04-10 2008-04-11 2008-04-12 2008-04-13 2008-04-14 2008-04-15 2008-04-16 2008-04-20 2008-04-17 2008-04-18 2008-05-24 2008-05-25 2008-05-26 2008-05-27 2008-05-28 2008-05-29 2008-05-30 2008-05-31 2008-06-01 2008-06-02 2008-06-03 2008-06-04 2008-06-05 2008-06-06 2008-07-05 2008-07-06 2008-07-07 2008-07-08 2008-07-09 2008-07-10 2008-07-11 2008-07-12 2008-07-13 2008-07-14 2008-07-15 2008-07-16 2008-07-17 2008-07-18 2008-07-19)<br /><br />bookings = DateRanger.new(dates)<br />p bookings.ranges<br /><br />>> [{"end"=>"2008-04-18", "start"=>"2008-03-29"}, {"end"=>"2008-04-23", "start"=>"2008-04-20"}, {"end"=>"2008-05-02", "start"=>"2008-04-26"}, {"end"=>"2008-06-06", "start"=>"2008-05-24"}, {"end"=>"2008-07-19", "start"=>"2008-07-05"}, {"end"=>"2008-08-01", "start"=>"2008-07-26"}, {"end"=>"2008-08-22", "start"=>"2008-08-09"}, {"end"=>"2008-12-31", "start"=>"2008-12-27"}]<br /><br />csv = bookings.to_csv('|')<br />bookings.csv.each {|row|<br />p row<br />}<br /><br />gives:<br /><br />"2008-03-29|2008-04-18"<br />"2008-04-20|2008-04-23"<br />"2008-04-26|2008-05-02"<br />"2008-05-24|2008-06-06"<br />"2008-07-05|2008-07-19"<br />"2008-07-26|2008-08-01"<br />"2008-08-09|2008-08-22"<br />"2008-12-27|2008-12-31"<br /></pre><br /><br />UPDATE:<br /><br />gem install dateranger<br /><br />api has changed slightly in the gem release:<br /><br />DateRanger::Range.new(dates_array)<br /><br />Plugin is best at this stage, easier to hack.broughhttp://www.blogger.com/profile/12822885617897320670noreply@blogger.com0tag:blogger.com,1999:blog-8380797580561682923.post-81012514591534266432008-03-07T10:08:00.000-08:002008-03-12T20:57:22.403-07:00Crisscrossing date range queries in RailsIf you are building an application that takes reservations or searches for availability, you're likely to want to query against booked date ranges to find if the candidate date range is available, or if it conflicts with an existing booking.<br /><br />There are various ways to find overlapping date ranges with SQL, using the SQL OVERLAPS operator, or a custom query, as described (in detail) by depesz on <a href="http://www.depesz.com/">http://www.depesz.com</a>: <a href="http://www.depesz.com/index.php/2007/11/21/find-overlapping-time-ranges/">http://www.depesz.com/index.php/2007/11/21/find-overlapping-time-ranges/</a><br /><br />In the past I might have written a method like this in my model to find available units (more often than not combined with other search criteria):<br /><pre><code> @rooms = Room.find_by_sql ["<br /> SELECT * FROM rooms WHERE rooms.id NOT IN<br /> (SELECT DISTINCT ON (rooms.id) rooms.id FROM bookings<br /> INNER JOIN rooms ON rooms.id=bookings.rooms_id<br /> WHERE ((start_date, end_date) OVERLAPS (DATE '#{start_date[0]}',<br /> INTERVAL '#{interval} days') = true;"]<span style="font-family:onload;"><br /><br /></span></code></pre>Enter <a href="http://github.com/broughcut/acts_as_line/tree/master">acts_as_line</a>, a simple plugin that unifies a date range into a single geometry and looks for other ranges (now lines) that spatially intersect with it.<br /><br />I am putting this together for querying larger datasets with several million date ranges, where OVERLAPS may feel the squeeze. The utility of using this method for small datasets is questionable, but it is trivial to extend a Postgres database with PostGIS, and I think I'll find the plugin more convenient than writing OVERLAPS queries. It certainly shouldn't be <span style="font-style: italic;">slower.</span> I've yet to benchmark it against OVERLAPS on large, indexed tables, but I expect it to be quicker.<br /><br />In its current form, the plugin ssumes the model has existing date fields, start_date and end_date, of type date or datetime, and a geometry column named 'geom'. There is a migration generator to add the dates geometry to an existing table (providing the postgres database is spatially enabled). I will be adding options to specify alternate field names via the model soon, and will probably rename the geometry field to date_geom to minimise the chances of conflict with existing schemas under the defaults (although I don't anticipate anyone storing other, geographic, geometries on the same table as the one storing date ranges).<br /><br />Adding acts_as_line to a model,<br /><pre><code><br />AgencyBooking < ActiveRecord::Base<br /><br /> acts_as_line<br /><br />end<br /></code></pre><br />makes the following methods available:<br /><pre><code><br />object = AgencyBooking.find(:first)<br /><br />results = AgencyBooking.touching(object) | results = Foo.intersects(object,true)<br />results = AgencyBooking.asunder(object) | results = Foo.intersects(object,false)<br /><br />results = AgencyBooking.touching(object,{:id => 123})<br />results = AgencyBooking.touching(object,{:id => '>123'})<br />results = AgencyBooking.touching(object,{:id => '<>123'}) etc<br />results = AgencyBooking.touching(object,{:id => 123, :title => 'bar'})<br /></code></pre><br />Some usage examples<br /><code><br /><br />create a new record:<br /><br />>> booking = AgencyBooking.new(:start_date => Date.today, :end_date => Date.today+6.days)<br />=> #<AgencyBooking id: nil, agency_id: nil, agency_unit_id: nil, start_date: "2008-03-07", end_date: "2008-03-13", created_at: nil, updated_at: nil, geom: nil><br />>> booking.save<br />=> true<br />>> booking<br />=> #<AgencyBooking id: 12746, agency_id: nil, agency_unit_id: nil, start_date: "2008-03-07", end_date: "2008-03-13", created_at: "2008-03-07 10:05:45", updated_at: "2008-03-07 10:05:45", geom: "0102000000020000000000000000000000000000803DF4D1410..."></code><br /><code><br /><br />find overlapping records:<br /><br />>> AgencyBooking.touching(booking)<br />=> [#<AgencyBooking id: 12740, agency_id: nil, agency_unit_id: nil, start_date: "2008-03-01 00:00:00", end_date: "2008-03-11 00:00:00", created_at: "2008-03-06 16:00:00", updated_at: "2008-03-06 16:00:00", geom: "01020000000200000000000000000000000000004043F2D1410...">, #<AgencyBooking id: 12746, agency_id: nil, agency_unit_id: nil, start_date: "2008-03-07 00:00:00", end_date: "2008-03-13 00:00:00", created_at: "2008-03-07 10:05:45", updated_at: "2008-03-07 10:05:45", geom:<br />"0102000000020000000000000000000000000000803DF4D1410...">]<br /><br />find records that do not overlap:<br /><br />>> AgencyBooking.asunder(booking)<br />=> [#<AgencyBooking id: 12741, agency_id: nil, agency_unit_id: nil, start_date: "2008-03-01 00:00:00", end_date: "2008-03-02 00:00:00", created_at: "2008-03-06 16:00:02", updated_at: "2008-03-06 16:00:02", geom: "01020000000200000000000000000000000000004043F2D1410...">, #<AgencyBooking id: 12742, agency_id: nil, agency_unit_id: nil, start_date: "2008-03-01 00:00:00", end_date: "2008-03-01 00:00:00", created_at: "2008-03-06 16:00:04", updated_at: "2008-03-06 16:00:04", geom: "01020000000200000000000000000000000000004043F2D1410...">, #<AgencyBooking id: 12743, agency_id: nil, agency_unit_id: nil, start_date: "2008-03-21 00:00:00", end_date: "2008-03-23 00:00:00", created_at: "2008-03-06 16:01:41", updated_at: "2008-03-06 16:01:41", geom: "01020000000200000000000000000000000000003CD7F8D1410...">, #<AgencyBooking id: 12744, agency_id: nil, agency_unit_id: nil, start_date: "2008-03-21 00:00:00", end_date: "2008-03-21 00:00:00", created_at: "2008-03-06 16:02:28", updated_at: "2008-03-06 16:02:28", geom: "01020000000200000000000000000000000000003CD7F8D1410...">, #<AgencyBooking id: 12745, agency_id: nil, agency_unit_id: nil, start_date: "2008-03-01 00:00:00", end_date: "2008-03-06 00:00:00", created_at: "2008-03-06 18:00:47", updated_at: "2008-03-06 18:00:47", geom: "01020000000200000000000000000000000000004043F2D1410...">]<br /></code><br /><br />You can grab the initial release from github:<br /><br /><a href="http://github.com/broughcut/acts_as_line/tree/master">acts_as_line</a><br /><br />I still have much to add and refine.broughhttp://www.blogger.com/profile/12822885617897320670noreply@blogger.com0tag:blogger.com,1999:blog-8380797580561682923.post-46463079451712707032008-03-02T22:33:00.000-08:002008-03-03T03:42:37.576-08:00DRYing up CMS with GoogleAs a follow-on to <a href="http://github.com/broughcut/picasync/tree/master">Picasync</a>, I've started work on a library to interface with Google Documents, so that google can be used to manage a website's text and articles in addition to its image galleries.<br /><br />Google Documents provides users with a familiar, intuitive word processing UI, revision history and drafts, and document folders... at the expense of lots of hpricot cannon-fodder.<br /><br />The process:<br /><ul><li>Find document on google, and :fetch its content<br /></li><li>Strip script and form tags and tag attributes (including inline styles)</li><li>convert to textile (c/o <a href="http://jystewart.net/process/2007/11/converting-html-to-textile-with-ruby">James Stewart's html2textile ruby script)</a></li><li>escape left-over html</li><li>convert back to [spartan? hopefully] html from textile via RedCloth (with some house-keeping regex that needs really expanding upon)</li></ul><span style="font-style: italic;">i.e.</span><br /><pre><code>doc = Gdocsync::Document.find_by_title("Foo", :fetch).clothe<br /></code></pre>or, skip Redcloth <span style="font-style: italic;">(but still escape tatterdemalion tags after textile conversion)</span>:<br /><pre><code>doc = Gdocsync::Document.find_by_title("Foo", :fetch).textile<br /></code></pre>With safe_html, the user gets pretty much what they see on google (however that document might be constructed), but you also get stuck with inline styles and tag soup -- only script tags (which Google Docs doesn't appear to allow, anyway), and form tags, are stripped:<br /><pre><code>doc = Gdocsync::Document.find_by_title("Foo", :fetch).safe_html<br /></code></pre>You also have recourse to the raw html body with no modifications beyond google's own processing (the 'raw' document is stored in the object and used as the starting point for the previous methods):<br /><pre><code>doc = Gdocsync::Document.find_by_title("Foo", :fetch).raw<br /></code></pre>This can all be easily tied together with database tables via a rake task. The following snippet loops through Properties, looks up the document on google and updates the database record. This example matches document title with object title field, which is not very smart -- in the next few days I will be making use of google document directories and tightening things up.<br /><pre><code>namespace :google do<br /><br /> task :docs => :environment do<br /> require '../gdocsync/lib/gdocsync'<br /><br /> Property.find(:all).each do |property|<br /> doc = Gdocsync::Document.find_by_title(property.title, :fetch)<br /> Property.update(property.id, :description => doc.clothe)<br /> end<br /><br /> end<br /><br />end<br /><br /></code></pre>$ rake google:docs<br /><br /><br /><a href="http://github.com/broughcut/gdocsync/tree/master">Gdocsync git repo (early days).</a>broughhttp://www.blogger.com/profile/12822885617897320670noreply@blogger.com0tag:blogger.com,1999:blog-8380797580561682923.post-88636535769389056972008-03-01T03:41:00.000-08:002008-03-03T03:32:31.622-08:00Ruby Interface for Google Picasa APIA nascent ruby module for interfacing with Picasa and mirroring user albums locally (initially conceived so I could programmatically delete the multitude of galleries I'd spawned while hacking around trying to get stuff to work).<br /><pre><br />Picasync::Album.find(:all).each {|album|<br /> album.delete!<br />}<br /><br />%w(one two three).each do |title|<br /> album = Picasync::Album.new(title)<br /> album.create!<br />end<br /></pre><br />Easy. No ability to upload images via the api yet, although its purpose is to farm-out uploading and cms tasks to Picasa's UI anyhow, so that Picasa can essentially power a site's galleries (but without hotlinking or hitting their feed on every page load).<br /><br />Albums are synced locally via <span style="font-style: italic;">Picasync::Sync::All.new</span> & <span style="font-style: italic;">Picasync::Sync::CSV.new</span>, which fetches files to a single directory, hashing the file names and generating a couple of csvs for image sizes, captions and parent albums. Still to add table migrations and automatic csv imports.<br /><br />It's also simple to fetch files arbitrarily, be it a whole album, or a particular image in a set (although things aren't properly tied together with csv generation yet):<br /><pre><br />Picasync::Image.mirror(:album, album.id)<br />Picasync::Image.mirror(image.id, album.id)<br /></pre><br />Uses google's ClientLogin authentication scheme.<br /><br />It's a work in progress and definitely not a drop-in solution for end users, <a href="http://github.com/broughcut/picasync/tree/master">but you can grab (and contribute to) the code on Github</a>.<br /><br />See the Readme for the methods I've got around to adding.<br /><pre><br />albums = Picasync::Album.find(:all, :images)<br />albums.each do |album|<br />puts album.title<br />album.images.each {|image|<br /> puts img.medium<br /> puts img.caption<br />}<br />end<br /></pre>broughhttp://www.blogger.com/profile/12822885617897320670noreply@blogger.com0