2. “A Programmer’s Best Friend”
3 Keywords:
Dynamic...
Ruby executes at runtime many common behaviors
that other languages might perform during
Reflective...
Ruby can observe (do type introspection) and modify
its own structure and behavior at runtime.
Humanity...
Ruby is designed for programmer productivity and
fun.
3. Snippet #1. OO
# The Greeter class
class Greeter
def initialize(name)
@name = name.capitalize
end
def salute
puts "Hello #{@name}!"
end
end
# Create a new object
g = Greeter.new("world")
4. Snippet #2.
Everything is an object
Applying an action on a number...
5.times do
print "We *love* Ruby -- it's outrageous!"
end
5. Snippet #3. Flexibility
Essential parts of Ruby can be removed or redefined,
at will...
class Numeric
def plus(x)
self.+(x)
end
end
y = 5.plus 6
# y is now equal to 11
6. Snippet #4. closures
creating a function together with a referencing
environment for the non-local variables of that
# In an object instance variable (denoted with '@'), remember
a block.
def remember(&a_block)
@block = a_block
end
# Invoke the above method, giving it a block which takes a
name.
remember {|name| puts "Hello, #{name}!"}
# When the time is right (for the object) -- call the
closure!
@block.call("Jon")
# => "Hello, Jon!"
7. Snippet #5. closures (cont.)
creating a function together with a referencing
environment for the non-local variables of that
def create_set_and_get(initial_value=0) # Note the default value of 0
closure_value = initial_value
return Proc.new {|x| closure_value = x}, Proc.new { closure_value }
end
setter, getter = create_set_and_get # ie. returns two values
setter.call(21)
getter.call # => 21
#You can also use a parameter variable as a binding for the closure.
#So the above can be rewritten as...
def create_set_and_get(closure_value=0)
return proc {|x| closure_value = x } , proc { closure_value }
end
8. Snippet #6. anonymous func
4 ways to create a function (or a subroutine) defined,
and possibly called, without being bound to an
proc {|arg| print arg}
Proc.new {|arg| print arg}
lambda {|arg| print arg}
->(arg) {print arg}
9. Snippet #7. meta-programming
writing of programs that write or manipulate other
programs (or themselves) as their data...
COLORS = { :black => "000",
:red => "f00",
:green => "0f0",
:yellow => "ff0",
:blue => "00f",
:magenta => "f0f",
:cyan => "0ff",
:white => "fff" }
class String
COLORS.each do |color,code|
define_method "in_#{color}" do
"<span style="color: ##{code}">#{self}</span>"
end
end
end
"Hello, World!".in_blue
=> "<span style="color: #00f">Hello, World!</span>"
10. Snippet #8. meta-prog (cont)
specialising the behaviour of methods called on a
specific object...
a = 'foo'
class << a #opens up a’s singleton class (eigenclass)
def inspect
'"bar"'
end
end
a.inspect # => "bar"
a = 'foo' # new object, new singleton class
a.inspect # => "foo"
"Hello, World!".in_blue
=> "<span style="color: #00f">Hello, World!</span>"
11. Snippet #9. eval
# Defining a class method with instance_eval
Fixnum.instance_eval { def ten; 10; end }
Fixnum.ten #=> 10
# Defining an instance method with class_eval
Fixnum.class_eval { def number; self; end }
7.number #=> 7
Fixnum.instance_eval treats Fixnum as an instance
(an instance of the Class class)
Fixnum.class_eval treats Fixnum as a class and
executes the code in the context of that class
12. Snippet #10. eval (cont.)
class BindingTest
def initialize(n)
@value = n
end
def getBinding
return binding() # using Kernel#binding
end
end
obj1 = BindingTest.new(10)
binding1 = obj1.getBinding
obj2 = BindingTest.new(“Binding Test”)
binding2 = obj2.getBinding
puts eval(“@value”, binding1) #=> 10
puts eval(“@value”, binding2) #=> Binding Test
puts eval(“@value”) #=> nil
14. “Often people, especially computer engineers, focus on the machines. They
think, "By doing this, the machine will run faster. By doing this, the machine will
run more effectively. By doing this, the machine will something something
something." They are focusing on machines. But in fact we need to focus on
humans, on how humans care about doing programming or operating the
application of the machines. We are the masters. They are the slaves.
”
15. Rails
2 Keywords:
Convention over Configuration...
a developer only needs to specify unconventional
aspects of the application.
Don't repeat yourself...
information is located in a single, unambiguous
place..
16. how to build a blog engine in
15 minutes with Ruby on Rails
17. Rails was created in 2003 by
David Heinemeier Hansson
I made that!
In 2005 he was
recognized by Google
and O'Reilly with the
Hacker of the Year
award for his creation
of Ruby on Rails.
Hansson appeared on
the cover of the July
2006 issue of Linux
Journal
18. “ Flexibility is not free. It’s overrated. And if you trade that
flexibility in for some constraints, you get a lot of complexity
removed from the equation, you get a lot of productivity back
from all the stuff you don’t have to do.”
24. module Rails
autoload :Info, 'rails/info'
autoload :InfoController, 'rails/info_controller'
class << self
def application
@@application ||= nil
end
def initialized?
@@initialized || false
end
...
end
end
25. module Rails
class Application < Engine
autoload :Bootstrap, 'rails/application/bootstrap'
autoload :Configuration, 'rails/application/configuration'
end
end
module Rails
module Generators
autoload :Actions, 'rails/generators/actions'
autoload :ActiveModel, 'rails/generators/active_model'
...
end
end
26. Railties
Railties is responsible for gluing all frameworks together.
Overall, it:
* handles the bootstrapping process for a Rails application;
* manages the +rails+ command line interface;
* and provides Rails generators core.
27. activesupport
A toolkit of support libraries and Ruby core extensions extracted
from the Rails framework. Rich support for multibyte strings,
internationalization, time zones, and testing.
http://rubygems.org/gems/activesupport
30. activesupport
Active Support is a collection of utility classes and standard library
extensions that were found useful for the Rails framework. These additions
reside in this package so they can be loaded as needed in Ruby projects
outside of Rails.
of great use to ruby learners!
31. actionpack
Action Pack is a framework for handling and responding to web
requests. It provides mechanisms for *routing* (mapping
request URLs to actions), defining *controllers* that implement
actions, and generating responses by rendering *views*, which
are templates of various formats. In short, Action Pack provides
the view and controller layers in the MVC paradigm.
http://rubygems.org/gems/actionpack
34. actionpack
* Action Dispatch, which parses information about the web request, handles
routing as defined by the user, and does advanced processing related to HTTP
such as MIME-type negotiation, decoding parameters in POST/PUT bodies,
handling HTTP caching logic, cookies and sessions.
* Action Controller, which provides a base controller class that can be
subclassed to implement filters and actions to handle requests. The result
of an action is typically content generated from views.
* Action View, which handles view template lookup and rendering, and provides
view helpers that assist when building HTML forms, Atom feeds and more.
Template formats that Action View handles are ERB (embedded Ruby, typically
used to inline short Ruby snippets inside HTML), and XML Builder.
35. activerecord
Active Record connects classes to relational database tables to establish an
almost zero-configuration persistence layer for applications. The library
provides a base class that, when subclassed, sets up a mapping between the new
class and an existing table in the database. In the context of an application,
these classes are commonly referred to as *models*. Models can also be
connected to other models; this is done by defining *associations*.
(c.f. mongoid)
http://rubygems.org/gems/activerecord
37. activerecord
Automated mapping between classes and tables, attributes and columns.
Associations between objects defined by simple class methods.
Aggregations of value objects.
Validation rules that can differ for new or existing objects.
Callbacks available for the entire life cycle (instantiation, saving, destroying, validating, etc.).
Observers that react to changes in a model.
Inheritance hierarchies.
Transactions
Reflections on columns, associations, and aggregations.
Database abstraction through simple adapters.
Logging support
Database agnostic schema management with Migrations.
38. activeresource
Active Resource (ARes) connects business objects and
Representational State Transfer (REST)
web services. It implements object-relational mapping for REST
web services to provide transparent
proxying capabilities between a client (ActiveResource) and a
RESTful service (which is provided by Simply RESTful routing
in ActionController::Resources).
http://rubygems.org/gems/activeresource
39. how it works
Model classes are mapped to remote REST resources
by Active Resource much the same way Active Record
maps model classes to databasetables. When a
request is made to a remote resource, a REST XML
request is generated, transmitted, and the result
received and serialized into a usable Ruby object.
40. models/product.rb
class Product < ActiveResource::Base
self.site = "http://localhost:3000"
end
models/post.rb
class Post < ActiveRecord::Base
def product
@product ||= Product.find(product_id) unless product_id.blank?
end
end
views/posts/edit.html.erb
<p>
<%= f.label :product_id %>
<%= f.collection_select :product_id, Product.find(:all), :id, :name %>
</p>
views/posts/show.html.erb
<% if @post.product %>
<strong><%=h @post.product.name %></strong>
<% end %>
41. Philosiphy
Active Resource attempts to provide a coherent
wrapper object-relational mapping for REST web
services. It follows the same philosophy as Active
Record, in that one of its prime aims is to reduce the
amount of code needed to map to these resources.
This is made possible by relying on a number of code-
and protocol-based conventions that make it easy for
Active Resourceto infer complex relations and
structures.
42. Philosophy (cont.)
class Person < ActiveResource::Base
self.site = "http://api.people.com:3000/"
end
<->
http://api.people.com:3000/people/
43. # Expects a response of
#
# <person><id type="integer">1</id><attribute1>value1</
attribute1><attribute2>..</attribute2></person>
#
# for GET http://api.people.com:3000/people/1.xml
#
ryan = Person.find(1)
44. # <person><first>Ryan</first></person>
#
# is submitted as the body on
#
# POST http://api.people.com:3000/people.xml
#
# when save is called on a new Person object. An empty response is
# is expected with a 'Location' header value:
#
# Response (201): Location: http://api.people.com:3000/people/2
#
ryan = Person.new(:first => 'Ryan')
ryan.new? # => true
ryan.save # => true
ryan.new? # => false
ryan.id # => 2
45. actionmailer
Action Mailer is a framework for designing email-service layers.
These layers are used to consolidate code for sending out
forgotten passwords, welcome wishes on signup, invoices for
billing, and any other use case that requires a written notification
to either a person or another system.
46. class Notifier < ActionMailer::Base
delivers_from 'system@loudthinking.com'
def welcome(recipient)
@recipient = recipient
mail(:to => recipient,
:subject => "[Signed up] Welcome #{recipient}")
end
end
47. message = Notifier.welcome # => Returns a Mail::Message object
message.deliver # => delivers the email
View!
Hello there,
Mr. <%= @recipient %>
Thank you for signing up!
48. and we get...
Date: Mon, 25 Jan 2010 22:48:09 +1100
From: system@loudthinking.com
To: david@loudthinking.com
Message-ID: <4b5d84f9dd6a5_7380800b81ac29578@void.loudthinking.com.mail>
Subject: [Signed up] Welcome david@loudthinking.com
Mime-Version: 1.0
Content-Type: text/plain;
charset="US-ASCII";
Content-Transfer-Encoding: 7bit
Hello there,
Mr. david@loudthinking.com
Thank you for signing up!
49. why directly calling on the class?
module ActionMailer #:nodoc:
class Base < AbstractController::Base
class << self
def method_missing(method, *args) #:nodoc:
return super unless respond_to?(method)
new(method, *args).message
end
end
end
end
50. Wait! where is activemodel?
activerecord relies on this!
activepack also relies on this!!!
A toolkit for building modeling frameworks like Active
Record and Active Resource. Rich support for
attributes, callbacks, validations, observers,
serialization, internationalization, and testing.
51. activemodel
Used to build mongoid,etc
as long as test is passed, free validation,callbacks,etc
provided
module ActiveModel
module Lint
module Tests
def test_model_naming
assert model.class.respond_to?(:model_name), "The model should respond to model_name"
model_name = model.class.model_name
assert_kind_of String, model_name
assert_kind_of String, model_name.human
assert_kind_of String, model_name.singular
assert_kind_of String, model_name.plural
end
...
end
52. all those are free
Add attribute magic to objects
Callbacks for certain operations
Tracking value changes
Adding +errors+ interface to objects
Model name introspection
Observer support
Making objects serializable
Internationalization (i18n) support
Validation support
Custom validators
55. Sinatra
small and flexible
NO MVC
"quickly creating web-applications in Ruby with
minimal effort."
require 'sinatra'
get '/hi' do
"Hello World!"
end
56. Nitro
it does not dictate
how a web
application should be
structured.
<select name="day">
<option for="day in 1..31" selected_if="day == Time.now.day">#{day}</option>
</select>
One could use templates with embedded code!
57. Camping
Camping is a web
application framework
written in Ruby which
consistently stays under
4kb - the complete
source code can be
viewed on a single page.
58. require "uri";require "rack";class Object;def meta_def m,&b;(class<<self;self
end).send:define_method,m,&b end end;module Camping;C=self;S=IO.read(__FILE__
)rescue nil;P="<h1>Camping Problem!</h1><h2>%s</h2>";U=Rack::Utils;O={};Apps=[]
class H<Hash;def method_missing m,*a;m.to_s=~/=$/?self[$`]=a[0]:a==[]?self[m.
to_s]:super end;undef id,type if ??==63 end;class Cookies<H;attr_accessor :_p;
def _n;@n||={}end;alias :_s :[]=;def set k,v,o={};_s(j=k.to_s,v);_n[j]=
{:value=>v,:path=>_p}.update o;end;def []=(k,v)set(k,v,v.is_a?(Hash)?v:{})end
end;module Helpers;def R c,*g;p,h=
/(.+?)/,g.grep(Hash);g-=h;raise"bad route"unless u=c.urls.find{|x|break x if
x.scan(p).size==g.size&&/^#{x}/?$/=~(x=g.inject(x){|x,a|x.sub p,U.escape((a.
to_param rescue a))}.gsub(/(.)/){$1})};h.any?? u+"?"+U.build_query(h[0]):u
end;def / p;p[0]==?/?@root+p :p end;def URL c='/',*a;c=R(c,*a) if c.respond_to?(
:urls);c=self/c;c=@request.url[/.{8,}?(?=/|$)/]+c if c[0]==?/;URI c end end
module Base;attr_accessor:env,:request,:root,:input,:cookies,:state,:status,
:headers,:body;T={};L=:layout;def lookup n;T.fetch(n.to_sym){|k|t=Views.
method_defined?(k)||(t=O[:_t].keys.grep(/^#{n}./)[0]and Template[t].new{
O[:_t][t]})||(f=Dir[[O[:views]||"views","#{n}.*"]*'/'][0])&&Template.
new(f,O[f[/.(w+)$/,1].to_sym]||{});O[:dynamic_templates]?t:T[k]=t} end
def render v,*a,&b;if t=lookup(v);r,@_r=@_r,o=Hash===a[-1]?a.pop: {};s=(t==true)?mab{
send v,*a,&b}: t.render(self,o[:locals]||{},&b);s=render(L,o.merge(L=>false)){s
}if o[L]or o[L].nil?&&lookup(L)&&!r&&v.to_s[0]!=?_;s;else;raise"no template: #{v}"
end;end;def mab &b;(@mab||=Mab.new({},self)).capture(&b) end;def r s,b,h={};b,h=
h,b if Hash===b;@status=s;@headers.merge!(h);@body=b end;def redirect *a;r 302,
'','Location'=>URL(*a).to_s end;def r404 p;P%"#{p} not found"end;def r500 k,m,e
raise e end;def r501 m;P%"#{m.upcase} not implemented"end;def serve(p,c)
(t=Rack::Mime.mime_type p[/..*$/],nil)&&@headers["Content-Type"]=t;c;end;def to_a;@env[
'rack.session']=Hash[@state];r=Rack::Response.new(@body,@status,@headers)
@cookies._n.each{|k,v|r.set_cookie k,v};r.to_a end;def initialize env,m
r=@request=Rack:: Request.new(@env=env);@root,@input,@cookies,@state,@headers,
@status,@method=r.script_name.sub(//$/,''),n(r.params),Cookies[r.cookies],
H[r.session.to_hash],{},m=~/r(d+)/?$1.to_i: 200,m;@cookies._p=self/"/" end
def n h;Hash===h ?h.inject(H[]){|m,(k,v)|m[k]=
n(v);m}: h end;def service *a;r=catch(:halt){send(@method,*a)};@body||=r;self
end end;module Controllers;@r=[];class<<self;def R *u;r=@r;Class.
new{meta_def(:urls){u};meta_def(:inherited){|x|r<<x}}end;def D p,m,e;p='/'if
!p||!p[0];(a=O[:_t].find{|n,_|n==p}) and return [I,:serve,*a]
@r.map{|k|k.urls.map{|x|return(k.method_defined? m)?[k,m,*$~[1..-1]]:
[I, 'r501',m]if p=~/^#{x}/?$/}};[I,'r404',p] end;N=H.new{|_,x|x.downcase}.
merge!("N"=>'(d+)',"X"=>'([^/]+)',"Index"=>'');def M;def M;end;constants.
map{|c|k=const_get(c);k.send:include,C,X,Base,Helpers,Models
@r=[k]+@r if @r-[k]==@r;k.meta_def(:urls){["/#{c.to_s.scan(/.[^A-Z]*/).map(&
N.method(:[]))*'/'}"]}if !k.respond_to?:urls}end end;I=R()end;X=
Controllers;class<<self;def
goes m;Apps<<a=eval(S.gsub(/Camping/,m.to_s),TOPLEVEL_BINDING);caller[0]=~/:/
IO.read(a.set:__FILE__,$`)=~/^__END__/&&(b=$'.split /^@@s*(.+?)s*r?n/m).shift rescue nil
a.set :_t,H[*b||[]];end;def call e;X.M
p=e['PATH_INFO']=U.unescape(e['PATH_INFO']);k,m,*a=X.D p,e['REQUEST_METHOD'].
downcase,e;k.new(e,m).service(*a).to_a;rescue;r500(:I,k,m,$!,:env=>e).to_a end
def method_missing m,c,*a;X.M;h=Hash===a[-1]?a.pop: {};e=H[Rack::MockRequest.
env_for('',h.delete(:env)||{})];k=X.const_get(c).new(e,m.to_s);h.each{|i,v|k.
send"#{i}=",v};k.service(*a) end;def use*a,&b;m=a.shift.new(method(:call),*a,&b)
meta_def(:call){|e|m.call(e)}end;def options;O end;def set k,v;O[k]=v end end
module Views;include X,Helpers end;module Models;autoload:Base,'camping/ar'
Helpers.send:include,X,self end;autoload:Mab,'camping/mab'
autoload:Template,'camping/template';C end
59. == A Camping Skeleton module Blog::Views
def layout
A skeletal Camping blog could look like this:
html do
require 'camping' head { title "My Blog" }
body do
Camping.goes :Blog
h1 "My Blog"
module Blog::Models self << yield
class Post < Base; belongs_to :user; end end
class Comment < Base; belongs_to :user; end end
class User < Base; end
end end
module Blog::Controllers def index
class Index
@posts.each do |post|
def get
@posts = Post.find :all h1 post.title
render :index end
end end
end
end
end