Ruby is Awesome

1,679 views
1,550 views

Published on

Overview of the ruby language and bits of awesome it gives to developers

Published in: Technology

Ruby is Awesome

  1. 1. Ruby is Awesome Vitaly Kushner astrails.com
  2. 2. Interesting Times
  3. 3. Fast, Good, or Cheap. Pick two.
  4. 4. Fast, Good, or Cheap. Pick two.
  5. 5. Change Environment
  6. 6. Choose Language
  7. 7. Good Enough
  8. 8. You can do almost anything with any language
  9. 9. http://www.flickr.com/photos/jantik/254695220/
  10. 10. Paul Graham http://paulgraham.com/
  11. 11. Blub Language Paradox
  12. 12. BASIC was“Good Enough”
  13. 13. No Recursion?!
  14. 14. Beating the averages http://paulgraham.com/avg.html
  15. 15. Lisp is good
  16. 16. Ruby is Better! :-)
  17. 17. Ruby is Practical
  18. 18. Ruby is an acceptable LISP
  19. 19. History
  20. 20. History• Yukihiro Matsumoto (aka "Matz")
  21. 21. History• Yukihiro Matsumoto (aka "Matz")• Released in 1993
  22. 22. History• Yukihiro Matsumoto (aka "Matz")• Released in 1993• Got known in US about 2000
  23. 23. History• Yukihiro Matsumoto (aka "Matz")• Released in 1993• Got known in US about 2000• Gained momentum around 2003-2005
  24. 24. LISP +Smalltalk + Python + Perl
  25. 25. • Simple consistent syntax• Dynamically typed• Late binding• Single Inheritance with Mixin support
  26. 26. • Everything is an object• Closures• Garbage Collection• Multi platform
  27. 27. Ruby is Awesome
  28. 28. Blocks
  29. 29. array.each { |e| puts }array.each { |e| puts e} array.each do |e| puts eend
  30. 30. map {|x| ...}collect {|x| ...}select {|x| ...}reject {|x| ...}find {|x| ...}any? {|x| ...}all? {|x| ...}sort {|a, b| ...}
  31. 31. File.readlines("foobar.dat").map{|l| l.strip}.sort {|x,y| x[-1] <=> y[-1]}.map {|x| x.upcase}.to_xml
  32. 32. File.readlines("foobar.dat").map{|l| l.strip}.sort {|x,y| x[-1] <=> y[-1]}.map {|x| x.upcase}.to_xml File.readlines("foobar.dat")
  33. 33. File.readlines("foobar.dat").map{|l| l.strip}.sort {|x,y| x[-1] <=> y[-1]}.map {|x| x.upcase}.to_xml File.readlines("foobar.dat") .map {|line| line.strip}
  34. 34. File.readlines("foobar.dat").map{|l| l.strip}.sort {|x,y| x[-1] <=> y[-1]}.map {|x| x.upcase}.to_xml File.readlines("foobar.dat") .map {|line| line.strip} .sort {|x,y| x.to_i <=> y.to_i}
  35. 35. File.readlines("foobar.dat").map{|l| l.strip}.sort {|x,y| x[-1] <=> y[-1]}.map {|x| x.upcase}.to_xml File.readlines("foobar.dat") .map {|line| line.strip} .sort {|x,y| x.to_i <=> y.to_i} .map {|x| x.upcase}
  36. 36. File.readlines("foobar.dat").map{|l| l.strip}.sort {|x,y| x[-1] <=> y[-1]}.map {|x| x.upcase}.to_xml File.readlines("foobar.dat") .map {|line| line.strip} .sort {|x,y| x.to_i <=> y.to_i} .map {|x| x.upcase} .to_xml
  37. 37. class Hash def to_html_attributes map { |k, v| k + =" + v + " }.join( ) endend
  38. 38. attrs = { :src => "foo.img", :width => 100, :height => 200, :class => "avatar"}attrs.to_html_attributes=> class="avatar" height="200" width="100" src="foo.img"
  39. 39. Closures
  40. 40. class Array def has_any_bigger_then(x) any? {|e| e > x} endend
  41. 41. def incrementor(x) proc {|y| y + x}endinc1 = incrementor(1)inc5 = incrementor(5)
  42. 42. def incrementor(x) proc {|y| y + x}endinc1 = incrementor(1)inc1.call(1)=> 2inc1.call(3)=> 4
  43. 43. def incrementor(x) proc {|y| y + x}endinc5 = incrementor(5)inc5.call(1)=> 6inc5.call(3)=> 8
  44. 44. # rubydef paidMore(amount) proc {|e| e.salary > amount}end// C#public Predicate<Employee>PaidMore(int amount) { return delegate(Employee e) { return e.Salary > amount; }}
  45. 45. Meta Programming
  46. 46. Extending the language to create new abstractions
  47. 47. Monkey Patching
  48. 48. class Hash def to_html_attributes map { |k, v| k + =" + v + " }.join( ) endend
  49. 49. attrs = { :src => "foo.img", :width => 100, :height => 200, :class => "avatar"}attrs.to_html_attributes=> class="avatar" height="200" width="100" src="foo.img"
  50. 50. 3.megabytes=> 3145728
  51. 51. 10.months.from_now=> Thu Aug 12 03:25:40 0300 2010
  52. 52. 5.minutes.ago=> Mon Oct 12 03:21:02 0200 2009
  53. 53. Duck Typing
  54. 54. .to_s [1, 2, 3].to_s=> "123"
  55. 55. .to_s123.to_s=> "123"
  56. 56. .to_s"123".to_s=> "123"
  57. 57. "a#{b}c""a" + b.to_s + c
  58. 58. method_missing
  59. 59. NoMethodError
  60. 60. User.find_by_company("Astrails")
  61. 61. User.find_by_name_and_company( "Vitaly Kushner", "Astrails")
  62. 62. Multiple inheritance is evil :-)
  63. 63. Modules a.k.a. Mixins
  64. 64. module FlyHome def set_home @home = position end def fly_home fly(@home) endend
  65. 65. class Bird < Living include FlyHome def fly(direction) ... def position ...endclass Airplane < Machine include FlyHome def fly(direction) ... def position ...end
  66. 66. DSLDomain Specific Language
  67. 67. class User < ActiveRecord::Base has_many :projects belongs_to :account has_many :reports, :class_name => "User", :foreign_key => :reports_to_id named_scope :activated, :conditions => "activated_at IS NOT NULL" validates_uniqueness_of :name validates_presence_of :crypted_password validates_acceptance_of :terms_of_serviceend
  68. 68. safe do local { path "/backup/:kind" } s3 :key => YOUR_S3_KEY, :secret => YOUR_S3_SECRET, :bucket => S3_BUCKET, :path => ":kind/" gpg :password => "foobar" mysqldump do options "-ceKq --single-transaction --create-options" user "astrails" password "foobar" database :blog database :astrails_com do skip_tables :request_logs end end tar do archive "etc-files" do files "/etc" end endend
  69. 69. safe do local { path "/backup/:kind" } s3 :key => YOUR_S3_KEY, :secret => YOUR_S3_SECRET, :bucket => S3_BUCKET, :path => ":kind/" gpg :password => "foobar" ...end
  70. 70. safe do ... mysqldump do options "-ceKq ..." user "astrails" password "foobar" database :blog database :astrails_com do skip_tables :request_logs end end ...end
  71. 71. safe do ... tar do archive "etc-files" do files "/etc" end endend
  72. 72. Rubygems
  73. 73. ➜

~

✗
sudo
gem
install
astrails‐safeSuccessfully
installed
astrails‐safe‐0.2.31
gem
installedInstalling
ri
documentation
for
astrails‐safe‐0.2.3...Installing
RDoc
documentation
for
astrails‐safe‐0.2.3...➜

~

✗

  74. 74. Rubyforge.org 8,426 gems
  75. 75. Github.com7,483 gems
  76. 76. Rails
  77. 77. http://www.flickr.com/photos/ecstaticist/2589723846/
  78. 78. Rails is Fun
  79. 79. optimized forprogrammers happiness and sustainable productivity
  80. 80. MVC
  81. 81. MVC• Model
  82. 82. MVC•Model• View
  83. 83. MVC•Model• View• Controller
  84. 84. convention over configuration
  85. 85. class User < ActiveRecord::Baseend
  86. 86. User.find(123)=> SELECT * FROM `users` WHERE (`users`.`id` = 123)
  87. 87. class User < ActiveRecord::Base belongs_to :account has_many :projectsendclass UsersController < ApplicationController def show @user = User.find(params[:user]) render :template => show endend
  88. 88. class User < ActiveRecord::Base belongs_to :account has_many :projectsendclass UsersController < ApplicationController def show @user = User.find(params[:user]) render :template => show endend
  89. 89. class User < ActiveRecord::Base belongs_to :account has_many :projectsendclass UsersController < ApplicationController def show @user = User.find(params[:user]) endend
  90. 90. Models
  91. 91. http://www.flickr.com/photos/slyadnev/3959052112/
  92. 92. ActiveRecord
  93. 93. ORMObject-Relational Mapping
  94. 94. • MySQL• PostgreSQL• MSSql• SQLite• Oracle• ODBC
  95. 95. Associationsclass User < ActiveRecord::Base has_many :projectsendclass Project < ActiveRecord::Base belongs_to :userend
  96. 96. u = User.find(1)=> SELECT * FROM `users` WHERE (`users`.`id` = 1)
  97. 97. u.projects.count=> SELECT count(*) AS count_all FROM `projects` WHERE(`projects`.user_id = 1)
  98. 98. User.find_by_first_name("Vitaly", :include => :projects)=> SELECT * FROM `users` WHERE (`users`.`first_name` = Vitaly) LIMIT 1=> SELECT `projects`.* FROM `projects` WHERE (`projects`.user_id = 1)
  99. 99. Project.first.user=> SELECT * FROM `projects` LIMIT 1=> SELECT * FROM `users` WHERE (`users`.`id` = 1)
  100. 100. Callbacks• before_validation• before_validation_on_create• after_validation• after_validation_on_create• before_save• before_create• after_create• after_save
  101. 101. Callbacks• before_validation• before_validation_on_create• after_validation• after_validation_on_create• before_save• before_create• after_create• after_save
  102. 102. class User < ActiveRecord::Base before_save :encrypt_password attr_accessor :password private def enctypt_password unless password.blank? self.salt ||= ActiveSupport::SecureRandom.hex(40) self.crypted_password = encrypt(password, salt) end endend
  103. 103. Validations
  104. 104. validates_presence_of :account_idvalidates_uniqueness_of :namevalidates_numericality_of :ccvdef validate unless name[0] == ?A errors.add(:name, "Names must start with an A") endend
  105. 105. ActiveSupport
  106. 106. 3.days.ago[1,2,3].to_json2.weeks.from_nowhash.slice(:foo, :bar)
  107. 107. Controllers
  108. 108. http://www.flickr.com/photos/benjamin-nagel/2902721172/
  109. 109. class UsersController < ApplicationController def index @users = User.paginate(:page => params[:page], :per_page => 20) endend
  110. 110. http://yourdomain/users/index
  111. 111. http://localhost:3000/users/index
  112. 112. ActionController::Routing::Routes.draw do |map| map.connect :controller/:action/:id map.connect :controller/:action/:id.:formatend
  113. 113. ActionController::Routing::Routes.draw do |map| map.connect :controller/:action/:id map.connect :controller/:action/:id.:formatend /welcome/home :controller => welcome :action => home
  114. 114. ActionController::Routing::Routes.draw do |map| map.connect :controller/:action/:id map.connect :controller/:action/:id.:formatend /users/edit/123 :controller => users :action => edit :id => 123
  115. 115. ActionController::Routing::Routes.draw do |map| map.connect :controller/:action/:id map.connect :controller/:action/:id.:formatend projects/show/456.xml :controller => projects :action => show :id => 456 :format => xml
  116. 116. /talks/create?title=ruby%20is%20awsome&author[name]=Vitaly&author[company]=Astrails { :controller => talks, :action => create, :title => "ruby is awesome", :user => { :name => Vitaly, :company => Astrails } }
  117. 117. REST
  118. 118. RESTRepresentational State Transfer
  119. 119. • HEAD• GET• POST• PUT• DELETE
  120. 120. • index • edit• new • update• create • destroy• show
  121. 121. indexGET /users
  122. 122. newGET /users/new
  123. 123. createPOST /users
  124. 124. showGET /users/123
  125. 125. editGET /users/123/edit
  126. 126. updatePUT /users/123
  127. 127. destroyDELETE /users/123
  128. 128. map.resources :users do |user| user.resources :projectsend GET /users/123/projects :controller => :projects, :action => :index, :user_id => 123 GET /users/123/projects/456 :controller => :projects, :action => :show, :user_id => 123, :id => 456
  129. 129. users_path=> /users
  130. 130. new_user_path=> /users/new
  131. 131. user_path(user)=> /users/123
  132. 132. edit_user_path(user)=> /users/123/edit
  133. 133. Filters
  134. 134. class UsersController < ApplicationController before_filter :login_required, :except => [:new, :create] before_filter :find_user, :only => [:edit, :show, :update, :destroy] def index ... def new ... def create ... def edit ... def show ... def update ... def destroy ...protected def login_required ... def find_user ...end
  135. 135. class UsersController < ApplicationController before_filter :login_required, :except => [:new, :create] ...protected def login_required unless session[:user_id] redirect_to "/login" end endend
  136. 136. class UsersController < ApplicationController before_filter :find_user, :only => [:edit, :show, :update, :destroy] ...protected def find_user @user = User.find(params[:id]) endend
  137. 137. def index @users = User.paginate( :page => params[:page], :per_page => 20)end
  138. 138. def new @user = User.new(params[:user])end
  139. 139. def create @user = User.new(params[:user]) if @user.save flash[:notice] = "User created" redirect_to user_path(@user) else flash.now[:error] = "Failed to create a user" render :action => "new" endend
  140. 140. def editend
  141. 141. def showend
  142. 142. def update if @user.update_attributes(params[:user]) flash[:notice] = "User updated" redirect_to user_path(@user) else flash.now[:error] = "Failed to update user" render :action => "edit" endend
  143. 143. def destroy if @user.destroy flash[:notice] = "User deleted" else flash[:error] = "Failed to delete user" end redirect_to users_pathend
  144. 144. Errors
  145. 145. class UsersController < ApplicationController before_filter :find_user, :only => [:edit, :show, :update, :destroy] ... def update if @user.update_attributes(params[:user]) ... end endprotected def find_user @user = User.find(params[:id]) endend
  146. 146. ActiveRecord::RecordNotFound
  147. 147. HTTP 404
  148. 148. HTTP 500
  149. 149. Session
  150. 150. Session Store• files• database• cookie
  151. 151. class UserSessionController < ApplicationController def create unless user = User.authenticate( params[:login], params[:password]) flash.now[:error] = "Login failed" render :action => :new return end session[:user_id] = user.id redirect_to user_path(user) endend
  152. 152. MVC
  153. 153. Views
  154. 154. http://www.flickr.com/photos/brapke/226101874/
  155. 155. def index @users = User.paginate( :page => params[:page], :per_page => 20)end
  156. 156. app/views/users/index.html.erb
  157. 157. ERB<p> Hello <%= h(@user.name) %></p>
  158. 158. ERB<ul>  <% @user.projects.each do |project| %>    <li><%= h(project.name) %></li>  <% end %><ul>
  159. 159. def index @users = User.paginate(...) respond_to do |format| # default action is to render index.html.erb format.html format.xml { @users.to_xml } format.json { @users.to_json } endend
  160. 160. HAML%p = @user.name%ul - @user.projects.each do |project| %li= project.name
  161. 161. Testing
  162. 162. TDD
  163. 163. TDDTest Driven Development
  164. 164. BDD
  165. 165. BDDBehavior Driven Development
  166. 166. Pressure
  167. 167. TATFT
  168. 168. TATFTTest All The Fucking Time
  169. 169. • Test::Unit• RSpec• Shoulda• Cucumber• Webrat
  170. 170. it "should be able to show media" do @media = stub_media Media.stub!(:find).and_return(@media) get :show, :id => @media.id response.should be_successend
  171. 171. Migrations
  172. 172. class CreateProjects < ActiveRecord::Migration def self.up create_table :projects do |t| t.integer :user_id, :null => false t.string :title, :null => false t.text :description, :null => false t.integer :budget, :null => false t.timestamps end end def self.down drop_table :projects endend
  173. 173. Plugins
  174. 174. inherited_resourfcestandard implementation for the 7 REST actions
  175. 175. acts_as_treedb schema and methods for storing trees
  176. 176. country_selectHTML helper to present a select box of countries
  177. 177. Rails Scales?
  178. 178. http://www.flickr.com/photos/pinksherbet/223270526/
  179. 179. Rails Scales? YES
  180. 180. LinkedIn’s “Bumper Sticker” More then billion page views per month
  181. 181. if you have noscalability problems you are not growing fast enough!
  182. 182. Ruby is Awesome Q &A Vitaly Kushner astrails.com

×