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
Wednesday, 30 April 2008
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.
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.
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
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
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
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)
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>
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="]
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.
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
1) script/generate scaffold
2) not do script/generate model
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.
Subscribe to:
Posts (Atom)