Wednesday 30 April 2008

History Meme

So I got tagged to do this by my boss Roger, and a lot of my work colleagues are doing it. So here is the result from my macbook:

history|awk '{print $2}'|sort|uniq -c|sort -rn|head

167 prove
62 cd
55 svn
45 cover
42 ls
32 rake
28 mate
11 make
8 ./bin/apachectl
7 sudo

So I am testing quite a lot on my machine (prove and cover) (always trying for test driven development is the reason for this). cd is obvious. svn is vitally important.

I am surprised rake turns up more than make though

More service soon.

Andy

Monday 7 April 2008

Javascript for all

So on Friday, as a gentle way of trying to get back into work mode after the Rails course had finished, I started
by trying to refactor out a lot of javascript from the templates.

I have just bought 'Pragmatic Ajax (A Web 2.0 Primer)' from The Pragmatic Programmers. It is a very interesting read, and
inspired me to 'get the code out of the view'

It's true to say, that we have been quite lax in simply putting <script> tags in with fairly specialised javascript
functions, which don't really need any variables passed to them (as the function ends up with the paths and div ids hard=coded).

Well, I managed to refactor out most of the functions that we had written, and with a few additions to variables being passed to them, managed to reduce the number of some function (or make them more genericised for future reference). I even discovered a slight problem with my scrumptious.js which I need to tweak and document.

The great thing is that we have now reduced the code in the views. This makes the views easier to read and keep upto date.

There are still a few functions which I should be able to refactor, but I need to find out a couple of extra things first.

I've only got through the first 3 chapters of 'Pragmatic Ajax' so far, but it has explained a bit that so far I hadn't known from just my experience learning some Ajax through RoRails. Chapter 1 explains about what Ajax is, Chapter 2 shows you how to develop Ajaxian Maps (a google maps clone). Then it has started to go into the Nitty Gritty details of Ajax and Client-side Javascript.

However, so far the javascript examples have all been written in the html head, rather than in a separate .js file. I imagine (hope) that this will change in a best practice suggestion. I'm also hoping it will show a bit on testing javascript, which so far is something that I haven't done.

My experience of programming books has led me to find the Pragmatic Programmers books are a great way of finding the information in an easy to read style. So far, Pragmatic Ajax is a good book and hasn't let me down in it's style and (most importantly) content.

Thursday 3 April 2008

Advancing with Rails course - Day 4 pt 3

So, the final bit of the course has been looking at integration testing

objective is to go through the processes to a conclusion, i.e.

login >>
attempt to bid on an auction you started >>
fail

login >>
bid on auction as highest bidder >>
fail

login >>
bid on auction >>
pass

and so on

can cross controllers which is why next level up from functional tests. May be more than one/two asserts as
they are linked and this is useful to ensure it doesn't bother trying tests it can't even get to

very good place to often refactor heavily

integration testing routes

def test_show_route
assert_recognizes({:controller => :auctions,
:action => :show,
:id => "1"}, auction_path(1)) <==named routes
end
def test_generate_route
assert_generates("/auctions/3", :controller => :auctions,
:action => :show,
:id => "3")
end

also can assert_routes

you could get back responses to look at when rjs is rendered

irb>> app.get("/auctions/destroy/1")
>> 200
irb>> app.response.body
(output of rjs file)

rcov tool

coverage of code with tests

ZenTest

runs in another terminal window, and everytime you make and save a change to a file, it works out the tests which are affected and reruns those tests.

Advancing with Rails course - Day 4 pt 2

Plugins

How to install and generate a plugin

guest095:yadb ajb$ script/plugin install http://repo.pragprog.com/svn/Public/plugins/annotate_models
+ ./ChangeLog
+ ./README
+ ./lib/annotate_models.rb
+ ./tasks/annotate_models_tasks.rake
guest095:yadb ajb$ ls vendor/plugins/
annotate_models/ nested_has_many_through/
guest095:yadb ajb$ ls vendor/plugins/annotate_models/
ChangeLog README lib/ tasks/
guest095:yadb ajb$ ls vendor/plugins/annotate_models/tasks/annotate_models_tasks.rake
vendor/plugins/annotate_models/tasks/annotate_models_tasks.rake
guest095:yadb ajb$

adds schema info to the top of your model files

guest095:yadb ajb$ rake annotate_models
(in /Users/ajb/dev/vtes/yadb)
Annotating Card
Annotating CardDiscipline
Annotating Clan
Unable to annotate Clan: Could not find table 'clans'
Annotating CostType
Annotating Deck
Annotating Discipline
Annotating Minion

card.rb:

# == Schema Information
# Schema version: 6
#
# Table name: cards
#
# id :integer not null, primary key
# name :string(255)
# text :string(255)
# requirements :string(255)
# cost :integer
# cost_type_id :integer
# minion_id :integer
# deck_id :integer
# discipline_id :integer
#

class Card < ActiveRecord::Base
belongs_to :deck
belongs_to :cost_type
belongs_to :minion
has_many :card_disciplines
has_many :disciplines, :through => :card_disciplines
belongs_to :vampire, :class_name => "Minion", :conditions => "minion.name = 'vampire'"
belongs_to :werewolf, :class_name => "Minion", :conditions => "minion.name = 'werewolf'"

validates_presence_of :name
validates_uniqueness_of :name
validates_presence_of :deck_id

end

writing a plugin

guest095:yadb ajb$ script/generate plugin nice_error_fields
create vendor/plugins/nice_error_fields/lib
create vendor/plugins/nice_error_fields/tasks
create vendor/plugins/nice_error_fields/test
create vendor/plugins/nice_error_fields/README
create vendor/plugins/nice_error_fields/MIT-LICENSE
create vendor/plugins/nice_error_fields/Rakefile
create vendor/plugins/nice_error_fields/init.rb
create vendor/plugins/nice_error_fields/install.rb
create vendor/plugins/nice_error_fields/uninstall.rb
create vendor/plugins/nice_error_fields/lib/nice_error_fields.rb
create vendor/plugins/nice_error_fields/tasks/nice_error_fields_tasks.rake
create vendor/plugins/nice_error_fields/test/nice_error_fields_test.rb

guest095:yadb ajb$ cd vendor/plugins/nice_error_fields/
guest095:nice_error_fields ajb$ ls
MIT-LICENSE Rakefile install.rb tasks uninstall.rb
README init.rb lib test

install.rb needs the require 'nice_error_fields.rb'
and the lib directory is added to the path

the init.rb files are all read (so no need to create an initializer) and all the lib paths are added

Advancing with Rails course - Day 4 pt 1

Forms first off, a bit of idioms created based on RESTful:

forms if you where to put in def new to render edit, when rendered it knows to post to create rather than edit,
and know is calling edit to put to update

AciveRecord can query objects via the new_record? method (@thing.new_record?), which means that it can determine whether it is a new object and so RESTfully can determine whether to create or update

David doesn't like this as it has the feeling that it is not very elegant. It is over economising. Use a partial if there is >serious overlap, and then have different templates, which can have extra bits.

(This was originally a plugin simply_helpful, now core and only works with REST)

Subclass

subdirectories

You can

script/generate controller admin/things

in routes

map.namespace :admin do |n|
n.resources :things
end

this gives a set of restful routes like new_admin_thing
with a
parallel tree of views app/views/admin/things/


Then onto some more Ruby

Procs and Callbacks

Ruby: proc (function) objects

anonymous functions which are themselves objects (and can bepassed around, etc)

Proc can remember a local variable within itself, rather than it going out of scope and being garbage collected

guest095:yadb ajb$ irb
>> Proc.new {}
=> #<Proc:0x00000000@(irb):1>
>> lambda {}
=> #<Proc:0x00000000@(irb):2>
>> proc {}
=> #<Proc:0x00000000@(irb):3>


lambda and proc are synonyms, but different from Proc.new

code blocks can be captured within a def method

p = lambda {|x| puts x * 10}
array = [1,2,3,4,5,]
array.each(&p)


def convert(n, &block) <-- special & argument syntax captures code block as a Proc object
end

puts convert (10) { |x| x * 30 }

alternative

def convert(n)
if block_given?
yield(n)
else
n * 2
end
end

good unless you need to objectify the method for some reason

it is a closure on the variables that exist around where it is created
>> y= 1; [1,2,3].each {|x| puts x * 10; puts y; y += 1 }
10
1
20
2
30
3
=> [1, 2, 3]
>> puts y
4
=> nil

but leaves alone ones created after its creation

>> class Counter
>> def self.create(n=0, inc=1)
>> return Proc.new{ n += inc; n - inc }
>> end
>> end
=> nil
>> c = Counter.create
=> #<Proc:0x0059f330@(irb):3>
>> puts c.call
0
=> nil
>> puts c.call
1
=> nil
>> n = 222
=> 222
>> c = Counter.create
=> #<Proc:0x0059f330@(irb):3>
>> puts c.call
0
=> nil
>> puts c.call
1
=> nil
>> c = Counter.create(5,5)
=> #<Proc:0x0059f330@(irb):3>
>> puts c.call
5
=> nil
>> puts c.call
10
=> nil
>> puts n
222
=> nil


you can write code blocks with {} or do - end, but they are not interchangeable

>> puts [1,2,3].map {|x| x * 10 }
10
20
30
=> nil
>> puts [1,2,3].map do |x| x * 10 end
1
2
3
=> nil

you can write a method to warn about the presence of a block (or not), but not every ruby method will do this

>> def m; raise "A block!" if block_given?; end
=> nil
>> m
=> nil
>> m {}
RuntimeError: A block!
from (irb):21:in `m'
from (irb):23

>> m do |x| x*10 end
RuntimeError: A block!
from (irb):27:in `m'
from (irb):28

>> def m; raise "A block!" if !block_given?; end
=> nil
>> m {}
=> nil
>> m
RuntimeError: A block!
from (irb):24:in `m'
from (irb):26

Built in callbacks
method_missing
Modules: - included
Classes: - inherited

>> module M
>> def talk; puts "Hi!"; end
>> end
=> nil
>> M.new
NoMethodError: undefined method `new' for M:Module
from (irb):4

you can't instantiate a module, but they are good for mixins

you can include Classes in Modules

>> module Violin
>> class String; end
>> end
=> nil

modules can give their methods to a class, via include

?> class Person
>> include M
>> end
=> Person
>> andy = Person.new
=> #<Person:0x59b71c>
>> andy.talk
Hi!
=> nil


Classes - inherited

>> class Furniture
>> def self.inherited(c)
>> puts "#{self} has been inherited by class #{c}"
>> end
>> end
=> nil
>>
?> class Chair < Furniture
>> end
Furniture has been inherited by class Chair
=> nil


extend

?> module N
>> def walk; puts "I am walking!"; end
>> end
=> nil
>> Person.ancestors
=> [Person, M, Object, Kernel]
>> andy.walk
NoMethodError: undefined method `walk' for #<Person:0x59b71c>
from (irb):29
>> andy.extend(N)
=> #<Person:0x59b71c>
>> andy.walk
I am walking!
=> nil

use extend via a module if you want to replace core methods as this is a low impact way

>> module M;def shout;puts "HI!!!!";end;end
=> nil
>> class C;end
=> nil
>> C.extend(M)
=> C
>> C.shout
HI!!!!
=> nil

Classes are objects. It extends the singleton methods on the object

equivalent to

class << C; include M; end

extend gives class methods, include are instance methods, all though David wasn't really sure what that ultimately would mean

Wednesday 2 April 2008

Advancing with Rails course - Day 3 - pt3

Final blog for day 3 (I'll probably end up summarising a lot of this is a presentation to foomongers, so I'll post the slides if I do. However, for now:

We started to look at REST.

Just a few notes- not in any structure. A good resource to read is Roy T. Fielding's dissertation (http://www.ics.uci.edu/~fielding/)

Representational State Transfer

request sequence (slide 198)

by default

link_to => GET
form_for => POST

link_to with movie_path => GET
link_to using edit_movie_path => GET

as browsers don't issue PUT and DELETE requests, rails cheats with:

to get PUT:
form_for with :url => movie_path(@movie) and :html => (:method => "put")
you get => method="post", intput type="hidden" name="_method" value="put"

to get DELETE
link_to wih item_path(@item), :method => "delete"
you get => DELETE (but wrapped in a form so that spiders don't follow it)

REST and CRUD have no actual real connection, but when brought into rails, then they meet up,
because it has been orchestrated

And then finally during the day onto some Ajax stuff

Ajax:

Ajax request happens overall with in the cycle of a request

link_to_remote "Click me", :url => {....}
-> goes to server, does stuff C/A -> sends back to client

typically, not a whole cycle, as only sends back a snippet/fragment which you want to drop into your document

link_to_remote "Click me", :url => {:action => click_me}, :update => "mydiv"

so drops it into a div entitled mydiv

def click_me
render :partial => "clicked"
end

renders the partial and drops it in

link_to_remote "Click me", :url => {:action => click_me}, :div => "mydiv"

as no update

def click_me
@div = params[:div]
@del = Auction.delete(params[:id])
end

hands off to click_me.rjs

if @del.zero?
page.alert("Destroy operation failed")
else
page.visual_effect :shrink, @div, :duration =>1
page.delay(2) do
page.alert("That auction is history!")
end
end

page sends back the javascript to do what you want

form_remote_for :item, :url => {:action => :update}, :update => "mydiv"

flash is normally waiting for the next request cycle

flash.now[:notice] will have it come through to the ajx response there and then, rather than wait for the page refresh

raise request.xhr? method which means is this an ajax request, which means that you can fork on the request.

name.html.erb will take preference over name.rjs, so you either shouldn't have them with the same name,
or render rjs directly in the controller

render :update do |page|
page.alert("Hello")
end

which would bypass that issue

you can put rjs directly in the view to get javascript directly in the page

xhr requests are post by default, so if you are requesting a view only allowed by get, put or delete, then you need to explicity state this in the link_to_remote tag

Advancing with Rails course - Day 3 - pt2

Rails Routing

routing system does 2 things

1) recognises and inteprets URLs
2) generating URLs and path strings in your views

recognition
- goals to determine controller and action
store other values in params hash

generation
- arguments to link_to, form_for, etc
- for redirection in controllers
anywhere you need a path or URL in your actual code

top-level route

map.root :controller => "cards"

link_to "top", root_path

map.connect ':controller/:action/:id'

shows what you want your URL to look like

routing system only makes sense to itself -> only the routing system can know what the URL means if it generated it.

routing system doesn't know how to resolve for another application

model doesn't know the controller which is manipulating it.

to inject css into a link_to

<%= link_to "log in", {:controller => 'this', :action => 'that'}, :class => "blah" %>

hard coding

map.connect 'help',
:controller => "leagues",
;action => "assist"

this will resolve http://www.web.site.com/help to the specific path leagues/assist
and will only resolve /help as that

map.connect 'help/:controller', :action => "assist"

resolves controller in http://www.web.site.com/help/wibble to the path wibble/assist

extra wildcards can also be added and matched positionally, which are then stored in params
:id is special case though, which allows it to be nil, whereas anything else needs to specified,
or will find no route (unless you use globbing)

you can constrain the wildcards with pattern matching, etc and have many of these, but orderis important
(higher up the routes.rb file, will hit first)

You may need to catch a routing error.

current and default values

missing components get defaults

:controller and :action from current ones
:id, :topic, etc from params hash

default gets turned off after the first one you specify

named routes:

map.<name> "name",
:controller => :x,
:action => :y

then allows
redirect_to name_url
link_to "text" name_path

map.vampires 'vampire/:name/:capacity',
:controller => :cards,
:action => :vampires

<%= link_to "#{vampire} with capacity #{capacity}",
vampires_path :name => vampire, :capacity => capacity %>

<%= link_to "#{vampire} with capacity #{capacity}",
vampires_path (vampire, capacity) %>
(walks through the list into the url space)

Named routes and CRUD

these can be expressive and encourage good action names

cluster thinking around the operation (borrow book is create loan, renew is update, return is destroy/delete, view all loans is read)

Advancing with Rails course - Day 3 - pt1

protect_from_forgery -> ensures that posts are only acted on if from your application

session hash is now stored as a cookie on the client side
typical to put in a user_id

before_filter s : anything is true unless false and will stop processing if it gets false,
so explicitly return true

attr_accessible and attr_protected

in model

attr_protected :admin, :hashed_password
black list of stuff which can't be changed

attr_accessible :price, :size
white list of stuff which can be changed

however, if not in attr_accessible and this is declared, can't be updated

needs maintenance by hand, but pretty cheap

untrusted_input

item = Item.find(:first, :conditions => ["material = ?", untrusted_input])

Don't know how the database does boolean
User.find(:first, :conditions => ["admin = ?", true])

for doing in
User.find(:first, :conditions => ["email in (?)", ["a@c", "b@d]])

User.find(:first, :conditions => ["created_at < ?", Time.now])

method h

<%=h item.name %>

when there is the chance that it could be html which could get injected, which then escapes the html and so doesn't drag in a <script> or <img>

Advancing with Rails course - Day 2

April 1st

So, a lot to blog about from day 2. Don't feel you need to read it all, and some may hardly be a revelation to most, but part of the purpose of this blog is also for my own record of what occurred and what I learnt, so you'll have to bear with me.

We did a lot of working within our own apps today, doing little bits of lecture, and then seeing if it can be applied.
I seem to have picked a good little app to work on, as it does forms, and needs associations, which was some of what we worked on today. (Looking through my notes again, please note that some of these are my interpretations of the discussions an what notes I grabbed, so may not be the whole truth, or even everything David said)

The first lecture bit started on the "Request Cycle".

tThis was hardly a revelation to myself, but was nice to see it formalised. Basically, all rails apps work on the idea of a request cycle.

A user clicks somewhere (to launch the application, a hyperlink, sends a form...)
Mongrel sets off a dispatcher, which goes to routing system to load share (if your server is set up this way)
then calls controller/action/params[x]

i.e. items/show/1
-> contr/act/params[:id]

It then takes items and adds up to make it items_controller.rb and looks for ItemsController class.

This is now just all in the hands of the ruby interpreter, the server is just waiting for a response, which it then sends back to the user.

The server then cares nothing about what happens until it receives another request from the user. Quite obvious, but it explains why new instances of the objects are created each time, whereas within a gui app, the program loads once, and then there are very few times that a new instance of an object would get created. However, the application expects further user input (even if it is just a kill command), whereas the Rails app doesn't. Even if you request a form, the rails app doesn't actually expect you to fill it in and send it back.

So what is happening during the ruby interpreter phase.

firstly it creates an instance or object of the controller

controller = ItemsController.new

and then looks for an action on that instance of the given name

controller.show

passing in the params[:id] if given

now by default active record objects know that they should render a view with the same name, but you can use the render method to render another template. They also know how to render themselves as xml by default.

render :xml => @item

or you could use a redirect, but this starts a new request cycle (sends a 302 to the user/client, which the goes back to mongrel), which therefore needs to reassign and item id (and any other params that might need to pass through)

(update doesn't typically have an update.html.erb, as you usually want a redirect or to render the form again)

render uses the changed values, whereas redirect sets a new instance, which then enables the wrong values to propagate through to the rendered template

before_filter command

before_filter :set_item,
:only => ['edit', 'udpate', 'show']
this would cause set_item action to run before the stated actions

This is good, because it then enforces use of the correct variable use, and then, should you end up with a blank action (ie: def show;end) because the set_item does everything, then it will jump through and render the show.html.erb file, without even needing def show;end to be there at all


Now, instance variables are how the controller talks to the views, but in this case the instance variable does not belong purely to self, as

self in controller is a controller object
self in view is an ActionView::Base object

when rails hands off a variable to the view, it walks through the array and sets its type to the ActionView::Base,
so they don't technically share them.

--

Before lunch we looked at non-default associations. This got very interesting for my app YADB, we found a plugin which allowed nested has many through associations.

belongs_to :auction (gives methods from Auction)
belongs_to :bidder, :class_name => "User" (enables User to be used as a bidder, but no bidder object)

self.bidder = User.new(x)

has_many :bids, :foreign_key => "bidder_id" (sets the use of a different foreign key as otherwise would look for bids_id)
has_many :auctions_held, :class_name => "Auction", :foreign_key => "seller_id"
(combination of the previous two)
has_many :auctions_bid_on (no class), :through => :bids,
:source => :auction, (however you got there, I want to know via bid.auction)
:uniq => true (creates many to many relationship, but with useful join table with it's own model)


define singleton methods on the association

has_many :bids do
def average_interval
i = 0.0
inject {|a,b| i += b.created_at = a.created_at; b }
i / (size-1)/ 1.day
end
end

A bit on inject:

it is a method on enumerable

[1,2,3,4].inject do |a,b|
a+b
end

loop

1st time 1 is a, 2 is b
next time a is result of code block, b is the next element

1: a = 1, b = 2
2: a = 3, b = 3
3: a = 6, b = 4

also used with hashes (both are equivalent)

[1,2,3,4,5].inject({}) do |hash,e| hash[e] = e * 10; hash; end
[1,2,3,4,5].inject({}) do |hash,e| hash.update(e => e * 10); end

I had a problem about trying to link through 2 tables, with the following idea

class Disciplines < ActiveRecord::Base
has_many :card_disciplines
has_many :cards, :through => :card_disciplines
has_many :vampires,
:through => :cards,
:source => :minion, :conditions => "minions.name = 'vampire'"
end

Now this isn't in 2.0.2, but there had been a lot of discussion about it. A patch has been submitted and the writers have created the following plugin

nested_has_many_through.rb
https://svn.colivre.coop.br/svn/noosfero/trunk/vendor/plugins/nested_has_many_through/

which works. David mentioned that the discussion about this has reached a point where he thought it had been incorporated, and may be in edge rails, but clearly didn't make the cut to 2.0.2

I hope that it makes it in, as when I first attempted YADB with 1.2, I was looking for some links like this. There is some discussion about how this hits the database, but it keeps the Rails pragma, instead of writing a SQL statement myself, and still only generated 1 single SQL statement. What more do you want?

--

We then looked at errors and validation.

ActiveRecord examines the object to decide if it is valid. If it fails, then it never even attempts to save the information in the database.

validates_size_of :name, minimum => 5
validates_size_of :name, maximum => 50
- > must be on separate lines

You can create your own validations by using validate

def validate
errors.add("name", "That's impossible") if name =~ /\d/
end

All ActiveRecord objects have and errors attribute. If the errors attribute is empty, then the object is valid

You can trap the errors if the database has the constraints on it that disallow data to be be saved, but do this via the controller, as you are expecting a big error, rather than a protection of the data integrity

--

This then lead onto forms and processing, starting with a bit on the difference between each and map

each vs map

each loops over 1 element at a time, and return value is the object

x = [1,2,3,4]
y = x.each {|e| puts e*10}
y.equal?(x) => true

map returns a mapping (an accumulator of the results of the block)

newx = x.map {|e| e* 10 }
newx => [10,20,30,40]

What goes on when you display and process forms

<% form_tag :action => 'update', :id => @item.id do %>
<p> name: <%= text_field 'item', 'name' %></p>
<p> year: <%= text_field 'item', 'year' %></p>
prepopulates these on form generation

then, for each first argument

params[:item]

and then

params[:item][:name]
params[:item][:year]

fields_for will allow you to override the default selected using form_for

<% form_for 'auction' :url => {:action => create} do |f| %>

<p>Title: <%= f.text_field "title" %></p>
<% fields_for 'item' do |fi| %>
<p>Description: <%= fi.text_field 'description' %>
<% end %>
<%= submit_tag %>
<% end %>

gives

params[:auction][:title]
params[:item][:description]

errors which occur get wrapped with a <div class="fieldWithErrors"></div> which causes the box to jump down beneath the title.

This means you could style it

You can change it.

It calls a proc, which is a function and you can replace it.

Lazy (in environment.rb, should be an initializer)

ActionView::Base.field_error_proc = Proc.new {|a.b|
"<p>Andy's placeholder</p>"
}

This is an executable object that I can call again and again when I want it.

Look on google with 'field_error_proc'

--

The final lecture session was our daily dose of ruby. We were looking at the singleton method. This was interesting to find out, as we looked the singleton class, and a bit on how an object looks to find if it has a method.

The method lookup path

1) The object's singleton class
- modules mixed into singleton class
2) The object's class
- modules mixed into object's class
3) The object's superclass
- modules mixed into object's superclass

repeat 3 as needed until
-Object
a) kernel

To open the singleton class definition for self

class << self

this is a very frequent idiom for class methods, and means if you have lots of

def self.method
...
end

then you can save the 'self.' by

class << self
def method
end
def another_singleton_method
end
end

To demonstrate this, I'll just dump here the irb that I did whilst trying this, and that will be all from day 2. Looking forward to day 3.

guest095:~ ajb$ irb
>> class Person; attr_accessor :name; end
=> nil
>> andy = Person.new
=> #<Person:0x5a2abc>
>> class << andy
>> def talk
>> puts "Hi"
>> end
>> end
=> nil
>> andy.talk
Hi
=> nil
>> aclass = Person
=> Person
>> aclass.methods
=> ["to_yaml_style", "inspect", "private_class_method", "const_missing", "clone", "method", "public_methods", "public_instance_methods", "yaml_as", "instance_variable_defined?", "method_defined?", "superclass", "equal?", "freeze", "included_modules", "const_get", "to_yaml_properties", "methods", "respond_to?", "module_eval", "class_variables", "dup", "instance_variables", "protected_instance_methods", "to_yaml", "__id__", "public_method_defined?", "eql?", "object_id", "require", "const_set", "id", "send", "singleton_methods", "taguri", "class_eval", "taint", "require_gem", "instance_variable_get", "frozen?", "yaml_tag_class_name", "taguri=", "include?", "private_instance_methods", "instance_of?", "__send__", "private_method_defined?", "to_a", "name", "yaml_tag_read_class", "autoload", "type", "new", "<", "instance_eval", "gem", "protected_methods", "<=>", "display", "==", ">", "===", "instance_method", "instance_variable_set", "extend", "kind_of?", "protected_method_defined?", "const_defined?", ">=", "ancestors", "to_s", "<=", "public_class_method", "allocate", "class", "hash", "private_methods", "=~", "tainted?", "instance_methods", "class_variable_defined?", "untaint", "nil?", "constants", "is_a?", "yaml_tag_subclasses?", "autoload?"]
>> dc = class << andy; self; end
=> #<Class:#<Person:0x5a2abc>>
>> dc
=> #<Class:#<Person:0x5a2abc>>
>> dc.instance_methods.sort
=> ["==", "===", "=~", "__id__", "__send__", "class", "clone", "display", "dup", "eql?", "equal?", "extend", "freeze", "frozen?", "gem", "hash", "id", "inspect", "instance_eval", "instance_of?", "instance_variable_defined?", "instance_variable_get", "instance_variable_set", "instance_variables", "is_a?", "kind_of?", "method", "methods", "name", "name=", "nil?", "object_id", "private_methods", "protected_methods", "public_methods", "require", "require_gem", "respond_to?", "send", "singleton_methods", "taguri", "taguri=", "taint", "tainted?", "talk", "to_a", "to_s", "to_yaml", "to_yaml_properties", "to_yaml_style", "type", "untaint"]
>> dc.instance_methods(false)
=> ["name", "talk", "name="]

Tuesday 1 April 2008

Advancing with Rails course - Day 1

Mar 31 was the first of our 4 day Ruby on Rails course at work. It is being presented by David Black, author of Ruby for Rails, and Director of Ruby Power and Light LLC.

We have essentially managed to catch him between the European Ruby conference and Scotland on Rails.

The day was very much an introductory day, with a few little tidbits. We set up our own little Sandbox project to work within. I have opted to start with something I have been looking to do for sometime which is a VTES CCG deck-building program. (VTES is a vampire based collectable card game which is where the Vampire Software comes from. It will be something I'll get into sourceforge soon!)

David also explained a bit more about Ruby objects and rails functional testing. The former was a good tidy up of what I already knew/had worked out. The latter was a good formalisation of what they are trying to achieve, as whilst I had done some functional testing in rails before, most was concentrated on the unit testing. (A note here. functional testing can mean different things in different frameworks. Some prefer to think of functional testing in Rails as just an extension of Unit testing specifically for controllers, as in other frameworks, functional tests are what Rails calls Integration tests).

At this point, David also gave us a reason for NOT using scaffold generation to generate model backed controllers and views. Personally, I like this approach (I think that code which writes code is a very good thing) but he says that as you get further into rails, you end up removing more from scaffold generated code, than you would write that could have been auto-generated, and as such prefers to suggest that you should learn writing from scratch. With my limited experience, I'll take a rain-check on making a confirmed decision, but I will (for now), stick with auto-generation when it comes to a model backed controllers.

One thing here though that I did discover is in Rails 2.0.2, if you have generated your model first, and then run script/generate scaffold , it stops after it finds a migration with the same name as it wants to generate. You actually need to do the following

1) script/generate scaffold --skip-migration
2) not do script/generate model first
3) rename the migration file and class within it before running

This was annoying, as before when I did this, it used to ask if I wanted to replace it, and it generates everything up to the point where it barfs, but nothing after it. Oh well, I suppose it is something I can get used to. Just think I'll need to read up on the new stuff for 2.0.2 sooner than I expected. :)

We also touched upon the new Initializers, which look very useful for keeping track of requirements. I hope we might look at those again.

The final thing to talk about here was how to implement Composite Primary Keys. Firstly - why? ActiveRecord is never going to support them.

Secondly - However, we have a big legacy database using them extensively, so David showed us how to implement Dr. Nic's composite_primary_keys gem (requiring it via an Initializer!). I won't explain it all here, but the scratchpad attempt I made of it worked well. (to install it though - sudo gem install composite_primary_keys)

And finally, speaking of Legacy databases. In Rails 'type' is a reserved column type for when using Single Table Inheritance, and ActiveRecord expects to use this as a class name. We have fallen foul of this with our legacy database, but fall foul of it no more. In the model put:

set_inheritance_column :nothing

and hey presto! you can use the 'type' column in your legacy schema as planned.

That will do for now. Let's see what the next day brings.