RubyEnRails2007 - Dr Nic Williams - DIY Syntax

4,880 views

Published on

Many features of rails give you beautiful syntax - here's some ideas to create your own

Published in: Business, Technology
0 Comments
4 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
4,880
On SlideShare
0
From Embeds
0
Number of Embeds
184
Actions
Shares
0
Downloads
278
Comments
0
Likes
4
Embeds 0
No embeds

No notes for slide

RubyEnRails2007 - Dr Nic Williams - DIY Syntax

  1. 1. Dr Nic drnicwilliams.com DIY Syntax
  2. 2. $ irb >> $KCODE = quot;uquot; >> quot;303274quot; => quot;üquot;
  3. 3. Isn’t this nicer? >> U00FC => quot;üquot;
  4. 4. $ sudo gem install charesc $ irb >> $KCODE = quot;uquot; >> require 'rubygems' >> require 'charesc' >> quot;charesc is made by Martin D#{U00FC}rstquot; => quot;charesc is made by Martin Dürstquot; >> U00FC => quot;üquot; >> quot;303274quot; => quot;üquot; charesc
  5. 5. Active Records SELECT * FROM conferences WHERE (start_date > '2007-06-07') # But, much nicer is...
  6. 6. Active Records SELECT * FROM conferences WHERE (start_date > '2007-06-07') # But, much nicer is... Conference.find(:all, :conditions => [‘start_date > ?’,Date.today])
  7. 7. Active Records SELECT conferences.quot;idquot; AS t0_r0, conferences.quot;namequot; AS t0_r2, ..., conference_sessions.quot;idquot; AS t1_r0, conference_sessions.quot;conference_idquot; AS t1_r1, ... FROM conferences LEFT OUTER JOIN conference_sessions ON conference_sessions.conference_id = conferences.id WHERE (start_date < '2007-05-26') # But, definitely easier and nicer is...
  8. 8. Active Records SELECT conferences.quot;idquot; AS t0_r0, conferences.quot;namequot; AS t0_r2, ..., conference_sessions.quot;idquot; AS t1_r0, conference_sessions.quot;conference_idquot; AS t1_r1, ... FROM conferences LEFT OUTER JOIN conference_sessions ON conference_sessions.conference_id = conferences.id WHERE (start_date < '2007-05-26') # But, definitely easier and nicer is... Conference.find(:all, :conditions => ['start_date < ?', Date.today], :include => :conference_sessions)
  9. 9. Sexy Syntax ActiveRecords - no SQL ... + marshalling to objects
  10. 10. Sexy Syntax Convenient Code
  11. 11. Schemas create_table :conferences do |t| t.fkey :user t.string :name, :limit => 50 t.text :description t.date :start_date, :end_date t.auto_dates end # instead of... I can never remember SQL syntax
  12. 12. Schemas create_table :conferences do |t| t.fkey :user t.string :name, :limit => 50 t.text :description t.date :start_date, :end_date t.auto_dates end # instead of... Look up SQL on I can never remember SQL syntax
  13. 13. Composite Primary Keys ProductHistory.find(:first, :conditions => ['id = ? and start_date = ?', 56, Date.new(2000,5,5)]) # rather... What about Ruby syntax improving on other Ruby syntax?
  14. 14. Composite Primary Keys ProductHistory.find(:first, :conditions => ['id = ? and start_date = ?', 56, Date.new(2000,5,5)]) # rather... ProductHistory.find(56, Date.new(2000,5,5)) What about Ruby syntax class ProductHistory < ActiveRecord::Base improving on other Ruby set_primary_keys :id, :start_date syntax? end
  15. 15. What’s this do? #1 Nice syntax can quickly tell you want the code will does @user.conference_sessions_for(@conference)
  16. 16. What’s this do? #1 Nice syntax can quickly tell you want the code will does @user.conference_sessions_for(@conference) # Returns all ConferenceSessions # for a user at a conference
  17. 17. What’s this do? #2 @conference_attendees.map_id_and_login_and_full_name
  18. 18. What’s this do? #2 @conference_attendees.map_id_and_login_and_full_name # map {|att| [att.id, att.login, att.full_name]}
  19. 19. What’s this do? #3 User.find(params[:id])
  20. 20. What’s this do? #3 @to_user = @target_db::User.find(params[:id]) User.find(params[:id])
  21. 21. What’s this do? #3 @to_user = @target_db::User.find(params[:id]) User.find(params[:id]) @to_user.update(@from_db::User.find(params[:id]))
  22. 22. What’s this do? #3 @to_user = @target_db::User.find(params[:id]) User.find(params[:id]) @to_user.update(@from_db::User.find(params[:id])) # Copies a User from one database to another # see Magic Multi-Connection # http://magicmodels.rubyforge.org
  23. 23. Let’s see how they work...
  24. 24. What’s this do? #1 Nice syntax can quickly tell you want the code will does @user.conference_sessions_for(@conference)
  25. 25. What’s this do? #1 Nice syntax can quickly tell you want the code will does @user.conference_sessions_for(@conference) # Returns all ConferenceSessions # for a user at a conference
  26. 26. What’s this do? #1 class User < ActiveRecord::Base has_many :conference_sessions def conference_sessions_for(conference) conference_sessions.find(:all, :conditions => ['conference_id = ?', conference]) end end No meta-magic, but it gives nice syntax
  27. 27. What’s this do? #2 @attendees.map_by_id_and_login_and_full_name # map {|att| [att.id, att.login, att.full_name]}
  28. 28. Cute ways to use #map @users = User.find(:all) @users.map {|user| user.login} # => ['drnic', 'topfunky', 'dhh']
  29. 29. Cute ways to use #map @users = User.find(:all) @users.map {|user| user.login} # => ['drnic', 'topfunky', 'dhh'] @users.map &:login # => ['drnic', 'topfunky', 'dhh']
  30. 30. Cute ways to use #map @users = User.find(:all) @users.map {|user| user.login} # => ['drnic', 'topfunky', 'dhh'] @users.map &:login # => ['drnic', 'topfunky', 'dhh'] # but it gets ugly when you chain them... @users.map(&:login).map(&:size) # => [5, 8, 3]
  31. 31. Cute ways to use #map # So, instead of... @users.map(&:login).map(&:size) # We might like... @users.map_login.map_size
  32. 32. Remember find_by_xxx ? def method_missing(method_id, *arguments) pattern = /^find_(all_by|by)_([_a-zA-Z]w*)$/ if match = pattern.match(method_id.to_s) finder = determine_finder(match) # from active_record/base.rb, line 1190
  33. 33. Dissecting the request def method_missing(method, *args, &block) pattern = /(map|select|...)_by_([w_]+??)/ if (match = method.match(pattern)) iterator, callmethod = match[1], match[2] # changed ‘find’ to ‘map’ # or select, reject, each, collect...
  34. 34. Iterating the request # continued... iterator, callmethod = match[1], match[2] self.send(iterator) {|obj| obj.send callmethod } # @array.map_by_foo # callmethod => ‘foo’ # #foo invoked on each element of Array
  35. 35. Iterating many callmethods # continued... iterator, callmethod = match[1], match[2] callmethods = callmethod.split('_and_') callmethods.map do |callmethod| self.send(iterator) {|obj| obj.send callmethod } end # @array.map_by_foo_and_bar # callmethods => [‘foo’, ‘bar’] # #foo and #bar invoked on each element of Array
  36. 36. map_by_method gem # for complete source: $ gem install map_by_method $ mate $RUBYGEMS_PATH/map_by_method-0.6.0/lib/map_by_method.rb
  37. 37. map_by_method gem # for complete source: $ gem install map_by_method $ mate $RUBYGEMS_PATH/map_by_method-0.6.0/lib/map_by_method.rb # add the following to your ~/.irbrc require 'map_by_method'
  38. 38. What’s this do? #3 User.find(params[:id])
  39. 39. What’s this do? #3 @to_user = @target_db::User.find(params[:id]) User.find(params[:id])
  40. 40. What’s this do? #3 @to_user = @target_db::User.find(params[:id]) User.find(params[:id]) @to_user.update(@from_db::User.find(params[:id]))
  41. 41. What’s this do? #3 @to_user = @target_db::User.find(params[:id]) User.find(params[:id]) @to_user.update(@from_db::User.find(params[:id])) # Copies a User from one database to another # see Magic Multi-Connection # http://magicmodels.rubyforge.org
  42. 42. Magic Multi-Connections @db::User.find(params[:id])
  43. 43. MMC - alternate ideas @db::User.find(params[:id]) # but I would have preferred... User.find(params[:id]).from_db(@db)
  44. 44. MMC - alternate ideas @db::User.find(params[:id]) # but I would have preferred... User.find(params[:id]).from_db(@db) # but we’d need FindProxies # like AssociationProxies
  45. 45. But what does this mean? @db::User
  46. 46. class creator helper class Module def create_class(class_name, superclass = Object, &block) klass = Class.new superclass, &block self.const_set class_name, klass end end >> module Connection; end >> Connection.create_class 'User' => Connection::User
  47. 47. class creator helper class Module def create_class(class_name, superclass = Object, &block) klass = Class.new superclass, &block self.const_set class_name, klass end end >> module Connection; end >> Connection.create_class 'User' => Connection::User @db::User
  48. 48. But...the class... “when to create it?” When you ask for it!
  49. 49. const_missing class Module alias :old_const_missing :const_missing def const_missing(const_id) return old_const_missing(const_id) rescue nil target_class = quot;::#{const_id}quot;.constantize rescue nil raise NameError.new(quot;bad constant #{const_id}quot;) unless target_class create_class(const_id, target_class) end end
  50. 50. const_missing class Module alias :old_const_missing :const_missing def const_missing(const_id) return old_const_missing(const_id) rescue nil target_class = quot;::#{const_id}quot;.constantize rescue nil raise NameError.new(quot;bad constant #{const_id}quot;) unless target_class create_class(const_id, target_class) end end magic multi-connections
  51. 51. Picks up root classes class Person; end module Remote establish_connection :other end >> Remote::Person.superclass => Person
  52. 52. const_missing class Module def const_missing(const_id) return old_const_missing(class_id) rescue nil table_name = DrNicMagicModels::Schema.models[const_id] raise NameError.new(quot;bad constant #{const_id}quot;) unless table_name create_class(class_id, ActiveRecord::Base) do set_table_name table_name end end end “Does the class name match to a table name?”
  53. 53. const_missing class Module def const_missing(const_id) return old_const_missing(class_id) rescue nil table_name = DrNicMagicModels::Schema.models[const_id] raise NameError.new(quot;bad constant #{const_id}quot;) unless table_name create_class(class_id, ActiveRecord::Base) do set_table_name table_name end end dr nic’s magic models end “Does the class name match to a table name?”
  54. 54. const_missing >> U00FC => quot;üquot;
  55. 55. const_missing def const_missing(const) if const.to_s =~ /^((U( [0-9ABCEF][0-9A-F]{3} # general BMP | D[0-7][0-9A-F]{2} # excluding surrogates | [1-9A-F][0-9A-F]{4} # planes 1-15 | 10 [0-9A-F]{4} # plane 16 ) )* ) $/ix unescaped = $1.split(/[Uu]/)[1..-1].collect do |hex| hex.to_i(16) end.pack('U*')
  56. 56. Sexy Syntax Convenient Code
  57. 57. Enjoy En ! drnicwilliams.com by Dr Nic

×