Your SlideShare is downloading. ×
Intro to Ruby on Rails
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×
Saving this for later? Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime – even offline.
Text the download link to your phone
Standard text messaging rates apply

Intro to Ruby on Rails

1,535
views

Published on


0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total Views
1,535
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
7
Comments
0
Likes
0
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide
  • - configure the DB first – this can be against your instant rails locally or an external source
    -
  • - create a new project – from console or project
    - start with model objects
    - generate model product
    - note that it's a lowercase singular name
    pick apart the results
    note the plurilizaation and naming of objects, creation of fixtures, tests, etc.
    open up ojects and examine
  • depot> ruby script/console
    Loading development environment.
    >> Product.column_names
    ["id", "name", "address", "email", "pay_type"]
    Order.columns_hash["pay_type"]
  • Transcript

    • 1. Slide 1 Presentation Name January 25, 2007 Intro to Ruby on Rails ✔Goals of this session ✔Bring developers up to speed on Ruby on Rails basics ✔SVN basics and best practices ✔Review project governance ✔Review mock project and assign tasks
    • 2. Slide 2 Presentation Name January 25, 2007 MVC from 40,000 Feet ✔ The model is responsible for maintaining the state of the application. ✔ Sometimes the state is permanent and will be stored outside the application,often in a database. ✔ Sometimes transient, lasting a few iterations. ✔ Enforces all the business rules that apply to that data.
    • 3. Slide 3 Presentation Name January 25, 2007 MVC cont... ✔ The view is responsible for generating a user interface, normally based on data in the model. ✔ The view may present the user with various ways of inputting data, BUT the view itself NEVER handles incoming data. ✔ There may well be many views that access the same model data, often for different purposes.
    • 4. Slide 4 Presentation Name January 25, 2007 MVC cont... ✔ Controllers orchestrate the application. ✔ Controllers receive events from the outside world (normally user input), interact with the model, and display an appropriate view to the user.
    • 5. Slide 5 Presentation Name January 25, 2007
    • 6. Slide 6 Presentation Name January 25, 2007 How does it work in Rails? ✔ In a Rails application, incoming requests are first sent to a router. ✔ Router works out where in the application the request should be sent and how the request itself should be parsed. ✔ Router identifies a particular method (called an action) somewhere in the controller code. ✔ The action might look at data in the request, might interact with the model, might cause other actions to be invoked. ✔ The action prepares information for the view, which renders something to the user.
    • 7. Slide 7 Presentation Name January 25, 2007
    • 8. Slide 8 Presentation Name January 25, 2007 How do I create a RoR project? ✔ Make a project directory ✔ From the command line ­ 'rails my_app' ✔ From RadRails – new rails project ✔ Have a look at the project structure....
    • 9. Slide 9 Presentation Name January 25, 2007
    • 10. Slide 10 Presentation Name January 25, 2007
    • 11. Slide 11 Presentation Name January 25, 2007
    • 12. Slide 12 Presentation Name January 25, 2007 Logging in Rails ✔ logger.debug(“Argh!  Problems! {#var.name}”) ✔ logger.warn(“foo”) ✔ logger.info(“bar”) ✔ logger.error(“foobar”) ✔ logger.fatal(“ack!”)
    • 13. Slide 13 Presentation Name January 25, 2007 Using irb in Rails ✔ Run script/console ✔ pr = Product.find(:first) ✔ pr.price ✔ => 29.95 ✔ Lets you interrogate your objects and 'play with  them' in the sandbox
    • 14. Slide 14 Presentation Name January 25, 2007 Ruby refresher ✔ Local vars, method params, and method names – all  lowercase_with_underscores ✔ @instance_variables_names ✔ @@class_variable_names ✔ :symbol – string literals that are made into constants  – think of it as 'thing named' ­ :symbol is 'the thing  named symbol'
    • 15. Slide 15 Presentation Name January 25, 2007 Ruby refresher cont... ✔ Arrays = ['an','array','example','named'] ✔ Arrays << 'arrays' – will append to the end of an  array ✔ Hashes = {:foo => 'bar', :bar => 'foo'} ✔ Accessed same as array – Hashes[:foo] ✔ Special notice – hashes as parameter lists...
    • 16. Slide 16 Presentation Name January 25, 2007 Ruby refresher cont... ✔ Special note – ruby allows you to omit the braces on  hashes as parameter calls if the has is the last  parameter of the call... ✔ What's that mean? ✔ Redirect_to :action => 'show', :id => product.id ✔ Is the same as ✔ Params = {:action => 'show', :id = > product.id} ✔ redirect_to(params)
    • 17. Slide 17 Presentation Name January 25, 2007 Ruby refresher... ✔ Puts “writes to the console” ✔ H method is used to escape html
    • 18. Slide 18 Presentation Name January 25, 2007 Lets jump in! Fire up RadRails and start a project...
    • 19. Slide 19 Presentation Name January 25, 2007 Create a model object – product ✔exists app/models/ ✔exists test/unit/ ✔exists test/fixtures/ ✔create app/models/product.rb ✔create test/unit/product_test.rb ✔create test/fixtures/products.yml ✔exists db/migrate ✔create db/migrate/001_create_products.rb
    • 20. Slide 20 Presentation Name January 25, 2007 3 options when defining columns ✔ :null => true or false ✔ Adds not null constraint (if db supports) ✔ :limit => size ✔ Appends size string to column type def ✔ :default => value ✔ Sets default value
    • 21. Slide 21 Presentation Name January 25, 2007
    • 22. Slide 22 Presentation Name January 25, 2007
    • 23. Slide 23 Presentation Name January 25, 2007 What can migrations do? ✔ add_column/remove_column ✔ rename_column/rename_table ✔ change_column ✔ create_table/drop_table ✔ :force=>true will drop existing table before creating ✔ :temporary=>true creates temp table – will go away after  app disconnects ✔ :options=>”db secific options”
    • 24. Slide 24 Presentation Name January 25, 2007 What can migrations do? Cont.. ✔ add_index/remove_index ✔ Down method – deletes all rows ✔ Primary keys – what's the scoop. ✔ Rails assumes every table has a numeric primary key –  normally called id. ✔ Rails hates tables that don't have numeric primary keys,  but doesn't care what the column name is. ✔ Best practise?  Discuss with everyone else why you'd  want to use composite keys, etc before you do it.  Plugins  are available – but it's not fun (anectodal)
    • 25. Slide 25 Presentation Name January 25, 2007 When migrations attack! ✔ Tip: if you need to manually override the the schema  version that in the DB: ✔ ruby script/runner  'ActiveRecord::Base.connection.execute( "INSERT  INTO schema_info (version) VALUES(0)")' ✔ schema_info table is created in backgroun on first  migration run, and holds current migration number
    • 26. Slide 26 Presentation Name January 25, 2007 Run a migration ✔ Rake migrate – runs all ✔ Rake migrate VERSION=X ✔ Note: schema dump file
    • 27. Slide 27 Presentation Name January 25, 2007 Lets play with creating migrations.. ✔ Create 3 model objects – one for people, one for  groups, one for memberships  ✔ Give each model some fields using :string data types ✔ Run the migration to version 3 ✔ Roll back to 0 ✔ Run to version 3 (make mods if you care too) ✔ Can you find the schema dump file?  Have a look at  it...
    • 28. Slide 28 Presentation Name January 25, 2007 Active Record Basics ✔ What is Active Record? ✔ ORM (object­relational mapping) tool supplied with  Rails. ✔ Tables map to classes, rows to objects (model  objects), and columns to object attributes  (accessors/mutators) ✔ Different then others (ie. Hibernate) in that it ships  with sensible defaults – minimal config.
    • 29. Slide 29 Presentation Name January 25, 2007 Active Record Basics cont… ✔ When you create a subclass of ActiveRecord::Base  you create an object to wrap a DB table. ✔ Rails assumes that the name of the table is the  PLURAL FORM OF THE CLASS NAME ✔ Rails also assumes that if the name contains multiple  camel­case words that the table name has  underscores between the words. ✔ Inflector does handle some irregular plurals
    • 30. Slide 30 Presentation Name January 25, 2007
    • 31. Slide 31 Presentation Name January 25, 2007 Active Record Basics cont… ✔ Can change default behavior – but why? ✔ Active Record objects correspond to rows in a DB  table ✔ These objects have attributes that correspond to  table columns ✔ Notice we didn’t define these attributes in our model  objects? ✔ Active Record determines them dynamically at run
    • 32. Slide 32 Presentation Name January 25, 2007 Active Record Basics cont… ✔ Why’s this good?  Modify migration – re­run – test. ✔ Very agile.
    • 33. Slide 33 Presentation Name January 25, 2007
    • 34. Slide 34 Presentation Name January 25, 2007 Active Record Basics cont… ✔ Lets try playing a bit.
    • 35. Slide 35 Presentation Name January 25, 2007 Active Record Basics cont… ✔ Add some data to a table in your DB ✔ Open up corresponding model object in RR ✔ Generate ‘controller admin’ ✔ Open contrller object ✔ Scaffold :model_object_to_play_with ✔ Start mongrel ✔ Localhost:port/admin
    • 36. Slide 36 Presentation Name January 25, 2007 Active Record Basics cont… ✔ Hit localhost:port/admin ✔ Play around a bit….
    • 37. Slide 37 Presentation Name January 25, 2007 Active Record Basics cont… ✔ Now let’s get concrete…. ✔ Generate scaffold product admin ✔ Check force ✔ Watch your console! ✔ This is called a static scaffold. ✔ Two parameters – one for model, one for controller
    • 38. Slide 38 Presentation Name January 25, 2007 Active Record Basics cont… ✔ What did we just do? ✔ Took all that scaffolded code and made it ‘concrete’ ✔ Notice we didn’t overwrite our migration? ✔ You will have to be careful of model objects though  – can be overwritten. ✔ Go back to site – looks the same.
    • 39. Slide 39 Presentation Name January 25, 2007 Active Record Basics cont… ✔ Go into your new controller object.  Don’t focus on  what’s in there too much, but look for a ‘show’  method. ✔ Modify your Product.find(params[:id]) to  Product.find(1) ✔ Play
    • 40. Slide 40 Presentation Name January 25, 2007 Lets talk CRUD ✔ What’s crud?  Create Retrieve Update Delete ✔ Lets look at how to create data in Rails ✔ There’s a bunch of ways….lets go most manual to  least…and talk about why you’d use different ones.
    • 41. Slide 41 Presentation Name January 25, 2007 CREATE a_product = Product.new a_product.col_name = “blah" etc… a_product.save
    • 42. Slide 42 Presentation Name January 25, 2007 Product.new do |o|     o.col_name = “blah"     # . . .     o.save end
    • 43. Slide 43 Presentation Name January 25, 2007 a_product = Product.new( :col_name => “blah" , :col_name2 => “blah) an_order.save ­ notice it takes a hash of params?
    • 44. Slide 44 Presentation Name January 25, 2007 product = Product.new(params[:order]) Something to note with this way – could  miss params if there’s no column in DB!
    • 45. Slide 45 Presentation Name January 25, 2007 But what about our primary key? ✔ Connect to db – look at the table.   ✔ See the id column? ✔ Active record automatically creates a unique key  and sets the id as the row is saved. ✔ This column was created during migration
    • 46. Slide 46 Presentation Name January 25, 2007 READ ✔ Give Active Record criteria, it will return model  objects containing row data. ✔ For example: ✔ A_product = Product.find(1) ✔ What’s wrong with this?  Assumes we know a  product ID and that it exists.  Will throw a  RecordNotFound error. ✔ Try it!
    • 47. Slide 47 Presentation Name January 25, 2007 Read cont… ✔ # Get a list of product ids from a form ✔ product_list = params[:product_ids] ✔ Will return an array = number of id’s passed as  params. ✔ But what about SQL? ✔ Here’s where it get’s interesting.
    • 48. Slide 48 Presentation Name January 25, 2007 READ cont… ✔ product = Product.find(:all, :conditions =>  “something= ‘something' and something_else =  ‘something else'" ) ✔ Will return an array of matching rows, wrapped in a  Product object. ✔ If nothing comes back – array is empty. 
    • 49. Slide 49 Presentation Name January 25, 2007 READ cont.. ✔ But what about dynamic parameters? ✔ something = params[:something] ✔ pos = Order.find(:all, :conditions => ["name = ?" ,  name]) ✔ Side note – other ways of doing this – but this is  immune to sql injection attacks.
    • 50. Slide 50 Presentation Name January 25, 2007 ✔ something = params[:named_param] ✔ something_else = params[:another_named] ✔ pos = Product.find(:all, :conditions => [“something  = : something and something_else = :  something_else" ,{: something => something ,  :something_else => something_else}]) ✔ Just uses placeholders and takes values as hash ✔ What’s interesting about that?
    • 51. Slide 51 Presentation Name January 25, 2007 ✔ prod = Order.find(:all,:conditions => [“something  = :something and something_else =  :something_else" , params[:form_name]]) ✔ Can use hash values returned directly from our  form! ✔ As of 1.2 ✔ prod = Order.find(:all, :conditions =>  params[:order]) ✔ Careful with this though – matches ALL values
    • 52. Slide 52 Presentation Name January 25, 2007 Just a note on like clauses… ✔ User.find(:all, :conditions => ["name like ?" ,  params[:name]+"%" ]) ✔ You have to append the extra quotes around the  parameter.
    • 53. Slide 53 Presentation Name January 25, 2007 Power find()ing… ✔ Lets talk about find(:first,…) and find(:all,…) ✔ Pretty self explanitory – one is a select *, one selects  only one row ✔ Worth noting ­ :first doesn’t select all and return  first member of array ✔ Order not guaranteed so :first can be imprecise
    • 54. Slide 54 Presentation Name January 25, 2007 :conditions ✔ :conditions param lets you specify the condition  passed to sql where clause. ✔ Can be a string, array of sql and substitution vals, or   a hash.
    • 55. Slide 55 Presentation Name January 25, 2007 :order ✔ prod = Product.find(:all,:conditions => “something =  ‘something'" , :order => “name DESC" ) ✔ This is the only way to guarantee order.
    • 56. Slide 56 Presentation Name January 25, 2007 :limit ✔ Limit’s number of rows returned by find(:all,…) ✔ prod = Product.find(:all,:conditions => “something =  ‘something'" , :limit => 10,  :order => “name DESC"  )
    • 57. Slide 57 Presentation Name January 25, 2007 :offset ✔ prod = Product.find(:all, :order => “name DESC“,  :limit=page_size, :offset =>  page_number*page_size ) ✔ Think pagenation….
    • 58. Slide 58 Presentation Name January 25, 2007 :joins ✔ Can you use them? ✔ Yes. ✔ Should you use them? ✔ Not really. ✔ Active Record handles joins for you (later topic)
    • 59. Slide 59 Presentation Name January 25, 2007 :select ✔ prod = Product.find(:all, :select  =>  ‘a_column_name, another_column’,:conditions =>  “something = ‘something'" , :order => “name  DESC" ) ✔ Allows you to include columns from other tables –  can eliminate the need to run multiple child/parent  table queries.
    • 60. Slide 60 Presentation Name January 25, 2007 :readonly ✔ If set to true, the objects returned cannot be pushed  back into the DB. ✔ If you use :joins or :select options – this is  automatic!
    • 61. Slide 61 Presentation Name January 25, 2007 :from ✔ Overrides the table name in the select clause
    • 62. Slide 62 Presentation Name January 25, 2007 :group ✔ Applies grouping to data – on the DB. ✔ prod = Product.find(:all, :select => ‘name,  sum(some_column) as sum, :group => ‘name’" )
    • 63. Slide 63 Presentation Name January 25, 2007 Roll your own SQL ✔ NOT A BAD THING!  NOT DIRTY/EVIL! ✔ Sometimes for performance, or in odd situations this  is necessary! ✔ products = Product.find_by_sql(“blah”) ✔ Only attributes returned by a query are populated in  model object.
    • 64. Slide 64 Presentation Name January 25, 2007 Column stats… ✔ average = Product.average(:blah) # average amount  of orders ✔ max = Product.maximum(:blah) ✔ min = Product.minimum(:blah) ✔ total = Product.sum(:blah) ✔ number = Product.count ✔ All correstpond to aggregate functions in DB. ✔ All take same hash methods as regular find()
    • 65. Slide 65 Presentation Name January 25, 2007 Counting ✔ A bit different ✔ Takes zero, one, or two params ✔ Product.count – counts all ✔ Product.count “column_value > 10” ✔ Product.count hash_of_parameters_like_a => find
    • 66. Slide 66 Presentation Name January 25, 2007 Sortcut/dynamic find methods ✔ Product.find_by_column_name([params[‘form_valu e’]) ✔ Returns object matching a value from a named column ✔ Also comes in product_find_all_by_foo flavour
    • 67. Slide 67 Presentation Name January 25, 2007 Shortcut ✔ Product.find_or_create_by will call create on the  model class if  the finder would otherwise return nil ✔ Product.find_or_initialize will call new on the model  class if the finder would return nil ✔ Remember – CREATE SAVES data in db – new  creates in memory.
    • 68. Slide 68 Presentation Name January 25, 2007 UPDATE ✔ Similar to creating new model objects – can use  hashes, etc. ✔ Call save after will persist changes. ✔ Can also use the  Product.update_attribute() method  to update and save in a single call. ✔ Product.update_all() allows you to specify the set  and where clauses of update statement ✔ Product.update_all(“something = 1.1*something" ,  "title like '%Java%'" )
    • 69. Slide 69 Presentation Name January 25, 2007 UPDATE cont… ✔ save vs save! ✔ Save returns true if record saved – otherwise nil ✔ Save! Returns true if saved – otherwise throws  exception ✔ Create returns record object regardless if saved.   You must manually check for validation errors  (more on this later). ✔ Create! Returns record object if saved – raises error  if not.
    • 70. Slide 70 Presentation Name January 25, 2007 DELETE ✔ 2 kinds of row deletion – delete and delete_all ✔ Delete() takes single id or array of id’s and deletes  rows ✔ Delete_all() deletes rows matching a condition  (same format as finds) ✔ No exception is thrown if record not found.
    • 71. Slide 71 Presentation Name January 25, 2007 Delete cont… ✔ There’s also another 2 class­level destruction  methods – destroy() and destrol_all() ✔ Same as delete, but first read corresponding rows  into model objects, deletes rows, and freezes the  model objects. 
    • 72. Slide 72 Presentation Name January 25, 2007 Misc… ✔ Magic column names ✔ Created_at, created_on, updated_at, updated_on ✔ Lock_version – will track row version numbers and perform  optimistic locking ✔ Type – used by single table inheritence to track row type (more on  this later) ✔ Id – primary key  ✔ XXX_id foregn key referance to table – named with plural form of  XXX ✔ Position – the position of the row in a list if acts_as_list is used ✔ Parent_id – reference to id of row’s parent if acts_as_tree is used
    • 73. Slide 73 Presentation Name January 25, 2007 Break?
    • 74. Slide 74 Presentation Name January 25, 2007 Active Record Relationships ✔ In Rails table relationships are expressed by linking  tables based on primary key values (except for  single­table inheritance – more later) ✔ Active Record converts foreign key relationships to  high level interobject mappings.
    • 75. Slide 75 Presentation Name January 25, 2007 Active Record cont.. ✔ Handles 3 basic relationships: ✔ One row in table A is associated with 0 or 1 rows in table  B ✔ One row in table A is associated with X rows in table B ✔ X rows in table A are associated with X rows in table B  
    • 76. Slide 76 Presentation Name January 25, 2007 Active Record cont… ✔ Active record needs some help when it comes to  intertable relationships.   ✔ This is minimal however. ✔ In migrations where we create tables ­ create foreign  key references using format XXX_id ✔ Note:  you may want to put foreign_id constraints in  as well – but rails doesn’t explicitly need them ✔ Be aware of pluralization as well!  Foreign key  references are singular.
    • 77. Slide 77 Presentation Name January 25, 2007 Active Record – Join tables def self.up create_table :products do |t| t.column :title, :string # ... end create_table :categories do |t| t.column :name, :string # ... end create_table :categories_products, :id => false do |t| t.column :product_id, :integer t.column :category_id, :integer end # Indexes are important for performance if join tables grow big add_index :categories_products, [:product_id, :category_id] add_index :categories_products, :category_id end
    • 78. Slide 78 Presentation Name January 25, 2007 Active Record… ✔ Rails assumes that a join table is named after the two  tables it joins (with the names in alphabetical order). ✔ If you use another name you have to add a  declaration ✔ Note the join table doesn’t need an it column  because the combination of product and category is  unique.
    • 79. Slide 79 Presentation Name January 25, 2007 Relationships in Models ✔ Active Record has 3 types of relationships between  tables: ✔ One­to­one ✔ One­to­many ✔ Many­to­many
    • 80. Slide 80 Presentation Name January 25, 2007 Relationships in Models ✔ These are indicated by adding declarations to our  models: ✔ has_one ✔ has_many ✔ belongs_to ✔ has_many_and_belongs_to_many
    • 81. Slide 81 Presentation Name January 25, 2007 One­to­one note: the model for the table that contains the foreign key *always*  has the belongs_to declaration
    • 82. Slide 82 Presentation Name January 25, 2007 One­to­many
    • 83. Slide 83 Presentation Name January 25, 2007 Many­to­many Many­to­many associations are symmetrical—both of the joined tables declare their  association with each other using habtm.
    • 84. Slide 84 Presentation Name January 25, 2007 Relationships in Models ✔ You can override foreign / primary key assumptions  by passing belongs_to a hash of options after  association name: belongs_to :paid_order,  :class_name => "Order" , :foreign_key => "order_id" , :conditions => "paid_on is not null"
    • 85. Slide 85 Presentation Name January 25, 2007 Belongs_to methods ✔ product(force_reload=false)    Return the associated product (or nil if no associated product exists) The result  is cached, and the database will not be queried again when this association is  subsequently used unless true is passed as a parameter. ✔ product=obj Associate this line item with the given product, setting the product_id column in  this line item to the product’s primary key. If the product has not been saved, it  will be when the line item is saved, and the keys will be linked at that time.
    • 86. Slide 86 Presentation Name January 25, 2007 Belongs_to methods cont… ✔ build_product(attributes={}) Construct a new product object, initialized using the given attributes.  This line  item will be linked to it. The product will not yet have been saved. ✔ create_product(attributes={}) Build a new product object, link this line item to it, and save the product.
    • 87. Slide 87 Presentation Name January 25, 2007 Lets see that in action…first the models class Product < ActiveRecord::Base has_many :line_items end class LineItem < ActiveRecord::Base belongs_to :product end
    • 88. Slide 88 Presentation Name January 25, 2007 Now the code… item = LineItem.find(2) # item.product is the associated Product object puts "Current product is #{item.product.id}" puts item.product.title item.product = Product.new(:title => "Rails for Java Developers" , :description => "..." , :image_url => "http://....jpg" , :price => 34.95, :available_at => Time.now) item.save! puts "New product is #{item.product.id}" puts item.product.title
    • 89. Slide 89 Presentation Name January 25, 2007 What was the result? Current product is 1 Programming Ruby New product is 2 Rails for Java Developers
    • 90. Slide 90 Presentation Name January 25, 2007 Big deal?  Sure! ✔ We used the methods product and product= that we  generated in the LineItem class to access and modify  the project object. ✔ Active Record took care of the details! It saved the  product changes when we saved the line item – and  then linked them.
    • 91. Slide 91 Presentation Name January 25, 2007 How else could we have done it? item.create_product(:title => "Rails Recipes" , :description => "..." , :image_url => "http://....jpg" , :price => 32.95, :available_at => Time.now) ✔ However using create – we don’t have to save the  product.
    • 92. Slide 92 Presentation Name January 25, 2007 Has_one ✔ Declares that a given class is a child of this class. ✔ What’s that mean? ✔ The table corresponding to the child class will have  a foreign key reference containing the declaration.
    • 93. Slide 93 Presentation Name January 25, 2007 Example… class Order < ActiveRecord::Base has_one :invoice End
    • 94. Slide 94 Presentation Name January 25, 2007 Same methods as belongs_to ✔ So we could write… order = Order.new(... attributes ...) invoice = Invoice.new(... attributes ...) order.invoice = invoice ✔ If no child row exists for a parent row, the has_one  association will be set to nil (which in Ruby is  treated as false).  Which means….
    • 95. Slide 95 Presentation Name January 25, 2007 if order.invoice print_invoice(order.invoice) end ✔ Is valid code!  If you assign a new object to a  has_one association, the existing object will be  updated to remove its foregh key assocation with the  parent (key set to nill).  This orphans records!
    • 96. Slide 96 Presentation Name January 25, 2007 Example…
    • 97. Slide 97 Presentation Name January 25, 2007 Options for has_one ✔ Can modify defaults  with a has of options – the  same as belongs_to – but there’s also additional: :dependent => :destroy (or true) The child row is destroyed at the time the parent row is  destroyed. :dependent => :nullify The child row is orphaned at the time the parent row is  destroyed. This is done by setting the child row’s foreign key  to null
    • 98. Slide 98 Presentation Name January 25, 2007 Options for has_one cont… :dependent => false (or nil) The child row is not updated or deleted when the parent  is destroyed. (If you have defined foreign key constraints  between the child and parent tables, using this option  might lead to a constraint being violated when the parent  row is deleted.)
    • 99. Slide 99 Presentation Name January 25, 2007 Has_many ✔ Defines an attribute that behaves like a collection of  the child objects.
    • 100. Slide 100 Presentation Name January 25, 2007 Has_many cont… ✔ You can access the children as an array, find  particular children, and add new children. order = Order.new params[:products_to_buy].each do |prd_id, qty| product = Product.find(prd_id) order.line_items << LineItem.new(:product => product, :quantity => qty) end order.save
    • 101. Slide 101 Presentation Name January 25, 2007 Something to note… ✔ The append operator (<<) normally just appends a  new item to an array.   ✔ Thanks to Active Record – it also arranges to link  the line item back to the order by setting the foreign  key to the order_id. ✔ Also the line items will be saved when the parent  order is saved.
    • 102. Slide 102 Presentation Name January 25, 2007 Has_many cont… ✔ You can also iterater over childer nof a has_many  relationship. order = Order.find(123) total = 0.0 order.line_items.each do |li| total += li.quantity * li.unit_price end
    • 103. Slide 103 Presentation Name January 25, 2007 Has_many methods… ✔ As with has_many, you can modify AR’s defaults  with a hash of options – the :class_name,  :foreign_key, and :conditions are the same as  has_one ✔ :dependant can take the values :destroy, :nullify, and  false – they mean the same as has_one but they  apply to *all* child rows. ✔ :dependant can also take :delete_all – removes all  rows if parent is destroyed.
    • 104. Slide 104 Presentation Name January 25, 2007 :destroy vs :delete_all ✔ :dependant => :destroy traverses child table calling  destroy on each rows with a foreign key reference. ✔ :dependant => :delete_all will cause child rows to be  deleted in a single SQL statement – faster! ✔ However ­ :delete_all is only appropriate if the child  table is only used by the parent table, and has no  hooks that it uses to perform any actions on deletion  (more on these later).
    • 105. Slide 105 Presentation Name January 25, 2007 Has_many methods cont… ✔ You can override the sql that AR uses to fetch and  count child rows using :finder_sql and :counter_sql ✔ Child records can also be ordered using the :order  symbol
    • 106. Slide 106 Presentation Name January 25, 2007 Has_many methods…new.. orders(force_reload=false) Returns an array of orders associated with this customer (which may be empty if there is none). The result is cached, and the database will not be queried again if orders had previously been fetched unless true is passed as a parameter. orders <<order Adds order to the list of orders associated with this customer. orders.push(order1, ...) Adds one or more order objects to the list of orders associated with this customer. concat is an alias for this method.
    • 107. Slide 107 Presentation Name January 25, 2007 Has_many methods…new..cont.. orders.replace(order1, ...) Replaces the set of orders associated with this customer with the new set. Detects the differences between the current set of children and the new set, optimizing the database changes accordingly. orders.delete(order1, ...) Removes one or more order objects from the list of orders associated with this customer. If the association is flagged as :dependent => :destroy or :delete_all, each child is destroyed. Otherwise it sets their customer_id foreign keys to null, breaking their association. orders.delete_all Invokes the association’s delete method on all the child rows.
    • 108. Slide 108 Presentation Name January 25, 2007 Has_many methods…new..cont.. orders.destroy_all Invokes the association’s destroy method on all the child rows. orders.clear Disassociates all orders from this customer. Like delete, this breaks the association but deletes the orders from the database only if they were marked as :dependent. orders.find(options...) Issues a regular find call, but the results are constrained to return only orders associated with this customer. Works with the id, the :all, and the :first forms. orders.length Forces the association to be reloaded and then returns its size.
    • 109. Slide 109 Presentation Name January 25, 2007 Has_many methods…new..cont.. orders.count(options...) Returns the count of children. If you specified custom finder or count SQL, that SQL is used. Otherwise a standard Active Record count is used, constrained to child rows with an appropriate foreign key. Any of the optional arguments to count can be supplied. orders.size If you’ve already loaded the association (by accessing it), returns the size of that collection. Otherwise returns a count by querying the database. Unlike count, the size method honors any :limit option passed to has_many and doesn’t use finder_sql. orders.empty? Equivalent to orders.size.zero?.
    • 110. Slide 110 Presentation Name January 25, 2007 Has_many methods…new..cont.. orders.sum(options...) Equivalent to calling the regular Active Record sum method on the rows in the  association. Note that this works using SQL functions on rows in the database  and not by iterating over the in­memory collection. orders.uniq Returns an array of the children with unique ids. orders.build(attributes={}) Constructs a new order object, initialized using the given attributes and linked to  the customer. It is not saved. orders.create(attributes={}) Constructs and saves a new order object, initialized using the given attributes and  linked to the customer.
    • 111. Slide 111 Presentation Name January 25, 2007 IT’S CONFUSING! ✔ Chances are in the future this will be cleaned up… we’re only in version 1.2!
    • 112. Slide 112 Presentation Name January 25, 2007 HABTM ✔ Has_and_belongs_to_many – acts a log like  has_many. ✔ Adds the ability to add information to the join table  when you associate two objects (though this is  somewhat frowned on). ✔ Remember – the join table is not a AR object!
    • 113. Slide 113 Presentation Name January 25, 2007 Thoughts on join tables… ✔ Join tables should be pure – they should contain  only a pair of foreign key columns. ✔ IF you need to add more info – what you’re doing is  creating a NEW model object ✔ Lets compare two situations…and see what that  means to us.
    • 114. Slide 114 Presentation Name January 25, 2007 Example setup…
    • 115. Slide 115 Presentation Name January 25, 2007 Model code… class Article < ActiveRecord::Base has_and_belongs_to_many :users # ... end class User < ActiveRecord::Base has_and_belongs_to_many :articles # ... end
    • 116. Slide 116 Presentation Name January 25, 2007 What could we do… # Who has read article 123? article = Article.find(123) readers = article.users # What has Christian read? christian = User.find_by_name(“Christian" ) articles_that_christian_read = christian.articles # How many times has each user read article 123 counts = Article.find(123).users.count(:group => "users.name" )
    • 117. Slide 117 Presentation Name January 25, 2007 What could we do…cont… ✔ When our app notices someone read an article, it links their  user record with it – do this with an instance method… class User < ActiveRecord::Base has_and_belongs_to_many :articles # This user just read the given article def just_read(article) articles << article end # ... end
    • 118. Slide 118 Presentation Name January 25, 2007 Lets be frank though… ✔ There’s many situations where you want additional  data about the relationship.   ✔ This is where another model object is useful
    • 119. Slide 119 Presentation Name January 25, 2007 Model objects as join tables.
    • 120. Slide 120 Presentation Name January 25, 2007 Code for this… class Article < ActiveRecord::Base has_many :readings end class User < ActiveRecord::Base has_many :readings end class Reading < ActiveRecord::Base belongs_to :article belongs_to :user end
    • 121. Slide 121 Presentation Name January 25, 2007 So we could then … ✔ When a user reads an article….we can record that  info… reading = Reading.new reading.rating = params[:rating] reading.read_at = Time.now reading.article = current_article reading.user = session[:user] reading.save
    • 122. Slide 122 Presentation Name January 25, 2007 Ahhh…..but… ✔ We can’t really ask an article who it’s readers are, or  ask a user what articles they’ve read. ✔ Bring in the :through option!
    • 123. Slide 123 Presentation Name January 25, 2007 Lets see that again… class Article < ActiveRecord::Base has_many :readings has_many :users, :through => :readings end class User < ActiveRecord::Base has_many :readings has_many :articles, :through => :readings end
    • 124. Slide 124 Presentation Name January 25, 2007 Nice! ✔ The :through option tells Rails that a table can be  used to navigate from X to X table using a join  table!
    • 125. Slide 125 Presentation Name January 25, 2007 Example? an_article = Article.find(123) readers = an_article.users ✔ Rails constructs the SQL to return all the user rows  referenced from the readers table, where readers  rows reference the original article…
    • 126. Slide 126 Presentation Name January 25, 2007 Huh?  How? ✔ Nominates the association to navigate through in the  original model class – so… class Article < ActiveRecord::Base has_many :readings has_many :users, :through => :readings end ✔ Tells AR to use the has_many :readings to find a model called  reading. ✔ The name we give the association (:users) then tells AR with  attribute to use to look up the users (user_id)
    • 127. Slide 127 Presentation Name January 25, 2007 However.. ✔ You can change the attribute used to do lookup using the  :source symbol.  Why?  Better name signifigance… ✔ We were calling poepl who’d read articles :users because  that’s the object they were named…however… class Article < ActiveRecord::Base has_many :readings has_many :readers, :through => :readings, :source => :user end ✔ We can name them readers using :source.  Pretty.
    • 128. Slide 128 Presentation Name January 25, 2007 But wait there’s more! ✔ This essentially a has_many declaration so it accepts all of  the has_many parameters! class Article < ActiveRecord::Base has_many :readings has_many :readers, :through => :readings, :source => :user has_many :happy_users, :through => :readings, :source => :user, :conditions => 'readings.rating >= 4' end
    • 129. Slide 129 Presentation Name January 25, 2007 Removing duplicates… ✔ In this example we have the potiential for non­ unique results – what if someone read an article  more then once? ✔ :unique => true will remove the duplicates – this  relies on AR to do the removal however. ✔ :select => “distinct users.*” is a hack that will do the  distinct on the DB
    • 130. Slide 130 Presentation Name January 25, 2007 Creating new associations.. user = User.create(:name => "dave" ) article = Article.create(:name => "Join Models" ) article.users << user #(<< is refered to as push) ✔ BOTH sides must have been previously saved for this to  work. article = Article.create(:name => "Join Models" ) article.users.create!(:name => "dave" ) ✔ Same as prevous, but will create a row at the far end of the  association.  Can’t set attributes in the intermediate table  like this.
    • 131. Slide 131 Presentation Name January 25, 2007 Finder methods…(AKA Extensions) user = User.find(some_id) user.articles.find(:all, :conditions => [ 'rating >= ?' , 3]) ✔ Breaks ecapsulation – we want to keep this kind of business logic in the model class User < ActiveRecord::Base has_many :readings has_many :articles, :through => :readings do def rated_at_or_above(rating) find :all, :conditions => ['rating >= ?' , rating] end end end
    • 132. Slide 132 Presentation Name January 25, 2007 Allows is to then say… user = User.find(some_id) good_articles = user.articles.rated_at_or_above(4) ✔ Though I’m only showing it for the :through option – you  can do this with any association.   ✔ But if we want to share the extension?  Put it in a Ruby  module, and pass the module to the association using  :extend has_many :articles, :extend => RatingFinder has_many :articles, :extend => [ RatingFinder, DateRangeFinder ]
    • 133. Slide 133 Presentation Name January 25, 2007 Joining to multiple tables… ✔ Tricky subject…however… ✔ Rails provides two ways of doing multiple table  joins – single­table inheritance, and polymorphic  associations.
    • 134. Slide 134 Presentation Name January 25, 2007 Single table inheritance. ✔ When we program we use inheritance to express  relationships between abstractions. ✔ For example – people – can have various roles  (customer, employee, manager, etc). ✔ All roles will have properties in common, and  properties specific to that role. ✔ We might model this using subclasses – which  inherit the properties of their parent.  Nothing new.
    • 135. Slide 135 Presentation Name January 25, 2007 In the DB world however… ✔ There’s no concept of inheritance – relationships are  expressed as associations. ✔ BUT – single­table inheritance pattern maps all  classes in the hierarchy into a single table. ✔ This table contains a column for each of the  attripbues of all of the classes in the hierarchy, and a  column that (by convention) is named TYPE. ✔ Type identifies the particular class of object.
    • 136. Slide 136 Presentation Name January 25, 2007
    • 137. Slide 137 Presentation Name January 25, 2007 So how do we do this in AR? ✔ Define the hierarchy in your model classes, and  ensure that the table corresponding to the base class  of the hierarchy contains columns for all attributes,  as well as a type column. ✔ Don’t forget – some columns will contain null  values!
    • 138. Slide 138 Presentation Name January 25, 2007 Migration example… create_table :people, :force => true do |t| t.column :type, :string # common attributes t.column :name, :string t.column :email, :string # attributes for type=Customer t.column :balance, :decimal, :precision => 10, :scale => 2 # attributes for type=Employee t.column :reports_to, :integer t.column :dept, :integer # attributes for type=Manager # ­ none ­ end
    • 139. Slide 139 Presentation Name January 25, 2007 Model objects… class Person < ActiveRecord::Base end class Customer < Person end class Employee < Person belongs_to :boss, :class_name => "Employee" , :foreign_key =>  :reports_to end class Manager < Employee end
    • 140. Slide 140 Presentation Name January 25, 2007 So lets put in some data… Customer.create(:name => 'John Doe' , :email => "john@doe.com" , :balance => 78.29) wilma = Manager.create(:name => 'Wilma Flint' , :email => "wilma@here.com" , :dept => 23) Customer.create(:name => 'Bert Public' , :email => "b@public.net" , :balance => 12.45) barney = Employee.new(:name => 'Barney Rub' , :email => "barney@here.com" , :dept => 23) barney.boss = wilma barney.save!
    • 141. Slide 141 Presentation Name January 25, 2007 No lets retrieve some data… manager = Person.find_by_name("Wilma Flint" ) puts manager.class  #=> Manager puts manager.email  #=> wilma@here.com puts manager.dept  #=> 23 customer = Person.find_by_name("Bert Public" ) puts customer.class  #=> Customer puts customer.email   #=> b@public.net puts customer.balance   #=> 12.45
    • 142. Slide 142 Presentation Name January 25, 2007 Whoa…notice that? ✔ We uses the base class Person to do the query, but it  returned an object Manager, and an object  Customrer… ✔ AR looks at the type column of the row and created  the appropriate object!  Yay AR! ✔ Notice also the use of belongs_to :boss to create an  attribute that uses the reports_to column
    • 143. Slide 143 Presentation Name January 25, 2007 There’s a few constraints though… ✔ Two subclasses can’t have attributes with the same  name but different types – they’d map to the same  column in the schema.  Duh. ✔ You shouldn’t access the attribute ‘type’ directly –  instead access it by creating objects of the right class  or via the models indexing interface…like this person[:type] = 'Manager'
    • 144. Slide 144 Presentation Name January 25, 2007 Polymorpic Associations.. ✔ One major bummer of Single Table Inheritance is that  theres…well…a single table that contains all the info for  your subclasses. ✔ What’s polymorphism again? a mechanism that lets you  abstract the essence of something’s interface regardless of  its underlying implementation. ✔ In Rails, a polymorphic association is an association that  links to objects of different types. The assumption is that  these objects all share some common characteristics but that  they’ll have different representations.
    • 145. Slide 145 Presentation Name January 25, 2007 A migration…lets create ‘Resource’ create_table :articles, :force => true do |t| t.column :content, :text end create_table :sounds, :force => true do |t| t.column :content, :binary end create_table :images, :force => true do |t| t.column :content, :binary end
    • 146. Slide 146 Presentation Name January 25, 2007 This won’t work…. class Article < ActiveRecord::Base has_one :catalog_entry end class Sound < ActiveRecord::Base has_one :catalog_entry end class Image < ActiveRecord::Base has_one :catalog_entry end
    • 147. Slide 147 Presentation Name January 25, 2007 Why not? ✔ When we say has_one :catalog_entry in a model, it  means that the catalog_entries table has a foreign  key reference back to our table. ✔ here we have three tables each claiming to have_one  catalog entry: we can’t possibly arrange to have the  foreign key in the catalog entry point back to all  three tables... ✔ ….without polymorphic associations
    • 148. Slide 148 Presentation Name January 25, 2007 The trick… ✔ Use two columns in our catalog entry for the foreigh  key.   ✔ One to hold the id of the target, one to tell AR what  model that key’s in. ✔ If we call the foreigh key for our catalog entry  ‘resource’ we need 2 columns – ‘resource_id’ and  ‘resource_type’
    • 149. Slide 149 Presentation Name January 25, 2007 So lets add… create_table :catalog_entries, :force => true do |t| t.column :name, :string t.column :acquired_at, :datetime t.column :resource_id, :integer t.column :resource_type, :string end
    • 150. Slide 150 Presentation Name January 25, 2007 Now we can add.. ✔ The AR model for our catalog entry…we just have  to tell it that we’re using polymorpic associaton. class CatalogEntry < ActiveRecord::Base belongs_to :resource, :polymorphic => true end
    • 151. Slide 151 Presentation Name January 25, 2007 And now…. ✔ We can fix our AR models for our 3 types… class Article < ActiveRecord::Base has_one :catalog_entry, :as => :resource end class Sound < ActiveRecord::Base has_one :catalog_entry, :as => :resource End class Image < ActiveRecord::Base has_one :catalog_entry, :as => :resource end
    • 152. Slide 152 Presentation Name January 25, 2007 So if we wanted to try it… a = Article.new(:content => "This is my new article" ) c = CatalogEntry.new(:name => 'Article One' , :acquired_at =>  Time.now) c.resource = a c.save!
    • 153. Slide 153 Presentation Name January 25, 2007 What would be in the db? mysql> select * from articles; +­­­­+­­­­­­­­­­­­­­­­­­­­­­­­+ | id | content   | +­­­­+­­­­­­­­­­­­­­­­­­­­­­­­+ | 1 | This is my new article | +­­­­+­­­­­­­­­­­­­­­­­­­­­­­­+ mysql> select * from catalog_entries; +­­­­+­­­­­­­­­­­­­+­­­­­­­­­­­­­­­­­­­­­+­­­­­­­­­­­­­+­­­­­­­­­­­­­­­+ | id | name | acquired_at | resource_id | resource_type | +­­­­+­­­­­­­­­­­­­+­­­­­­­­­­­­­­­­­­­­­+­­­­­­­­­­­­­+­­­­­­­­­­­­­­­+ | 1 | Article One | 2006­07­18 16:48:29 | 1 | Article | +­­­­+­­­­­­­­­­­­­+­­­­­­­­­­­­­­­­­­­­­+­­­­­­­­­­­­­+­­­­­­­­­­­­­­­+
    • 154. Slide 154 Presentation Name January 25, 2007 You can… ✔ Access data from both sides of the relationship… article = Article.find(1) p article.catalog_entry.name  #=> "Article One" cat = CatalogEntry.find(1) resource = cat.resource p resource #=> #<Article:0x640d80  @attributes={"id"=>"1", # "content"=>"This is my new article"}>
    • 155. Slide 155 Presentation Name January 25, 2007 Acts as…. ✔ What if you want to manage data in a tree like  structure, or using list like behaviour (moving items  around in a certain order) ✔ You can use acts as list and acts as tree
    • 156. Slide 156 Presentation Name January 25, 2007 Acts as list ✔ Gives child data lists­like behavior. ✔ Parent will be able to traverse child data, move it  around and remove it. ✔ Implemented by assigning each child a position  number. ✔ Can be done in migration using ‘position’ or another  name (whe’d have to tell rails).
    • 157. Slide 157 Presentation Name January 25, 2007 Migration example… create_table :parents, :force => true do |t| end create_table :children, :force => true do |t| t.column :parent_id, :integer t.column :name, :string t.column :position, :integer end
    • 158. Slide 158 Presentation Name January 25, 2007 Model Example… class Parent < ActiveRecord::Base has_many :children, :order => :position End class Child < ActiveRecord::Base belongs_to :parent acts_as_list :scope => :parent_id end ✔ Note: without declaring :scope we’d have a global list for all entries in the  parent table.
    • 159. Slide 159 Presentation Name January 25, 2007 Add some data… parent = Parent.new %w{ One Two Three Four}.each do |name| parent.children.create(:name => name) end parent.save ✔ And then look at the contents of our list def display_children(parent) puts parent.children(true).map {|child| child.name }.join(", " ) end ✔ Note the ‘true’ we’re passing – that forces child object to be reloaded every time we  access it!
    • 160. Slide 160 Presentation Name January 25, 2007 Lets play with it… display_children(parent)  #=> One, Two, Three, Four puts parent.children[0].first?  #=> true two = parent.children[1] puts two.lower_item.name  #=> Three puts two.higher_item.name  #=> One parent.children[0].move_lower display_children(parent)  #=> Two, One, Three, Four parent.children[2].move_to_top display_children(parent)  #=> Three, Two, One, Four parent.children[2].destroy display_children(parent)  #=> Three, Two, Four
    • 161. Slide 161 Presentation Name January 25, 2007 Acts as tree… ✔ Provides support for organizing rows of a table into  tree structure. ✔ Achieved by adding parent_id column to the table  (default name).   ✔ This column is a foreign key reference back to same  table, linking child rows to parents
    • 162. Slide 162 Presentation Name January 25, 2007 Migration…and model… create_table :categories, :force => true do |t| t.column :name, :string t.column :parent_id, :integer End class Category < ActiveRecord::Base acts_as_tree :order => "name" end
    • 163. Slide 163 Presentation Name January 25, 2007 Add some data… root = Category.create(:name => "Books" ) fiction = root.children.create(:name => "Fiction" ) non_fiction = root.children.create(:name => "Non Fiction" ) non_fiction.children.create(:name => "Computers" ) non_fiction.children.create(:name => "Science" ) non_fiction.children.create(:name => "Art History" ) fiction.children.create(:name => "Mystery" ) fiction.children.create(:name => "Romance" ) fiction.children.create(:name => "Science Fiction" )
    • 164. Slide 164 Presentation Name January 25, 2007 How do we work with it? display_children(root)  # Fiction, Non Fiction sub_category = root.children.first puts sub_category.children.size  #=> 3 display_children(sub_category)  #=> Mystery, Romance, Science Fiction non_fiction = root.children.find(:first, :conditions => "name = 'Non Fiction'" ) display_children(non_fiction)  #=> Art History, Computers, Science puts non_fiction.parent.name  #=> Books ✔ Note: has same methods as has_many
    • 165. Slide 165 Presentation Name January 25, 2007 Saving things… ✔ Say we had a simple situation… class Order < ActiveRecord::Base has_one :invoice end class Invoice < ActiveRecord::Base belongs_to :order end
    • 166. Slide 166 Presentation Name January 25, 2007 So.. ✔ You can work from either side of the realtionship. ✔ Both are almost equivalent – except on how you  save objects to the database ✔ If you assign on object to a has_one association in  an exiting object, that associated object will be  automatically saved. order = Order.find(some_id) an_invoice = Invoice.new(...) order.invoice = an_invoice  # invoice gets saved
    • 167. Slide 167 Presentation Name January 25, 2007 However… ✔ If you assign a new object to a belongs_to  association – it will never be automatically saved… order = Order.new(...) an_invoice.order = order  # Order will not be saved here an_invoice.save  # both the invoice and the order get saved
    • 168. Slide 168 Presentation Name January 25, 2007 There’s a danger though… ✔ If a child row cannot be saved, AR will not  complain – you will get no indication that no child  rows was added.   ✔ To get around this –  invoice = Invoice.new # fill in the invoice invoice.save! an_order.invoice = invoice
    • 169. Slide 169 Presentation Name January 25, 2007 Preloading child rows… ✔ Normally AR does lazy­loading – this can result in  many, many queries if you say…iterated over a  recordset looking at child items. ✔ By using :include option in the find method the  whole whack of data’s retrieved in a single queries.
    • 170. Slide 170 Presentation Name January 25, 2007
    • 171. Slide 171 Presentation Name January 25, 2007 Validations ✔ Validations are methods to validate the contents of a  model object. ✔ If validation fails an object will not be written to the  db – it will be left in memory in its current state to  be passed back to the form for user correction. ✔ Validations can be performed on save, create, or  update.
    • 172. Slide 172 Presentation Name January 25, 2007 So… ✔ Most simple is using methods validate,  validate_on_create, or validate_on_update in your  model object. ✔ Can also run validation at any time by calling valid?  method.
    • 173. Slide 173 Presentation Name January 25, 2007 For example… class User < ActiveRecord::Base def validate unless name && name =~ /^w+$/ errors.add(:name, "is missing or invalid" ) end end def validate_on_create if User.find_by_name(name) errors.add(:name, "is already being used" ) end end end
    • 174. Slide 174 Presentation Name January 25, 2007 Note… ✔ Errors.add takes two parameters – the name of the  errored attribute, and a message ✔ If you have an error that applies to the whole form  use add_to_base method – takes a single message  parameter
    • 175. Slide 175 Presentation Name January 25, 2007 Validaton helpers… ✔ Most take on: and :message options. ✔ :on determines when the validation is applied (:save, :create,  :update) ✔ :message is used to override the default error message ✔ When validaton fails – helpers add an error object to AR  model object – associated with field being validated. ✔ These errors can be accessed by looking at the errors  attribute of the model object.
    • 176. Slide 176 Presentation Name January 25, 2007 Conditional validation ✔ All validations take an optional :if that identifies  code to be run. ✔ Can be a symbol, a string, or a Proc object.
    • 177. Slide 177 Presentation Name January 25, 2007 Default error messages ✔ Can be changed programattically. ✔ They’re stored in a hash, keyed on a symbol and can  be accessed as  ActiveRecord::Errors.default_error_messages
    • 178. Slide 178 Presentation Name January 25, 2007 Callbacks ✔ Allows you to write code to be involked at a  particualr part of the AF life cycle. ✔ There are 20 callbacks – 18 are before/after pairs – 2  are after_find and after_initialize (ruby constructor)  which have no before_XXX callbacks.
    • 179. Slide 179 Presentation Name January 25, 2007
    • 180. Slide 180 Presentation Name January 25, 2007 2 ways to define callbacks…. ✔ Directly: class Order < ActiveRecord::Base # .. def before_save self.payment_due ||= Time.now + 30.days end End
    • 181. Slide 181 Presentation Name January 25, 2007 Declaring callbacks cont… ✔ Using a handler… class Order < ActiveRecord::Base before_validation :normalize_credit_card_number after_create do |order| logger.info "Order #{order.id} created" end protected def normalize_credit_card_number self.cc_number.gsub!(/­w/, '' ) end end
    • 182. Slide 182 Presentation Name January 25, 2007 End for today.
    • 183. Slide 183 Presentation Name January 25, 2007
    • 184. Slide 184 Presentation Name January 25, 2007 Action Controller ✔ How do apps know what to do with incoming requests? ✔ Rails encodes this information in the request URL and uses a  subsystem called routing to determine what should be done ✔ rails command generates the initial set of files for an application.   Routing is configed in config/routes.rb ✔ string ’:controller/:action/:id’ acts as a pattern, matching against the  path portion of the request URL.
    • 185. Slide 185 Presentation Name January 25, 2007 Controller cont… ✔ Localhost:3001/store/product/12 in url ✔ Equals these parameters in your controller ­  @params = { :controller => 'store' ,:action =>  ‘product' ,:id => 123 } ✔ We won’t modify this for now – more on this during  RESTful rails
    • 186. Slide 186 Presentation Name January 25, 2007 Lets look… ✔ Open up your admin_controller and look at methods. ✔ Note:  All of these methods are public my default –  if you want to hide one – use hide_action :blah  BEFORE the method – same as using private in  ruby.
    • 187. Slide 187 Presentation Name January 25, 2007 So how does it work? ✔ a controller object processes a request ✔ it looks for a public instance method with the same name as the incoming action ✔ if it finds one, that method is invoked - if not, but the controller implements method_missing, that method is called, passing in the action name as the first parameter and an empty argument list ✔ If no method can be called, the controller looks for a template named after the current controller and action if found, this template is rendered ✔ If none of these things happen, an ‘Unknown Action’ error thrown
    • 188. Slide 188 Presentation Name January 25, 2007 Controller Environment ✔ Accessor methods are used to access important instance  variables ✔ Action_name – name of action being processed ✔ Cookies – cookies associated with requests (more later) ✔ Headers – hash of HTTP headers. ✔ Params – hash­like object containing request parameters  (and psudo­params used for routing).  Hash­like? You can  index entries using a symbol or string (ie params[:id] or  params[‘id’]
    • 189. Slide 189 Presentation Name January 25, 2007 Controller environment – cont.. ✔ Request – request object containing ✔ Domain – returns last 2 parts of domain name ✔ Remote_ip – may have more then 1 if behind proxy. ✔ Env – environment of the request – can access values set by  browser ✔ Method – returns request method ­ :delete, :get, :head:, :post, or  :put (more later) ✔ delete?, get?, head?, post?, and put? return true or false based on  the request method – do some of these look unfamiliar? ✔ xml_http_request? and xhr? return true if this request was issued  by one of the AJAX helpers – more on these later – Yum!
    • 190. Slide 190 Presentation Name January 25, 2007 Controller Environment cont… ✔ Response – response object filled in during the  handling of request (lots more on this later) ✔ Session – has­line object representing current  session data (more later)
    • 191. Slide 191 Presentation Name January 25, 2007 Responding to users ✔ Controllers respond to users in four basic ways ✔ Render a template – a template is a view.  Takes info  provided by controller and uses it to generate  response ✔ Return a string to the browser without rendering a  view – rarely used. ✔ Can return nothing – for example during AJAX calls ✔ Can return ‘other’ data to client (other then html)  like a pdf, or word document.
    • 192. Slide 192 Presentation Name January 25, 2007 Responding cont… ✔ Controllers always respond once – that means every  controller method only has one call to render, render_to, or  send_xxx – throws error otherwise. ✔ Because it must respond once, controller checks to see if a  response has been generated before it finishes handling a  request. ✔ If it finds no calls to the above methods – it looks for a  template named after the controller and action and renders it ✔ You can have multiple templates with the same name – but  different extensions. – ie. .rhtml, .rxml, .rjs. ✔ Searches in the order above for tempates.
    • 193. Slide 193 Presentation Name January 25, 2007 Templates ✔ A Template is a file that defines the content of a  response. ✔ 3 formats  ✔ rhtml – html with imbedded ruby ✔ Builder – a programmatic way of constructing xml  content ✔ Rjs – generates javascript. – VERY cool.
    • 194. Slide 194 Presentation Name January 25, 2007 Render method ✔ At the heart of rendering in Rails ✔ Takes a hash of options that tell it what to render  and how. ✔ For example:
    • 195. Slide 195 Presentation Name January 25, 2007 Ahhhhh! BAD! def update @user = User.find(params[:id]) if @user.update_attributes(params[:user])     render :action => show End     render :template => "fix_user_errors" end
    • 196. Slide 196 Presentation Name January 25, 2007 Why was that bad? ✔ Will throw an error as render was called twice where  update_attributes succeeds. ✔ Seems pretyt natural that calling render should  terminate the processing of an action.  This isn’t the  case!
    • 197. Slide 197 Presentation Name January 25, 2007 Lets look at some examples of render() usage ✔ Render() – with no parameters render method  renders the default template for the current  controller and action. class ThinController < ApplicationController def index render     #will render the index.rhtml, index.rjs, etc…templates end end
    • 198. Slide 198 Presentation Name January 25, 2007 So will these! class ThinController < ApplicationController def index end end ­­­­­­­­­­­­­­­­­­­­­ class ThinController < ApplicationController end
    • 199. Slide 199 Presentation Name January 25, 2007 render() cont…. Render(:text => string) ✔ Sends the given string to client.  Note – no html escaping  is performed! render(:inline =>string, [ :type  =>"rhtml"|"rxml"|"rjs" ], [ :locals =>hash] ) ✔ Interprets string as a template (with an optional  type) and renders it back to the client – using  :locals to set values of local variables in the  template – ummmmmm – why?
    • 200. Slide 200 Presentation Name January 25, 2007 How about this? class SomeController < ApplicationController if RAILS_ENV == "development" def method_missing(name, *args) render(:inline => %{ <h2>Unknown action: #{name}</h2> Here are the request parameters:<br/> <%= debug(params) %> }) end end end
    • 201. Slide 201 Presentation Name January 25, 2007 Render() cont… ✔ Render(:action=>action_name) ✔ Confuses some people when they start (like me!) ✔ Render(:action => :index) – will not render the action  method :index – will only render the default template! ✔ Don’t don’t don’t use this to do redirects. ✔ render(:file =>path, [ :use_full_path =>true|false],  [:locals =>hash) ✔ Renders the template in given path (which must include a  file extension), using full path if boolean set, and using  the :locals hash to set local vars in the template
    • 202. Slide 202 Presentation Name January 25, 2007 Renders…ya…more… ✔ Render(:template => name) ✔ Renders a template back to the client ­ :template value  must contain both controller and action parts of the new  name seperated by a slash ✔ Render (:partial => blah) ✔ Renders a partial template – more on these later ✔ Render(:xml = somexml) ✔ Sets content type to application/xml and renders as text
    • 203. Slide 203 Presentation Name January 25, 2007 Renders…..more more more.. ✔ render(:update) do |page| ... end ✔ Renders the block as an rjs template, passing in the page  object. render(:update) do |page| page[:cart].replace_html :partial => 'cart' , :object => @cart page[:cart].visual_effect :blind_down if @cart.total_items == 1 end ✔ More on this later!
    • 204. Slide 204 Presentation Name January 25, 2007 Render() things to note.. ✔ All forms of the render method take an optional  :status, :layout, and :content_type parameters ✔ :status is used to sets status header in HTTP response –  ie. ‘200 OK’ ✔ :layout determines whether the result of the rendering  should be wrapped in a layout.  If ‘true’ or ‘nil’ a layout  will be applied if there’s one associated with the current  action.  If :layout contains a string – it will be taken as  the name of the layout to use. ✔ :content_type lets you specify the Content­Type HTTP  header.
    • 205. Slide 205 Presentation Name January 25, 2007 Sending files and other data… ✔ Send_data(data, options) ✔ Sends a stream of data to the client.  The browser will  deal with this using contente type and disposition to  determine what to do with the data. def tax_recept pdf_data = Receipt.generate_receipt(userId) send_data(pdf_data, :type => “document/pdf”, :disposition => “inline”) end
    • 206. Slide 206 Presentation Name January 25, 2007 Send_data…cont… ✔ :disposition string Suggests to the browser that the  file should be displayed inline (option inline) or  downloaded and saved (option attachment, the  default) ✔ :filename string A suggestion to the browser of the  default filename to use when saving thisdata ✔ :status string The status code (defaults to "200 OK") ✔ :type string The content type, defaulting to  application/octet­stream
    • 207. Slide 207 Presentation Name January 25, 2007 Send_file() ✔ Pretty self explanitory – sends contents of a file! ✔ send_file(path, options...) def send_pdf_file send_file("/files/charity_signup.pdf" ) headers["Content­Description" ] = “Charity Signup" end
    • 208. Slide 208 Presentation Name January 25, 2007 Send_file cont… ✔ :buffer_size (number) The amount sent to the browser in each write if  streaming is enabled (:stream is true). ✔ :disposition (string) Suggests to the browser that the file should be  displayed inline (option inline) or downloaded and saved (option  attachment, the default). ✔ :filename (string) A suggestion to the browser of the default filename  to use when saving the file. If not set, defaults to the filename part of  path. ✔ :status (string) The status code (defaults to "200 OK"). ✔ :stream (true or false) If false, the entire file is read into server  memory and sent to the client. Otherwise, the file is read and written  to the client in :buffer_size chunks. ✔ :type (string) The content type, defaulting to application/octet­stream.
    • 209. Slide 209 Presentation Name January 25, 2007 Redirects – consider this… ✔ A redirect is send from a server to a client in  response to a request that can’t be handled. ✔ These redirects are handled by clients browser. ✔ The url changes , slight delay – as far as the  browsers concerned this is the same as typing a url  in
    • 210. Slide 210 Presentation Name January 25, 2007 class ReceiptController def display @receipt = Receipt.find(params[:id]) end def add_line @ receipt = Receipt.find(params[:id]) Line = Line.new(params[:comment]) @ receipt.lines << line if @ receipt.save flash[:note] = “Line added!" else flash[:note] = “Meh…we’re tossing that line." end # DO THIS?  NO! render(:action => 'display' ) end end
    • 211. Slide 211 Presentation Name January 25, 2007 What’s wrong with that? ✔ As far as the browsers concerned – the current URL  is still the one that was ‘recept/add_line’ ✔ That mean if the user hits refresh – the url will be  sent again to the app!   ✔ The user wants to refresh to see if the info was  added – they get a double entry!  Whoops! ✔ Appropriate response in these circumstances – use a  redirect_to
    • 212. Slide 212 Presentation Name January 25, 2007 def add_line @ receipt = Receipt.find(params[:id]) Line = Line.new(params[:comment]) @ receipt.lines << line if @ receipt.save flash[:note] = “Line added!" else flash[:note] = “Meh…we’re tossing that line." end # Mo betta redirect_to(:action => 'display' ) end
    • 213. Slide 213 Presentation Name January 25, 2007 Redirect_to forms… ✔ Redirect_to(path) ✔ Redirects to given path – if no http:// is given controller  will prepend current url and port ✔ Redirect_to(:action => …options…) ✔ Sends redirect to browser ✔ Redirect_to(:back) ✔ Redirects to url in the HTTP_REFERRER header in  request
    • 214. Slide 214 Presentation Name January 25, 2007 Cookies ✔ Rails abstracts cookies behind a nice interface. ✔ The cookies attribute is a hash­like object that  contains key/value pairs that are sent to the browser  when the request finishes processing, that are  available to subsuquent requests (with a few rules)
    • 215. Slide 215 Presentation Name January 25, 2007 Cookies class CookiesController < ApplicationController def action_one cookies[:the_time] = Time.now.to_s redirect_to :action => "action_two" end def action_two cookie_value = cookies[:the_time] render(:text => "The cookie says it is #{cookie_value}" ) end end
    • 216. Slide 216 Presentation Name January 25, 2007 Cookies…cont ✔ The value portion of  a cookie value must be a string ✔ There’s a few options for cookies – if you don’t set them  you get a default set of values – the cookie will apply to  whole site, expire when the browsers’s closed, and wil apply  to the domain of the host. ✔ options are :domain, :expires, :path, :secure, and :value. ✔ :domain and :path determine the relevence of a cookie – a  browser will only send back a cookie if the path and domain  match the request ✔ :secure ensures a cookie is only returned if request uses  https://
    • 217. Slide 217 Presentation Name January 25, 2007 Flash ✔ Used to communicate between actions ✔ When a redirect_to is called the browser generates a new  request, and the app will use a fresh instance of a controller. ✔ Instance variables are lost  ­ so we use flash ✔ Flash is temporary storage for values ✔ Available to requests processed immediately following the  current request ✔ Used most often for error and informational messages
    • 218. Slide 218 Presentation Name January 25, 2007 Remember this? def add_line @ receipt = Receipt.find(params[:id]) Line = Line.new(params[:comment]) @ receipt.lines << line if @ receipt.save flash[:note] = “Line added!" else flash[:note] = “Meh…we’re tossing that line." end # Mo betta redirect_to(:action => 'display' ) end
    • 219. Slide 219 Presentation Name January 25, 2007 Any idea where this file would be stored? <head> <title>My Blog</title> <%= stylesheet_link_tag("blog" ) %> </head> <body> <div id="main" > <% if flash[:note] ­%> <div id="notice" ><%= flash[:note] %></div> <% end ­%> <%= yield :layout %> </div> </body>
    • 220. Slide 220 Presentation Name January 25, 2007 Filters and Verification ✔ Filters allow you to write code in controllers that  wrap the processing performed by actions. ✔ 3 types of filter – before, after and round ✔ Can be run as methods in your controller, or passed  the controller object when run (Hey…isn’t that  using the dependancy injection pattern?)
    • 221. Slide 221 Presentation Name January 25, 2007 Filters…cont ✔ Before and after filters – pretty straightforward ✔ When a controller’s about to run an action – it  executes all filters on the ‘before’ chain, then the  action, then all the filters on the ‘after’ chain ✔ Can be passive or active
    • 222. Slide 222 Presentation Name January 25, 2007 class ApplicationController < ActionController::Base private def authorize unless User.find_by_id(session[:user_id]) flash[:notice] = "Please log in" redirect_to(:controller => "login" , :action => "login" ) end end End ­­­­­­­­­ class AdminController < ApplicationController before_filter :authorize # ....
    • 223. Slide 223 Presentation Name January 25, 2007 class AuditFilter def self.filter(controller) AuditLog.create(:action => controller.action_name) end End ­­­­­­­ class SomeController < ApplicationController before_filter do |controller| logger.info("Processing #{controller.action_name}" ) end after_filter AuditFilter # ... end
    • 224. Slide 224 Presentation Name January 25, 2007 Modifiers.. class BlogController < ApplicationController before_filter :authorize, :only => [ :delete, :edit_comment ] after_filter :log_access, :except => :rss # ...
    • 225. Slide 225 Presentation Name January 25, 2007 Around filters ✔ Wrap the execution of action ✔ Can be in two different styles  ✔ First – a single chunk of code that is called before the  action is executed.  The filter involkes yeald and the  action is executed.  When the action completes – filter  finishes executing ✔ Second – is really deprecated – so…don’t use it!
    • 226. Slide 226 Presentation Name January 25, 2007 Verification ✔ Allows you to set preconditions for action execution  more concisely then using a filter. verify  :only => :post_comment, :session => :user_id, :add_flash => { :note => "You must log in to comment" }, :redirect_to => :index ✔  Will apply verification to the post_comment action
    • 227. Slide 227 Presentation Name January 25, 2007 Verify parameters.. ✔ Come in 3 categories ✔ Applicability ✔ :only =>:name or [ :name, ... ] ✔ :except =>:name or [ :name, ... ] ✔ Tests ✔ :flash =>:key or [ :key, ... ] ✔ :method =>:symbol or [ :symbol, ... ] ✔ :params =>:key or [ :key, ... ] ✔ :session =>:key or [ :key, ... ] ✔ :xhr => true or false
    • 228. Slide 228 Presentation Name January 25, 2007 Params…cont ✔ Actions ✔ :add_flash =>hash ✔ :add_headers =>hash ✔ :redirect_to =>params ✔ :render =>params
    • 229. Slide 229 Presentation Name January 25, 2007 Break to play!
    • 230. Slide 230 Presentation Name January 25, 2007 Action View! ✔ So…what comes after the routing portion of our  app…. ✔ ActionView encapsuleates all the functionality  needed to render templates. ✔ The View part of MVC.
    • 231. Slide 231 Presentation Name January 25, 2007 Views.. ✔ When you write a view – you’re writing a template.   Something that will be dynamically expanded to generate  the final results. ✔ The render method (in controller) expects to find templates  under the directory defined by the ‘template_root’  configuration option. ✔ By default – this is the app/views directory. ✔ Under this directory the convention is to have a separate  subdirectory for the views of each controller.
    • 232. Slide 232 Presentation Name January 25, 2007 Views cont.. ✔ Each template is typically named after the actions it  supports – but they don’t have to be.
    • 233. Slide 233 Presentation Name January 25, 2007 Templates ✔ Templates contain fixed text and code used to add dynamic  content. ✔ Templates have access to the information set up by the  controller: ✔ All instance variables are available by default ✔ Controller objects flash, headers, logger, params, request,  response, and session are available – not usually good to use most  these but they’re really handy for debugging (using the debug()  method) ✔ The current controller object is available – including public  methods ✔ The path to the tempates base directory is also available
    • 234. Slide 234 Presentation Name January 25, 2007 3 types of templates ✔ Rxml – use the builder library to construct xml ✔ Rhtml – use html and embedded ruby to construct  html pages ✔ Rjs – create JavaScript to be executed in the  browser.  Typically used to interact with AJAXified  pages.
    • 235. Slide 235 Presentation Name January 25, 2007 Builder templates ✔ Builder is a library that lets you express structured text in  code (like xml).  For example… xml.div(:class => "productlist" ) do xml.timestamp(Time.now) @products.each do |product| xml.product do xml.productname(product.title) xml.price(product.price, :currency => "USD" ) end end end
    • 236. Slide 236 Presentation Name January 25, 2007 Renders… <div class="productlist" > <timestamp>Sun Oct 01 09:13:04 EDT 2006</timestamp> <product> <productname>Pragmatic Programmer</productname> <price currency="USD" >12.34</price> </product> <product> <productname>Rails Recipes</productname> <price currency="USD" >23.45</price> </product> </div>
    • 237. Slide 237 Presentation Name January 25, 2007 Builder cont.. ✔ Builder can generate almost any XML you need. ✔ Supports namespaces, processing instructions, and  XML comments.
    • 238. Slide 238 Presentation Name January 25, 2007 RHTML ✔ Basically html with dynamic content ✔ Dynamic content is wrapped in <%...%> tags – very  familiar ✔ The expressions inside the code can be arbitrary  code. ✔ There’s nothing wrong with putting code in a  template – just don’t put too much, and make sure  it’s not business logic!
    • 239. Slide 239 Presentation Name January 25, 2007 RHTML…cont The time <% @time = Time.now %> is <%= @time %> ✔ Vs The time <% @time = Time.now ­%> is <%= @time %> ✔ No gap between line and line 2 – the ‘­’ removes newline.
    • 240. Slide 240 Presentation Name January 25, 2007 Escaping… ✔ When you insert a value using <%...%> it goes  directly into the output stream. ✔ This means you can get URL­encoded  gobbledygook in your page ✔ Using the  h() method in your scriptliet code excapes  the se values.  Use it often, if not always.
    • 241. Slide 241 Presentation Name January 25, 2007 Helpers ✔ A helper is a module that contains methods that  assist a view.   ✔ They are output­centric and exist to output html (or  view code), and extend the behavior of a template. ✔ Why use them?  Easier to edit your RHTML, esier  to test your helper files. ✔ By default each controller gets a helper modlue in  the app/helpers directory.
    • 242. Slide 242 Presentation Name January 25, 2007 Example…. <h3><%= @page_title || “My charity" %></h3> ✔ This is a simple example, but if you had this code in  each template you begin to let duplication slip in.   ✔ Say you wanted to modify the title of your store to  ‘Chrismas Charity’! ✔ You have to edit every template.
    • 243. Slide 243 Presentation Name January 25, 2007 Instead you could… module CharityHelper def page_title @page_title || "Pragmatic Store" end End ­­­­ <h3><%= page_title %></h3> ✔ You could use a partial as well…but we’ll talk about  those later.
    • 244. Slide 244 Presentation Name January 25, 2007 Sharing Helpers… ✔ Sometimes its good to share helper code between  controllers. ✔ This can be done by putting code in your  application_helper.rb file – making it available to all  views ✔ OR by telling your controller to include additional  helper modules by calling helper :helper_name in  your controller.
    • 245. Slide 245 Presentation Name January 25, 2007 Build in helpers ✔ No surprise – rails comes with a ton of built in  helpers – too many to cover today – consult Rdoc  for details! ✔ They do however break down to a few categories –  ✔ Formatting helpers ✔ Debug helpers ✔ Text helpers ✔ To name a few!
    • 246. Slide 246 Presentation Name January 25, 2007 Linking in RHTML ✔ There’s a bunch of methods to allow you to  reference resources external to your template.   ✔ Link_to is the most common – creates a hyperlink to  another action in your app. <%= link_to "Add Line" , :action => “add_line" %> ✔ The first parameter is the link text, the second is the  link target.
    • 247. Slide 247 Presentation Name January 25, 2007 Linking cont… ✔ You can also add HTML attributes to your link using the  :class parameter <%= link_to "Delete" , { :action => "delete" , :id => @product}, { :class => "dangerous"} ✔ The 3rd  parameter also supports 3 additional options that  modify the links behavior. ✔ :confirm  ­ takes a message for a confirmation popup ✔ :popup ­ takes either true, or an array of window creation options  for a window.open call – displays a popup window ✔ :method – is a hack used for REST – we’ll cover this later.
    • 248. Slide 248 Presentation Name January 25, 2007 Button_to ✔ Same as link, but creates a button in a form.
    • 249. Slide 249 Presentation Name January 25, 2007 Conditional Linking ✔ Rails has methods that generate hyperlinks if  conditions are met, and just return the link text  otherwise ✔ link_to_if and link_to_unless ✔ Take a condition parameter followed by regular  parameters of link_to ✔ Link_to_unless_current is used to create links where the  current page name is shown as plain text and other  entries are hyperlinks (think a menu)
    • 250. Slide 250 Presentation Name January 25, 2007 Linking Cont.. ✔ Link_to methods also support absolute urls ✔ image_tag helper can be used to create image tags (I  won’t cover this much now) linked to images that  live under the /images directory ✔ If you don’t specify an extension it assumes .png ✔ You can combine link_to and image_tags ✔ <%= link_to(image_tag(“image.jpg”, :size => “50x22”), ….
    • 251. Slide 251 Presentation Name January 25, 2007 Linking cont… ✔ Mail_to helper creates a mailto: hyperlink ✔ Takes an email address, the name of thi link, and a  set of options ✔ :bcc, :cc, :body, :subject, and :encode => “javascript”  used to obscure the generated link to block spiders. ✔ The AssetTagHelper module contains helpers to link  to stylesheets and JavaScript code.
    • 252. Slide 252 Presentation Name January 25, 2007 Linking onward… ✔ <% stylesheet_link_tag “mystylesheet” %> ✔ <% javascript_include_tag :defaults %> ✔ Will load prototype.js, effects.js, dragdrop.js, and  controls.js – as well as application.js if you create one. ✔ <% auto_discovery_link_tag (:rss, :action =>  ‘rss_feed’ )%> ✔ There is a pagination helper – it’s hotly debated as to  weather or not it’s well written – I won’t really  cover it.
    • 253. Slide 253 Presentation Name January 25, 2007 Forms ✔ The template uses helper methods to construct an  HTML form to let the user edit the data in a model  object. ✔ When a user submits a form the post data is sent  back to our controller – Rails extracts the fields from  the form and constructs the :params hash.  Simple  values are extracted, but if a parameter name has  brackets in it, rails assumes its more structred data,  and stores the info in a hash, using the strng in the  bracket as the key.
    • 254. Slide 254 Presentation Name January 25, 2007
    • 255. Slide 255 Presentation Name January 25, 2007 Forms cont.. ✔ form_for vs form_tag ✔ When you’re writing forms that map to DB resources –  use the form_for.  If they don’t map, or don’t map easily  use the form_tag
    • 256. Slide 256 Presentation Name January 25, 2007 Forms that wrap model objects <% form_for :user do |form| %> …. <% end %> ✔ The first parameter tells rails the name of the object  being worked on, and the name of the instance  variable holding a referance to that object. ✔ If the variable containing the model object is not  named after the models class, ad the variable as a  second argument
    • 257. Slide 257 Presentation Name January 25, 2007 Form_for ✔ Form_for takes a hash of options – the two most common  are :url and :html. ✔ The :url option takes the same methods as link_to – it  specifies the url to be invoked when the form is submitted. <% form_for :user => { :action => :create } %> ✔ If you don’t specify a url, the form posts back to the  originating action ✔ The :html tag lets you add HTML attributes to the form tag  (think CSS)
    • 258. Slide 258 Presentation Name January 25, 2007 Field helpers ✔ Form_for takes a block of code and passes the block  a form builder object. ✔ This is used to add form elements.
    • 259. Slide 259 Presentation Name January 25, 2007 Example <% form_for :product, :url => { :action => :create } do |form| %>  <p>Title: <%= form.text_field :title, :size => 30 %></p>  <p>Description: <%= form.text_area :description, :rows => 3 %></p>   <p>Image URL: <%= form.text_field :image_url %></p>   <p>Price: <%= form.text_field :price, :size => 10 %></p>  <%= form.select :title, %w{ one two three } %>  <p><%= submit_tag %></p>  <% end %> 
    • 260. Slide 260 Presentation Name January 25, 2007 Errors handling and model objects ✔ When building html for each field in a model, the helper  methods invole the models ‘errors.on(field)’ method. ✔ If any errors are returned the generated html will be  wrapped in div tags with class=‘fieldWithErrors’. ✔ As well as highlighting fields in error you can display the  text of the error message by calling the <% error_message_on(:model, :field_name %> Or <% error_messages_for(:model) %> (scaffolded)
    • 261. Slide 261 Presentation Name January 25, 2007 Custom Form Builders and Nonmodel  Forms ✔ See handouts!
    • 262. Slide 262 Presentation Name January 25, 2007 Layouts ✔ We know that many pages share the same tops, tails,  and sidebars ✔ Multiple pages may contain the same html snippets,  and the same functionality may appear in multiple  places. ✔ Rails has layouts, partials and components to reduce  duplication
    • 263. Slide 263 Presentation Name January 25, 2007 Layouts ✔ When Rails renders a request to render a template  from within a controller it’s actually rendering two  templates – the one you asked for, and a layout  template. ✔ When if finds the layout, it inserts the action specific  output into the layout
    • 264. Slide 264 Presentation Name January 25, 2007 Layouts… <html> A bunch of stuff here that we’d duplicate <%= yield :layout %> </html> ✔ When a layout is rendered there’s a call to yield ­  when the template for the action was renedered rails  stored it’s content and labeled it :layout – this yield  call inserts this :layout content
    • 265. Slide 265 Presentation Name January 25, 2007 Layouts cont… ✔ Layouts are controller specific – if the current  request is being handled by a controller named store,  rails will look for a layout called store in the  app/views/layouts directory ✔ You can override this by using the layout declaration  inside a controller ,but you can qualify these using  the :only and :except qualifiers ✔ Layouts have access to all the same data that’s  available to conventinal templates.
    • 266. Slide 266 Presentation Name January 25, 2007 In fact.. ✔ The same mechanism that lets you use the yield  :layout to embed the rendering of a template into the  layout also allows you to generate arbitrary content  in a template, and embed it. ✔ So in your template <%content_for(:sidebar) do %> This is sidebar stuff… <% end %>
    • 267. Slide 267 Presentation Name January 25, 2007 In fact cont… ✔ Can then be embedded in your layout using <div class=“page­specific­sidebar”> <%= yield :sidebar %> </div>  
    • 268. Slide 268 Presentation Name January 25, 2007 Partial page templates ✔ A partial is a kind of subroutine – you invoke it one  or more times from within another template,  potentially passing it objects to render as parameters. ✔ Internally a partial looks the same as any other  templage – externally they’re a bit different. ✔ The name of the partial must start with an  underscore.
    • 269. Slide 269 Presentation Name January 25, 2007 So…. ✔ Say you had a receipt line – you’d be reusing it often so  you’d store it in a partial that might look like this, saved in  _line.rhtml <div class=“line" > <div class=“lineheader" > <h3><%= line.title %></h3> </div> <div class=“linebody" > <%= h(line.body) %> </div> </div
    • 270. Slide 270 Presentation Name January 25, 2007 And other templates could render it like  this.. <%= render(:partial => “line" , :object => @an_article) %> <h3>Add Comment</h3>

    ×