![]() |
|
Participant agrees not to do the following, except with the advanced review and written approval of Google: issue or release any articles, advertising, publicity or other matter relating to this Agreement (including the fact that a meeting or discussion has taken place between the parties) or mentioning or implying the name of Google.
Non-Disclosure Agreement
Revision 0727.2005
Google Inc.
This Non-Disclosure Agreement ("Agreement") is made and entered
into between Google Inc., for itself and its subsidiaries and affiliates
("Google"), and "Participant" identified below, individually referred to as
a "Party" and collectively referred to as the "Parties". The Parties wish to
exchange Confidential Information (as defined below in Section 2) for
the following purpose(s): evaluating a potential employment opportunity
with Google (the "Purpose"). The Parties have entered into this
Agreement to assure that the confidentiality of such information is
maintained, in accordance with the following terms of this Agreement:
1. The Effective Date of this Agreement is _____________________.
2. Google may disclose certain information under this Agreement it
considers confidential and/or proprietary concerning Google's
business and/or technology ("Confidential Information") including,
but not limited to, all tangible, intangible, visual, electronic, present,
or future information such as: (a) trade secrets; (b) financial
information, including pricing; (c) technical information, including
research, development, procedures, algorithms, data, designs, and
know-how; (d) business information, including operations, planning,
marketing interests, and products; (e) the terms of any agreement
and the discussions, negotiations and proposals related to any
agreement; and (f) information acquired during any facilities tours.
3. Participant will use the Confidential Information only for the Purpose
described above. Participant will use the same degree of care, but
no less than a reasonable degree of care, as Participant uses with
respect to its own similar information to protect the Confidential
Information and to prevent: (a) any use of Confidential Information
not authorized in this Agreement; and/or (b) communication of
Confidential Information to any unauthorized third party.
Confidential Information may only be disseminated to employees,
directors, agents or third party contractors of Participant with a need
to know and who have first signed an agreement with Google
containing confidentiality provisions substantially similar to those set
forth herein ("Authorized Personnel"). Participant shall ensure
compliance by Authorized Personnel with the terms and conditions
of this Agreement, and shall be responsible for any breach of such
terms and conditions by any Authorized Personnel.
4. Participant agrees not to do the following, except with the advanced
review and written approval of Google: (a) issue or release any
articles, advertising, publicity or other matter relating to this
Agreement (including the fact that a meeting or discussion has
taken place between the parties) or mentioning or implying the
name of Google; (b) make copies of documents containing
Confidential Information; or (c) reverse engineer, disassemble,
decompile, translate, or attempt to discover any prototypes,
software, algorithms, or underlying ideas which embody Google's
Confidential Information.
5. This Agreement imposes no obligation upon Participant with respect
to Confidential Information that: (a) was known to Participant before
receipt from Google, as evidenced by Participant's files and records
in existence before the time of disclosure; (b) is or becomes publicly
available through no fault of Participant; (c) is rightfully received by
Participant from a third party without a duty of confidentiality; (d) is
disclosed by Google to a third party without a duty of confidentiality
on the third party; or (e) is disclosed by Participant with Google's
prior written approval. If Confidential Information is required to be
produced by law, court order, or other governmental demand
("Process"), Participant must immediately notify Google of that
obligation. Participant will not produce or disclose Confidential
Information in response to such Process unless Google has (i)
requested protection from the court or other legal or governmental
authority requiring the Process and such request has been denied,
or (ii) consented in writing to the production or disclosure of the
Confidential Information in response to the Process. Nothing in this
Agreement shall prohibit or limit either party's use or disclosure of
the U.S. Federal income tax treatment and U.S. Federal income
tax structure of any transaction contemplated by this Agreement
and all materials of any kind (including opinions or other tax
analyses) that are provided to it relating to such tax treatment or
tax structure, except where confidentiality is necessary to comply
with applicable federal or state securities laws.
6. ALL CONFIDENTIAL INFORMATION CONTAINED HEREIN IS
PROVIDED "AS IS". NO OTHER WARRANTIES ARE MADE,
EXPRESS OR IMPLIED.
7. Google does not wish to receive any confidential information from
Participant, and Google assumes no obligation, either express or
implied, for any information disclosed by Participant.
8. This Agreement shall remain in effect until such time as all
Confidential Information of Google disclosed hereunder becomes
publicly known and made generally available through no action or
inaction of Participant.
9. Participant, upon Google's written request, will promptly return all
Confidential Information received from Google, together with all
copies, or certify in writing that all such Confidential Information
and copies thereof have been destroyed.
10. This Agreement imposes no obligation on Google to exchange
Confidential Information, to proceed with the business opportunity,
or to purchase, sell, license, transfer or otherwise make use of any
technology, services or products.
11. No Party acquires any intellectual property rights under this
Agreement (including but not limited to patent, copyright, and
trademark rights) except the limited rights necessary to carry out
the purposes as set forth in this Agreement.
12. Participant acknowledges that damages for improper disclosure of
Confidential Information may be irreparable; therefore, Google is
entitled to seek equitable relief, including injunction and preliminary
injunction, in addition to all other remedies.
13. This Agreement does not create any agency or partnership
relationship. This Agreement will not be assignable or transferable
by Participant without the prior written consent of Google.
14. This constitutes the entire agreement between the parties with
respect to the subject matter hereof, and supersedes any prior oral
or written agreements. All additions or modifications to this
Agreement must be made in writing and must be signed by all
Parties. Any failure to enforce a provision of this Agreement shall
not constitute a waiver thereof or of any other provision.
15. This Agreement may be executed in two or more identical
counterparts, each of which shall be deemed to be an original and
all of which taken together shall be deemed to constitute the
agreement when a duly authorized representative of each party
has signed the counterpart.
16. This Agreement shall be governed by the laws of the State of
California, without reference to conflict of laws principles. The
exclusive venue for any dispute relating to this Agreement shall be
in the state or federal courts within Santa Clara County, California.
Inject code into/around ruby methods # Injects a block of code in front of the code in method m. def inject_front(m) alias_method "__inj_f_#{m}", m define_method(m) {|*arg| yield(*arg); send("__inj_f_#{m}", *arg);} end
# Injects a block of code in the back of the code in method m. # The injected block will be provided with the result and arguments of # the original method as parameters. def inject_back(m) alias_method "__inj_b_#{m}", m define_method(m) {|*arg| result = send("__inj_b_#{m}", *arg); return yield(result, *arg);} end
# Test class class Cat def miaow(arg1, arg2) puts "arg1: #{arg1}, arg2: #{arg2}" return "miaow!" end inject_front(:miaow) {puts "front injection!"} inject_back(:miaow) {|result, *args| puts "back injection! result: #{result} args: #{args.inspect}"} end
# Test usage c = Cat.new c.miaow(1, 2)
Strings, Arrays, ... and Code?
You don't have to code for long in any language before you get intimately familiar with some standard data types. We all have a fair grasp of Ruby's String and Array, because every language has something similar. Ruby has an unusual data type though, which can trip up newcomers. That type is Ruby code itself.
Allow me to explain what I mean, through an example. First, let's create a little in-memory database to work with:
class ClientDB
Record = Struct.new(:client_name, :location, :projects)
def initialize
@records = [ Record.new( "Gray Productions", "Oklahoma",
["Ruby Quiz", "Rails Extensions"] ),
Record.new( "Serenity Crew", "Deep Space",
["Ship Enhancements"] ),
Record.new( "Neo", "Hollywood",
["Rails interface for the Matrix"] ) ]
end
end
Of course we need to be able to query this data. Let's add a very basic query routine:
class ClientDB
def select( query )
# parse query String
field, value = query.split(/\s*=\s*/)
value.sub!(/^['"](.+)['"]$/, '\1')
# match records
@records.select { |record| record.send(field) == value }
end
end
Finally, we should be able to make some queries:
require "pp"
db = ClientDB.new
pp db.select("client_name = 'Gray Productions'")
pp db.select("location = 'Hollywood'")
We have the beginnings of a system here, but my queries aren't very powerful yet. Let's add support for a single boolean operator:
class ClientDB
def select( query )
# parse query String
rules = Hash[ *query.split(/\s*AND\s*/).map do |rule|
rule.split(/\s*=\s*/).map { |value| value.sub(/^['"](.+)['"]$/, '\1') }
end.flatten ]
# match records
@records.select do |record|
rules.all? { |field, value| record.send(field) == value }
end
end
end
The good news? We can now enter queries like:
pp db.select("client_name = 'Gray Productions' AND location = 'Oklahoma'")
But there's a lot of bad news too:
- My query language hack is weak and easily broken.
- We don't support many great operators like OR and !=.
- We still have no access to the projects field.
It's clear that what we really need here is a complete language. But wait, aren't we already using a language? What about Ruby?
This is the line of thinking that leads to Ruby's code blocks. Any method in Ruby can have a bit of code included with the call. That method can then yield to that bit of code, pass it parameters, and even get a return value from it. Let's rework select() again, but this time to use Ruby's code blocks:
class ClientDB
def select
@records.select { |record| block_given? and yield record }
end
end
In my experience, you know you're doing Ruby right when you are dropping code and gaining functionality. So how did we do? Check out these queries:
pp db.select { |record| record.client_name != "Gray Productions" }
pp db.select { |record| record.client_name =~ /crew/i }
pp db.select { |record| record.projects.size == 1 }
pp db.select { |record| record.projects.include? "Ruby Quiz" }
# and much, much more...
As you can see, we now have a full language at our command. We have a complete range of operators, access to goodies like Regular Expressions, and we can easily query the Array sub field in any way that we need.
Lambda
All this block stuff is very handy, as you can see. Soon after you start using them, you're going to think, "Now if I could just stick this block in a variable..."
Enter lambda().
Tools like block_given?() and yield() can only be used inside the method we passed the block to. Sometimes we want to hold onto a block object though and pass it around a little:
class ClientDB
def count( counter )
counter.call(@records.size) # call some passed in code block object
end
end
count = 0
counter = lambda { |items| count += items }
db.count(counter)
pp count # => 3
In this example, lambda() will just wrap up the provided block in an object you can pass around and use as needed. I've only used it to count a single database here, but I could have easily counted more if we had them.
This example shows off one more interesting aspect of Ruby's blocks: They are "closures". Uh oh, ugly CS term alert! It's not as complicated as it sounds. Relax.
See how the lambda() makes use of the local variable? That variable gets updated, even though the lambda() object may get passed away and run who knows where. That's what it means to be a closure. Ruby's blocks "close-up" the binding of where they are created and take it with them. That means they can use local variables and the current value of self.
Putting it all Together
I bet you're wondering what all this has to do with our database example. Let me tell you...
Ruby has one more shortcut for blocks. You can automatically have them wrapped up into an object (or even unwrapped!) at the time of the method call. In other words, I could have written select() like this:
class ClientDB
def select( &query )
@records.select { |record| query and query.call(record) }
end
end
That funny & symbol on the last variable of a method's parameter list tells Ruby, "Just pretend I had wrapped that block with lamdba(), okay?" Ruby will wrap it up and stick it in the variable for you.
Now that didn't change anything for our select() routine, but let's try using the same knowledge in a different way:
class ClientDB
include Enumerable # use a mix-in to get select(), map(), to_a(), ...
def each( &block ) # Enumerable requires each()
@records.each(&block)
end
end
I'm only using one new trick here: In each(), I delegate to the each() method of @records by unwrapping the block. The effect of that is the same as if @records.each() had been called directly and passed the block.
Since we defined an each(), we can mix-in Enumerable (a future article topic?) to get a whole slew of new options! Watch this:
pp db.inject(0) { |sum, record| sum + record.projects.size }
pp db.map { |record| record.client_name }
pp db.find { |record| record.client_name == "Gray Productions" }
# and much, much more...
That ends my tour of Ruby's blocks. Remember, the easiest way to let all this sink in is to tell yourself that code is just another data type in Ruby. Don't be too surprised if that gets you solving problems in a whole new way...
What's a MethodFinder
I use lots of different programming languages, and they all seem to have different names for the same
concepts. For example, string concatenation is "+" in ruby and java, "." in perl, "," in smalltalk and "^" in ocaml.
After a while, this starts to drive you mad. I know that I want a method which takes "foo" and "bar" and returns "foobar"
but I can't remember which incantation I need to utter today.
Smalltalk has this neat thing called the MethodFinder. It lets
you find methods by providing an example. If you've got the
string "foo", you can ask the MethodFinder to find all the method
that, when called with argument "bar" return "foobar". This is a
very useful tool. No more scrabbling around with documentation
to find the name of a method which you know exists. Stay in the
red-pill world, and ask the code.
Now, ruby is basically smalltalk (without lots of the k3wl bits). So we can easily build a method finder in ruby too!
Show me the code!
Here it is. The first bit is a little gnarly; it works around
the fact that FixNums etc are weirdly uncloneable in ruby.
class Object
# Clone fails on numbers, but they're immutable anyway
def megaClone
begin self.clone; rescue; self; end
end
end
class MethodFinder
# Find all methods on [anObject] which, when called with [args] return [expectedResult]
def self.find( anObject, expectedResult, *args )
anObject.methods.select { |name| anObject.method(name).arity == args.size }.
select { |name| begin anObject.megaClone.method( name ).call(*args) == expectedResult;
rescue; end }
end
# Pretty-prints the results of the previous method
def self.show( anObject, expectedResult, *args )
find( anObject, expectedResult, *args ).each { |name|
print "#{anObject.inspect}.#{name}"
print "(" + args.map { |o| o.inspect }.join(", ") + ")" unless args.empty?
puts " == #{expectedResult.inspect}"
}
end
end
Park Place Does Torrents, It's Called 0.7
#
For you:
gem install parkplace --source code.whytheluckystiff.net
Right now the parkplace app and the seeder are separate. The app has a built-in tracker, though.
parkplace 55.55.55.55 3002
parkseed 55.55.55.55 3003
A few tips:
- If you have a file saved at
http://ip:3002/mp3s/tuff_ghost.mp3, you can request the torrent by adding a ”?torrent” to the end: http://ip:3002/mp3s/tuff_ghost.mp3?torrent
- If you can’t get BT clients to connect, be sure you specified a globally accessible IP back when you started things up.
- I think
parkplace and parkseed have to be on the same IP.
- Simple tracker stats at
http://ip:3002/tracker
- Park Place stores its stuff in
~/.parkplace by default.
Many thanks to William Morgan for his superb RubyTorrent lib. Official gems will be out soon. Help us test!
Overlooked Feature of Ruby Hashes
Noticed on ruby-talk:
When you create a Hash object in Ruby, you have the option of providing a block; when a hash lookup fails, this block will be called and its result returned instead of nil. The block is passed two parameters: the hash itself, and the requested key.
For example:
h = Hash.new { |h, k|
"You asked for #{k}. #{k} is not here."
}
puts h["Zordo"] # => You asked for Zordo. Zordo is not here.
# See! The hash is not modified by the callback, though
p h # => {}
# But it can be...
h2 = Hash.new { |h, k|
puts "I sing of spleen."
h[k] = "Respond, o ye #{k}!"
"Hear me!"
}
puts h2["kumquats"] # => I sing of spleen.
Hear me!
# This time the lookup succeeds:
puts h2["kumquats"] # => Respond, o ye kumquats!
A Block Costume
#
We follow Block, who goes into a cloak room and emerges with a change of fashion. A black hat for its dark magic, a dagger for its villianous theft of an instance’s binding, and a glimmering red ring for its metaprogramming fu.
Hey, would you look at that? Blocks actually look pretty good as unbound eigenmethods!
The Cloaker
Let’s say we have an HTML class for building HTML. Here’s the short dressing method:
class HTML
def cloaker &blk
(class << self; self; end).class_eval do
define_method :cloaker_, &blk
meth = instance_method( :cloaker_ )
remove_method :cloaker_
meth
end
end
end
Giving Parents to the Kids
You’re probably used to seeing code by now, in Rails and Builder and other meta-heavy libs, which passes a single class into a block for tooling with.
For example, in Builder, you’ll make an XML file like this:
xml = Builder::XmlMarkup.new :indent => 2
xml.person do |b|
b.name("Jim")
b.phone("555-1234")
end
As you get deeper in the XML file, you pass down each element to its kids using a block variable. In the above example: b.
Now, I ask, what is self in the above example? Well, it’s set to whatever instance is self in the encompassing scope.
Self Theft
Let’s use a cloak so our block can steal the soul of self.
class HTML
TAGS = [:html, :head, :title, :body, :h1, :h2, :h3, :div]
def initialize &blk; html &blk end
def method_missing tag, text = nil, &blk
raise NoMethodError, "No tag `#{tag}'" unless TAGS.include? tag
print "<#{tag}>#{text}"
if blk
cloaker(&blk).bind(self).call
end
print "</#{tag}>"
end
end
Now when we attach the block, you’ll be able to run methods freely inside the block, as if you were inside a singleton instance method.
title = "My Love is Like a LaserJet Set at 300 DPI"
sub = "Poetry Selections from Kinko's Employees"
HTML.new do
head do
title title
end
body do
h1 title
h2 sub
div "Oh, Toner! How it doth trickle down the arms!"
end
end
The evil part is how the namespaces wash together. Check out that fifth line. The title method and title var cozy up just fine. A huge problem with this code is that methods in the block’s scope still take precedence. (Try adding: def title; end right before the first line.)
You can use self explicitly in those cases. Anyway, the full test script is here. (Hack inspired by override_method.)
Update: mel has noted that most of what cloaker does can be done with instance_eval. MenTaL has noted that 1.9’s instance_exec will do it all.
make 'include' a little more flexible
by allowing to rename or exclude specific methods.
He proposed the following syntax:
module TFoo
def method_a
"hello"
end
def method_b
"world"
end
end
module TBar
def method_a
"goodbye"
end
def method_b
"cruel world"
end
def method_y
"huh?"
end
end
class MyKlass
include TFoo,
:alias => {:method_a, :method_z},
:exclude => :method_b
include TBar
end
m = MyKlass.new
m.method_a # => "goodbye"
m.method_b # => "cruel world"
m.method_y # => "huh?"
m.method_z # => "hello"
Making it happen
Here's my implementation. I think somebody might have posted such a
thing to ruby-talk, but anyway this must have been written for the first
time by some Japanese Rubyist years ago, so yet another reinvention
won't matter nor hurt.
This definition of Class#include should be a
fairly safe replacement for the default one since I'm not raising an
exception when there's a nameclash (which I rather feel is a bad idea,
but I haven't given this much thought), but maybe it could make sense to
create a new method with those semantics.
The code:
include_and_rename.rb
class Class
old_include = instance_method(:include)
define_method(:include) do |*args|
default = {:alias => {}, :exclude => []}
hash_arg = (Hash === args.last) ? default.update(args.pop) : default
m = Module.new
args.each{|mod| m.module_eval{ include mod } }
# ^^^^^^^^^^^^^^^
# check for "method shadowing" here and raise an exception or
# something if you feel like it
hash_arg[:alias].each_pair do |old, new|
m.module_eval{ alias_method(new, old); undef_method(old) }
end
excluded = (Array === hash_arg[:exclude]) ? hash_arg[:exclude] : [hash_arg[:exclude]]
# [*hash_arg[:exclude]] won't work on 1.9 cause there's no Object#to_a
excluded.each{|meth| m.module_eval { undef_method(meth) } }
old_include.bind(self).call(m)
end
end
One Small Flask of Metaprogramming Elixir
#
We could make metaprogramming easier for the common streetsfolk. I’m not giving a triumphant answer here. I’m just wondering what else you knobbly ducks out there have done like this.
So you have this class which defines a structure for scripts within a larger program (MouseHole in this case):
class UserScript
attr_accessor :title, :description, :version, :mount,
:register_uri, :rewrite
def initialize
@register_uri, @rewrite = [], {}
end
end
Nothing special. But the initialize does tell us that register_uri and rewrite are an array and a hash, respectively.
Use a method_missing to set the accessors based on their defaults in initialize:
meta_make(UserScript) do
title "MouseCommand"
description "A-wiki-and-a-shell-in-one."
version "2.0"
mount :cmd do
%{<html> .. </html>}
end
rewrite HTML, XML do
document.change_someways!
end
rewrite CSS do
document.gsub! '.title', '.titleSUPERIOR'
end
# allow methods to be defined
def to_gravy; end
end
So, since rewrite was set up in initialize as a hash, the definition above will result in:
@title = "MouseCommand"
@description = "A-wiki-and-a-shell-in-one."
@version = "2.0"
@mount = [:cmd, #<Proc:903e>],
@rewrite = {
HTML => #<Proc:0xf140>,
XML => #<Proc:0xf140>,
CSS => #<Proc:0x34a3>
}
It’s very Railsish, but as people are now so accustomed to that syntax, it could ease transition of Railsers to your project, whatever it be.
Here’s the meta_make method:
def meta_make(klass, &blk)
o = klass.new
(class << o; self; end).instance_eval { @__obj = o }
class << o
def self.method_missing(m, *args, &blk)
set_attr(m, *args, &blk)
end
def self.set_attr(m, *args, &blk)
if blk
args << nil if args.empty?
args << blk
end
v = @__obj.instance_variable_get("@#{m}")
if v.respond_to? :to_ary
(v = v.to_ary).push args
elsif v.respond_to? :to_hash
v = v.to_hash
while args.length > 1
v[args.unshift] = args.last
end
elsif args.length <= 1
v = args.first
else
v = args
end
@__obj.instance_variable_set("@#{m}", v)
end
end
(class << o; self; end).class_eval &blk
o
end
Eval-less Metaprogramming
#
I often find myself resorting to eval when writing class methods for metaprogramming, simply because define_method hasn’t penetrated my brain basket yet. No more!
Here goes Florian GroBB changing Ruby as I know it—yet again—with his implementation of an eval-less has_many class method:
class Meta
def self.has_many( sym )
ivar = :"@#{ sym }"
define_method( sym.to_sym ) do
new_value = instance_variable_get( ivar ) || []
instance_variable_set( ivar, new_value )
new_value
end
end
end
Even though has_many is a sorta poor name (we’re not talking about databases here), this method acts like an attr method, but the attribute is always an Array.
class Person < Meta
attr_accessor :name
has_many :friends
def initialize( name ); @name = name; end
end
>> fred = Person.new( "Fred" )
>> joe = Person.new( "Joe" )
>> teagle = Person.new( "T. Eagle" )
>> teagle.friends << fred
>> teagle.friends << joe
=> [#<Person:0x80f3ebc @name="Fred">, #<Person:0x81fd404 @name="Joe">]
Seeing Metaclasses Clearly
If you’re new to metaprogramming in Ruby and you’d like to start using it, perhaps these four methods could give you a bit more vision.
class Object
# The hidden singleton lurks behind everyone
def metaclass; class << self; self; end; end
def meta_eval &blk; metaclass.instance_eval &blk; end
# Adds methods to a metaclass
def meta_def name, &blk
meta_eval { define_method name, &blk }
end
# Defines an instance method within a class
def class_def name, &blk
class_eval { define_method name, &blk }
end
end
I’ve been keeping these methods in a file called metaid.rb and it’s a start toward building a little library that can simplify use of metaclasses. Let’s talk about metaclasses and I advise you to keep metaid.rb at your side. Take time to run some code from this article and you’ll understand much better.
About Classes
Well, what is a Class? Let’s create a simple object and see.
>> class MailTruck
>> attr_accessor :driver, :route
>> def initialize( driver, route )
>> @driver, @route = driver, route
>> end
>> end
>> m = MailTruck.new( "Harold", ['12 Corrigan Way', '23 Antler Ave'] )
=> #<MailTruck:0x81cfb94 @route=["12 Corrigan Way", "23 Antler Ave"],
@driver="Harold">
>> m.class
=> MailTruck
An Object is storage for variables. Instance variables. A MailTruck object, once initialized, will have a @driver and a @route variable. It can hold any other variables as well.
>> m.instance_variable_set( "@speed", 45 )
=> 45
>> m.driver
=> "Harold"
Okay, so the @driver instance variable has an accessor. When Ruby sees attr_accessor :driver in the MailTruck class definition, you get reader and writer methods. The methods driver and driver=.
These methods are stored in the class. So the instance variable is in the object and the accessor methods are in the class. They’re in two completely different spots.
It’s an important lesson: objects do not store methods, only classes can.
Classes Are Objects
Okay, but classes are objects, right? I mean if everything is an object in Ruby, then classes and objects should both be objects. Which makes them the same?
Sure, classes are objects. You can run all the same methods on classes that you can run on object. Look, they each have their own ID in the symbol table.
>> m.object_id
=> 68058570
>> MailTruck.object_id
=> 68069450
But I’ve already told you: classes store methods. They’re different. Now I know you’re probably a bit confused wondering, “If a class is an object, but objects are built on classes, isn’t there a big confusing infinite cycle here that you’re not explaining?”
No, there’s not. I hate to break it to you, but a class isn’t really an object. From Ruby’s source code:
struct RObject {
struct RBasic basic;
struct st_table *iv_tbl;
};
struct RClass {
struct RBasic basic;
struct st_table *iv_tbl;
struct st_table *m_tbl;
VALUE super;
};
Look! A class has an m_tbl (a symbol table for storing methods) and a superclass (pointer to a superclass).
But let me reassure you. To a Ruby programmer, a class is an object. Because it meets the two big criteria: you can store instance variables in a class and it is descended from the Object class. That’s it.
>> o = Object.new
=> #<Object:0x815c45c>
>> o.class
=> Object
>> Class.superclass.superclass
=> Object
>> Object.class
=> Class
>> Object.superclass
=> nil
The Object class sits at the very head of the table and comes down to participate only when it has methods that can’t be found anywhere else.
What On Earth Are Metaclasses?
The term metaclass is supposed mean “a class which defines classes.” This definition doesn’t really work with Ruby, though, since “a class which defines a class” is simply: a Class.
Look at how you can add a method in the Class class and then use it in class definitions.
>> class Class
>> def attr_abort( *args )
>> abort "Please no more attributes today."
>> end
>> end
>>
>> class MyNewClass
>> attr_abort :id, :diagram, :telegram
>> end
Which prints Please no more attributes today. The attr_abort method can be used in definitions.
You’re constantly defining and redefining classes in Ruby. It’s not meta, it’s just part of the code. Classes hold methods. How can you complicate that?
Since the earlier definition doesn’t really work, I like to think of the Ruby metaclass as “a class which an object uses to redefine itself.”
Do Objects Need Metaclasses?
Objects can’t hold methods. Most objects don’t need to hold methods.
But sometimes you may want an object to have some methods. Sometimes that’s your answer to a problem. You can’t do that. But Matz has given us metaclasses which are good enough.
In the YAML library, you can customize the properties shown when an object is output.
>> require 'yaml'
>> class << m
>> def to_yaml_properties
>> ['@driver', '@route']
>> end
>> end
>> YAML::dump m
--- !ruby/object:MailTruck
driver: Harold
route:
- 12 Corrigan Way
- 23 Antler Ave
This is handy if you want to dump one specific object with a certain style of YAML without effecting every object from that class. In the above example, only the object in the m variable will be output with its properties in order. All other MailTruck objects will be output in whatever way the YAML library chooses. Sometimes we may want to display a certain string one way without needing to modify the String class (which affects every string in your code).
So the object in the m variable has its own special to_yaml_properties method. It’s stored in a metaclass. The metaclass stores methods for the object and sits right in the inheritance chain.
We could also add the to_yaml_properties method with this convenient syntax:
def m.to_yaml_properties
['@driver', '@route']
end
If you have the metaid.rb methods from the top of this article loaded, try this:
>> m.metaclass
=> #<Class:#<MailTruck:0x81cfb94>>
>> m.metaclass.class
=> Class
>> m.metaclass.superclass
=> #<Class:MailTruck>
>> m.metaclass.instance_methods
=> [..., "to_yaml_properties", ...]
>> m.singleton_methods
=> ["to_yaml_properties"]
When you use the class << m syntax, you’re opening up a metaclass. Ruby calls these virtual classes. Notice the result of m.metaclass. A class attached to an object: #<Class:#<MailTruck:0x81cfb94>>.
When an object finds methods in an attached metaclass, these methods are referred to as the object’s singleton methods rather than the object’s metaclass’ instance methods (if you get my drift.) And since there can only be a single metaclass attached to an object, it’s called a _single_ton.
It’s much easier to see metaclasses when you’re using the metaclass method. Normally, you would need to use ( class << self; self; end ) wherever you wanted to root out a metaclass. But this makes it much simpler.
Do Metaclasses Need Metaclasses?
>> m.metaclass.metaclass
=> #<Class:#<Class:#<MailTruck:0x81cfb94>>>
>> m.metaclass.metaclass.metaclass
=> #<Class:#<Class:#<Class:#<MailTruck:0x81cfb94>>>>
Check out those frivolous metaclasses we’re creating. So what can we do with a metaclass of a metaclass?
Well, the same thing we do with a normal metaclass. A normal metaclass holds methods for an object. So a metaclass of a metaclass holds methods for that metaclass—which is just an object, of course!
The problem with a metaclass of a metaclass is that there’s not much practical use for them. You can only use the methods if you’re deep inside the chain and we don’t really want to spend much time down there.
>> m.meta_eval do
>> self.meta_eval do
>> self.meta_eval do
>> def ribbit; "*ribbit*"; end
>> end
>> end
>> end
>> m.metaclass.metaclass.metaclass.singleton_methods
=> ["class_def", "metaclass", "constants", "meta_def",
"attr_test", "nesting", "ribbit"]
Metaclasses are only really useful one level deep. You want to have give methods to an object. Or, as you will see, you might want a specific class to have a metaclass. Beyond that, you’re just storing methods in these obscure metaclasses that no one can really get at. Which you might need to do sometime. Who knows.
The important thing to know at this point is: metaclasses don’t go up, they go out. Yes, when you create a metaclass for an object, it happens to intercept method calls before the object’s inheritance chain. But that doesn’t mean inheritance is affected by further metaclasses. When you create a metaclass of a metaclass, it has no affect on the object referred to by the original metaclass.
Metaclasses Have One More Funky Trick For Classes and It’s The Crucial Trick In The Metaprogrammer’s Handbook
One more point and I believe this one is the juiciest. If you read the rest of this essay and quit before this section, you’ve come away without the most important lesson. You may know some nice things about objects and metaclasses, but it all pales.
I’m going to reiterate two previous statements about classes and build on them.
- Class are objects. This means they can hold instance variables.
- Metaclasses hold instance methods. When attached to an object, these methods become singleton methods. These methods intercept calls before they trickle up the chain of inheritance.
Have you ever used instance variables in a class before? I don’t mean in a class method. I mean in the class itself.
class MailTruck
@trucks = []
def MailTruck.add( truck )
@trucks << truck
end
end
Why not just use a class variable?
class MailTruck
@@trucks = []
def MailTruck.add( truck )
@@trucks << truck
end
end
They work exactly the same, right? I mean it doesn’t matter, does it?
Here are two reasons you’ve probably been using class variables rather than class instance variables:
- Class variables are clearly class variables. They have two at-symbols. Less confusion.
- Class variables can be referenced in instance methods, if needed.
See, this works properly:
class MailTruck
@@trucks = []
def MailTruck.add( truck )
@@trucks << truck
end
def say_hi
puts "Hi, I'm one of #{@@trucks.length} trucks!"
end
end
But this does not:
class MailTruck
@trucks = []
def MailTruck.add( truck )
@trucks << truck
end
def say_hi
puts "Hi, I'm one of #{@trucks.length} trucks!"
end
end
So what are instance variables good for? What a waste of space! I’m never using them again! (Yes, please stick to class variables in situations like the above.)
Let me also point out that metaclasses are again showing up above, since every class method is stored in a metaclass. That’s simply just how it works.
Which is why you can also use self:
class MailTruck
def self.add( truck )
@@trucks << truck
end
end
Or the singleton syntax:
class MailTruck
class << self
def add( truck )
@@trucks << truck
end
end
end
Class instance variables and metaclass instance methods are really pretty pointless in a plain old class. But when inheritance enters the mix, the party comes alive. Writhing bodies and drunken madness, believe me.
class MailTruck
def self.company( name )
meta_def :company do; name; end
end
end
The above method is remarkably simple, but excavates a beachhead worth of possibilities. A new company class method is added to MailTruck that can be used in a class definition.
class HappyTruck < MailTruck
company "Happy's -- We Bring the Mail, and That's It!"
end
Okay, so the company class method gets executed with the Happy’s company name and slogan. What does meta_def do with it??
Well, the meat of meta arrives here. The meta_def adds a new method called company to the HappyTruck metaclass. The beauty of this is that the method is not added to the MailTruck metaclass, but to the derived class HappyTruck.
This may seem simple, but it’s very powerful. You can write simple class methods which will add class methods to a derived class. This is the secret to Rails and Ruby/X11 and so many other examples of metaprogramming in Ruby.
Dwemthy’s Array
I discovered most of this while building Dwemthy’s Array for my cartoon Ruby book. I was able to simplify the Creature code (which gives a readability to the RPG) down to this fragment:
class Creature
def self.traits( *arr )
return @traits if arr.empty?
attr_accessor *arr
arr.each do |trait|
meta_def trait do |val|
@traits ||= {}
@traits[trait] = val
end
end
class_def :initialize do
self.class.traits.each do |k,v|
instance_variable_set( "@#{k}", v )
end
end
end
end
The meta_def and class_def help make the metaprogramming a bit more clear. Pay close attention to the use of instance variables in the meta_def. If you want to understand just why class variables won’t work in this situation, then try changing the instance variables to class variables above.
Then, start creating monsters as described on the Dwemthy’s Array page and you’ll watch them step all over each other.
Hatching New Methods in Mid-air
#
Ken Miller sent in a very interesting class, which creates new methods as they are needed. And, also, the respond_to? method is hacked to respond positively in these cases. I could see this kind of thing going into Rails so you can Student.find_classes or Student.find_grades. That sort of thing.
class MethodMagic
alias_method :old_respond_to?, :respond_to?
def respond_to? (method, include_private=false)
old_respond_to?(method.to_sym, include_private) ||
create_dynamic_method(method)
end
alias_method :old_method_missing, :method_missing
def method_missing(method, *args)
if create_dynamic_method(method)
send(method.to_sym, *args)
else
old_method_missing(method)
end
end
# If the method name conforms to our rigorous specifications,
# create a new body on the fly and define the sucker
def create_dynamic_method(method)
if method.to_s.match /^find_by_/
self.class.send(:define_method, method.to_sym) { |*args|
puts "Called #{method}(#{args.join ", "})"
}
true
else
false
end
end
# just here as an example of a normal method
def find; end
end
mm = MethodMagic.new
p mm.respond_to?(:find) # => true
p mm.respond_to?(:find_by_letters) # => true
mm.find_by_letters("a", "b", "c") # prints 'Called find_by_letters(a, b, c)'
mm.find_by_numbers(1, 2, 3) # prints 'Called find_by_numbers(1, 2, 3)'
p mm.respond_to?(:poopy) # => false
mm.poopy # raises NoMethodError
What about you? Do you have any metaprogramming tricks for us? If nothing else, read Ruby/X11 source and get back to me.
# Adventure.rb
# This is a one to one translation of the lisp tutorial at
#
# http://www.lisperati.com/casting.html
#
# into the ruby programming language.
#
# It does not use much of ruby's beauty, because it tries to be as close to the
# original as possible. It is also tweaking the repl to use it directly as the
# interface. So you could do some cool things. e.g. enter
#
# 10.times do
# dirs = look[1].map { | desc | desc[/going (.*?) from here./, 1].to_sym }
# puts walk dirs[rand(dirs.length)]
# end
#
# for some random strolling through the world.
#
# Using a method_missing hack it is possible to enter things like this into irb
#
# splash bottle frog
#
# which is translated into
#
# splash([:bottle, :frog])
#
# This code is under the ruby licence.
#
# The code can be found at http://ruby.brian-schroeder.de/quiz/adventure/
module Kernel
#(setf *objects* '(whiskey-bottle bucket frog chain))
Objects = [:whiskey_bottle, :bucket, :frog, :chain]
#(setf *map* '((living-room (you are in the living-room of a wizards house. there is a wizard snoring loudly on the couch.)
# (west door garden)
# (upstairs stairway attic))
# (garden (you are in a beautiful garden. there is a well in front of you.)
# (east door living-room))
# (attic (you are in the attic of the wizards house. there is a giant welding torch in the corner.)
# (downstairs stairway living-room))))
Map = {
:living_room => {:description => "You are in the living-room of a wizards house. there is a wizard snoring loudly on the couch.",
:connections => {:west => [:door, :garden],
:upstairs => [:stairway, :attic]}},
:garden => {:description => "You are in a beautiful garden. there is a well in front of you.",
:connections => {:east => [:door, :living_room]}},
:attic => {:description => "You are in the attic of the wizards house. there is a giant welding torch in the corner.",
:connections => {:downstairs => [:stairway, :living_room] }}
}
#(setf *object-locations* '((whiskey-bottle living-room)
# (bucket living-room)
# (chain garden)
# (frog garden)))
ObjectLocations = {
:whiskey_bottle => :living_room,
:bucket => :living_room,
:chain => :garden,
:frog => :garden
}
#(setf *location* 'living-room)
#(setf *chain-welded* nil)
#(setf *bucket-filled* nil)
State = {
:location => :living_room,
:chain_welded => false,
:bucket_filled => false
}
#(defun describe-location (location map)
# (second (assoc location map)))
def describe_location(location, map)
Map[location][:description]
end
#(defun describe-path (path)
# `(there is a ,(second path) going ,(first path) from here.))
#
#(defun describe-paths (location map)
# (apply #'append (mapcar #'describe-path (cddr (assoc location map)))))
def describe_paths(connections)
connections.to_a.map { | (direction, (passage, next_location)) |
"There is a #{passage} going #{direction} from here."
}
end
#(defun is-at (obj loc obj-loc)
# (eq (second (assoc obj obj-loc)) loc))
def is_at(object, location, object_locations)
object_locations[object] == location
end
#(defun describe-floor (loc objs obj-loc)
# (apply #'append (mapcar (lambda (x)
# `(you see a ,x on the floor.))
# (remove-if-not (lambda (x)
# (is-at x loc obj-loc))
# objs))))
def describe_floor(location, objects, object_locations)
objects.select { | object | is_at(object, location, object_locations) }.
map { | object | "You see a #{object} on the floor." }
end
#(defun look ()
# (append (describe-location *location* *map*)
# (describe-paths *location* *map*)
# (describe-floor *location* *objects* *object-locations*)))
def look
[describe_location(State[:location], Map),
describe_paths(Map[State[:location]][:connections]),
describe_floor(State[:location], Objects, ObjectLocations)]
end
#(defun walk-direction (direction)
# (let ((next (assoc direction (cddr (assoc *location* *map*)))))
# (cond (next (setf *location* (third next)) (look))
# (t '(you cant go that way.)))))
def walk(direction)
next_state = Map[State[:location]][:connections][direction]
if next_state
State[:location] = next_state[1]
look
else
"You can't go that way."
end
end
#(defmacro defspel (&rest rest) `(defmacro ,@rest))
def method_missing(method_id, *args)
if args.empty?
method_id
else
[method_id] + args
end
end
#(defspel walk (direction)
# `(walk-direction ',direction))
# We need only the method missing hack from above
#(defun pickup-object (object)
# (cond ((is-at object *location* *object-locations*) (push (list object 'body) *object-locations*)
# `(you are now carrying the ,object))
# (t '(you cannot get that.))))
def pickup(object)
if is_at(object, State[:location], ObjectLocations)
ObjectLocations[object] = :body
"You are now carrying the #{object}"
else
"You cannot get that."
end
end
#(defspel pickup (object)
# `(pickup-object ',object))
# We need only the method missing hack from above
#(defun inventory ()
# (remove-if-not (lambda (x)
# (is-at x 'body *object-locations*))
# *objects*))
def inventory
Objects.select { | object | ObjectLocations[object] == :body }
end
#(defun have (object)
# (member object (inventory)))
def have(object)
inventory.include?(object)
end
# extend self
#(defspel game-action (command subj obj place &rest rest)
# `(defspel ,command (subject object)
# `(cond ((and (eq *location* ',',place)
# (eq ',subject ',',subj)
# (eq ',object ',',obj)
# (have ',',subj))
# ,@',rest)
# (t '(i cant ,',command like that.)))))
def define_game_action(command, subject, object, place, &action)
puts "Defining #{command}"
p self
define_method(command) do | *args |
subject_, object_ = *args.flatten
if State[:location] == place and
subject == subject_ and
object == object_ and
have(subject)
instance_eval &action
else
"I can't #{command} like that."
end
end
end
#(game-action weld chain bucket attic
# (cond ((and (have 'bucket) (setf *chain-welded* 't))
# '(the chain is now securely welded to the bucket.))
# (t '(you do not have a bucket.))))
define_game_action(:weld, :chain, :bucket, :attic) do
if have :bucket
State[:chain_welded] = true
"The chain is now securely welded to the bucket."
else
"You do not have a bucket."
end
end
#(game-action dunk bucket well garden
# (cond (*chain-welded* (setf *bucket-filled* 't) '(the bucket is now full of water))
# (t '(the water level is too low to reach.))))
define_game_action(:dunk, :bucket, :well, :garden) do
if State[:chain_welded]
State[:bucket_filled] = true
"The bucket is now full of water"
else
"The water level is too low to reach."
end
end
#(game-action splash bucket wizard living-room
# (cond ((not *bucket-filled*) '(the bucket has nothing in it.))
# ((have 'frog) '(the wizard awakens and sees that you stole his frog.
# he is so upset he banishes you to the
# netherworlds- you lose! the end.))
# (t '(the wizard awakens from his slumber and greets you warmly.
# he hands you the magic low-carb donut --- you win! the end.))))
define_game_action(:splash, :bucket, :wizard, :living_room) do
if not State[:bucket_filled]
"The bucket has nothing in it."
elsif have :frog
["The wizard awakens and sees that you stole his frog.",
"He is so upset he banishes you to the netherworlds -- you lose!",
" === The End ==="]
else
["The wizard awakens from his slumber and greets you warmly.",
"He hands you the magic low-carb donut --- you win!",
" === The End ==="]
end
end
def help
puts %(You can do the following things
- look
- pickup
- walk
- weld
- dunk
- splash
)
end
end
if __FILE__ == $0
require 'irb'
IRB.start
end
# Refactored adventure.rb
# This is a refaktored version of the lisp tutorial at
#
# http://www.lisperati.com/casting.html
#
# into the ruby programming language.
#
# This code is under the ruby licence.
#
# The code can be found at http://ruby.brian-schroeder.de/quiz/adventure/
# A Thing in the adventure world. Parallel to an object in the lisp version.
Thing = Struct.new(:name, :description, :portable)
# Maps objects that have a key to the object using the key converted by a
# block. If no block is given the key is converted using to_s.downcase
#
# This is a method that generates a FuzzyIndexedList class specialized for one
# index and a transformation proc
def FuzzyIndexedList(key, &key_transform_proc)
key = key.to_sym
key_transform_proc = key_transform_proc || lambda { | x | x.to_s.downcase }
result = Class.new
result.module_eval do
include Enumerable
def initialize
@data = {}
end
define_method(:<<) do | object |
@data[key_transform_proc[object.send(key)]] = object
self
end
define_method(:[]) do | name |
@data[key_transform_proc[name]]
end
define_method(:delete) do | key_or_object |
key_or_object = key_or_object.send(key) if key_or_object.respond_to?key
@data.delete(key_transform_proc[key_or_object])
end
def each
@data.each do | _, object | yield object end
end
end
result
end
ThingList = FuzzyIndexedList(:name)
Connection = Struct.new(:direction, :passage, :next_room)
ConnectionList = FuzzyIndexedList(:direction)
class Room
attr_reader :name, :description, :connections, :things
def initialize(name, description = "")
@name = name
@description = description
@connections = ConnectionList.new
@things = ThingList.new
yield self if block_given?
end
end
Map = FuzzyIndexedList(:name)
# We can inherit adventures from this class. WizardsHouse is the adventure from
# the lisp tutorial. A game action can be defined by a triplet of functions
# named
# action_name
# action_name_help
# action_name_complete
# where the first defines the action, the second returns a description of the
# action and a usage string and the third is used for auto completion.
#
# To see this in action take a look at the #action_look_at,
# #action_look_at_help and #action_look_at_complete functions.
#
# The adventure is interpreted by the Interpreter class using a readline
# interface.
class Adventure
attr_reader :map, :location, :held_things
def commands
methods.select{ | method | /^action_/ =~ method and not /action_(.*)(_complete|_help)/ =~ method }.map{ | method | method.sub(/^action_/, '') }.sort
end
def location=(location)
location = location.name if location.respond_to?:name
raise "Location not in map" unless @map[location]
@location = @map[location]
end
# Create the adventure world
def initialize
@map = Map.new
@held_things = ThingList.new
end
def describe_location
@location.description
end
def describe_paths
@location.connections.map { | connection |
"There is a #{connection.passage} going #{connection.direction} from here."
}
end
def describe_floor
@location.things.map { | object | "You see #{object.description}." }
end
def have(object)
@held_things[object]
end
def action_help
descriptions = commands.map { | cmd | respond_to?("action_#{cmd}_help") ? send("action_#{cmd}_help") : ["Description missing for #{cmd}", ""] }
max_usage_width = descriptions.map { | _, usage | usage.length }.max
max_description_width = descriptions.map { | description, _ | description.length }.max
[ 'You can do the following things',
"",
" Usage ".ljust(max_usage_width+4) + "Description",
"-" * (max_usage_width + 4 + max_description_width) ] +
descriptions.map { | description, usage | " #{usage}".ljust(max_usage_width+4) + description }
end
def action_help_help
["Show this help", "help"]
end
# Helper function to create actions that are applied to two objects and are
# dependent on a specific place.
#
# Defines all three helper functions for the action, though the help
# description is a bit ugly.
def self.def_combine_action(command, subject, object, place, &block)
#define_method("__action_#{command}", &block)
#private "__action_#{command}"
define_method("action_#{command}") do | subject_, object_ |
if have(subject) and
self.location.name == place and
subject_.to_s.downcase == subject.to_s.downcase and
object_.to_s.downcase == object.to_s.downcase
instance_eval(&block)
else
"I can't #{command} like that."
end
end
define_method("action_#{command}_help") do
["#{command} subject with object", "#{command} subject object"]
end
define_method("action_#{command}_complete") do | *args |
return false if args.length > 2
(self.location.things.to_a + self.held_things.to_a).map { | object | object.name }.grep(/^#{args[-1]}/i)
end
end
end
class WizardsHouse < Adventure
def initialize
super
# Define Rooms and things
map <<
Room.new(:living_room, "You are in the living-room of a wizards house. There is a wizard snoring loudly on the couch.") do | room |
room.things <<
Thing.new("Bucket", "a wooden bucket with a metal handle", true) <<
Thing.new("Bottle", "an empty whiskey bottle", true) <<
Thing.new("Wizard", "a completely drunken wizard snoring loudly on the couch", false) <<
Thing.new("Couch", "a couch whereon a drunken wizard lies", false)
end <<
Room.new(:garden, "You are in a beautiful garden. There is a well in front of you.") do | room |
room.things <<
Thing.new("Chain", "a rusty old chain", true) <<
Thing.new("Frog", "a vivid colored magic frog singing a happy frog-song", true) <<
Thing.new("Well", "an old well with a wooden hut above", false)
end <<
Room.new(:attic, "You are in the attic of the wizards house. There is a giant welding torch in the corner.") do | room |
room.things <<
Thing.new("Window", "a window showing that nothingness surrounds the wizards house", false) <<
Thing.new("Torch", "a giant welding torch built into the corner of the attic. This may come handy", false)
end
# Define Connections
map[:living_room].connections <<
Connection.new(:west, :door, map[:garden]) <<
Connection.new(:upstairs, :stairway, map[:attic])
map[:garden].connections <<
Connection.new(:east, :door, map[:living_room])
map[:attic].connections <<
Connection.new(:downstairs, :stairway, :living_room)
self.location = :living_room
@chain_welded = false
@bucket_filled = false
end
# Define actions
def action_look
[ describe_location, "",
describe_floor, "",
describe_paths ]
end
def action_look_help
["Take a look at the surroundings", "look"]
end
def action_look_at(object)
object = (held_things[object] || location.things[object])
if object
"You look at the #{object.name} and see #{object.description}."
else
"I do not know where to look"
end
end
def action_look_at_help
["Take a look at something in the room or in your inventory", "look_at object"]
end
def action_look_at_complete(*args)
return false if args.length > 1
(held_things.to_a + location.things.to_a).map { | thing | thing.name }.grep(/^#{args[0]}/i)
end
def action_walk(direction)
connection = self.location.connections[direction]
if connection
self.location = connection.next_room
action_look
else
"You can't go that way."
end
end
def action_walk_help
["Walk into the room adjoining in the given direction", "walk direction"]
end
def action_walk_complete(*args)
return false if args.length > 1
self.location.connections.map { | connection | connection.direction }.grep(/^#{args[0]}/i)
end
def action_pickup(object)
object = self.location.things[object]
if object and object.portable
self.held_things << self.location.things.delete(object)
"You are now carrying the #{object.name}"
else
"You cannot get that."
end
end
def action_pickup_help
["Take an object from the world", "pickup object"]
end
def action_pickup_complete(*args)
return false if args.length > 1
self.location.things.map { | object | object.name }.grep(/^#{args[0]}/i)
end
def action_drop(object)
object = self.held_things[object]
if object
self.location.things << self.held_things.delete(object)
"You have dropped the #{object.name}"
else
"You do not have that."
end
end
def action_drop_help
["Litter the world with things you carry", "drop object"]
end
def action_drop_complete(*args)
return false if args.length > 1
self.held_things.map { | object | object.name }.grep(/^#{args[0]}/i)
end
def action_inventory
result = self.held_things.map{ | object | "#{object.name} - #{object.description}" }
"You carry nothing." if result.empty?
end
def action_inventory_help
["Take a look at your inventory", "inventory"]
end
def_combine_action(:weld, "Chain", "Bucket", :attic) do
if have("Bucket")
@chain_welded = true
held_things[:bucket].description = "An empty bucket with a long chain"
held_things.delete(:chain)
"The chain is now securely welded to the bucket."
else
"You do not have a bucket."
end
end
def_combine_action(:dunk, "Bucket", "Well", :garden) do
if @chain_welded
@bucket_filled = true
held_things[:bucket].description = "A bucket full of water"
"The bucket is now full of water"
else
"The water level is too low to reach."
end
end
def_combine_action(:splash, "Bucket", "Wizard", :living_room) do
if not @bucket_filled
"Splashing an empty bucket on the wizard has no effekt."
elsif have("Frog")
["The wizard awakens and sees that you stole his frog.",
"He is so upset he banishes you to the netherworlds -- you lose!",
" === The End ==="]
else
["The wizard awakens from his slumber and greets you warmly.",
"He hands you the magic low-carb donut --- you win!",
" === The End ==="]
end
end
end
# Readline interface for the Adventure class.
class Interpreter
def initialize(adventure)
@adventure = adventure
end
# Readline interface with autocompletion. (Has some quirks but helps)
def play
require "readline"
Readline.completer_word_break_characters = ''
Readline.completion_proc = lambda do | line |
line.gsub!(/^\s+/, '')
args = line.downcase.split(/\s+/, -1)
if args.length <= 1
(['exit'] + @adventure.commands).grep(/^#{args[0]}/i)
else
cmd = args.shift
if @adventure.respond_to?("action_#{cmd}_complete")
completes = @adventure.send("action_#{cmd}_complete", *args)
if completes
completes.map { | complete | [line.split(/\s+/, -1)[0..-2], complete].join(" ") }
end
end
end
end
puts @adventure.action_look
while line = Readline.readline("\nWhat do you want to do? ", true)
break if /^exit/ =~ line
next if /^\s*$/ =~ line
begin
args = line.split(/\s+/)
cmd = args.shift
puts @adventure.send("action_#{cmd}", *args)
rescue => e
puts "I could not understand your command. Try help for a list of applicable commands."
p e if $DEBUG
end
end
end
end
if __FILE__ == $0
Interpreter.new(WizardsHouse.new).play
end
How to Send Files in Rails
Posted by TAD
18 hours from now
Setting up a link in your Rails app to allow your users to download any file is incredibly easy. Not just
incredibly easy - OMGWTHBBQ easy! Stuff like this makes Rails so cool!
First, put the file out on your server. I put my "README.txt" file in a "files" folder like this: "rails/
appname/files/README.txt."
Next, set up a link in one of your pages:
<%= link_to 'Get Readme', :action => 'get_readme' %>
Finally, add the "get_readme" action method to your controller:
def get_readme
send_file("files/README.txt", :filename => 'yo_readme.txt')
end
That's it! Now, whenever a user clicks the "Get Readme" link on your page, the file will be sent to the
client. The :filename option simply allows you to suggest a name for the file to your users. I threw it in
just to see how it works. I couldn't believe it was this easy, but sure enough, it is. Very cool.
There are other options like the :filename option which allow you to tell the browser what type of file to expect, how to display the file, and whether or not to stream the download. Streaming is turned on by default and allows downloading of very large files. All of these options go into the same line of code. Take a look at the article at this link.
As usual, expect a quick video demonstration of this posted later tonight.
Figure out what this is doing!
Jamis’ favorite new trick with Ruby hashes:
irb(main):001:0> map = {}
=> {}
irb(main):002:0> node = ((map[:foo] ||= {})[:bar] ||= {})
=> {}
irb(main):003:0> map
=> {:foo=>{:bar=>{}}}
irb(main):004:0>Â
Continuations are an unconventional computer language trick that sometimes provoke curiosity in the Ruby community. The following is JulianSnitow's explanation of them.
Ok, the basics of continuations:
A continuation (in theory) represents "the rest of the program" from the
point that it's created. Imagine you're walking down the street and you
come to a fork in the road. You don't know whether to go left or right,
so you generate a "continuation".
Road
Left-or-right?
generate a continuation called "choice"
| \--Go left
| |---walk around
| |---find a mean dog
| \---get bitten by the mean, terribly hungry dog
| \---that wasn't a good idea.
| \---tell "choice" that left was a bad idea
|
|----according to "choice", left was a bad idea
\----let's go right instead.
Now, the real magic of continuations happens when you have more than one
of them.
Say when you got bitten by the dog, you generated another continuation
called "got bitten", before telling "choice" that left was a bad idea.
So, you're walking along, un-bitten, since you went right at the fork in
the road after all, and suddenly you're hit by an oncoming train.
You decide that getting bitten by the dog was preferable to this, so you
kindly ask the continuation named "got bitten" to take you back to the
alley where you were lying bleeding from the dog bite, but at least
hadn't been hit by a train. ;-)
The moral of the story: When in doubt, just stay home.
For a more in-depth look at continuations, see "Run, Lola, Run", "The
Legend of Zelda: Ocarina of Time", and "Back to the Future: Part II" :-)
Continuations can be used to simulate Python-style generators (which are related to Ruby's iterators). See IterationStyles for a bit more info, including a link to JimWeirich's page containing an actual implementation.
There is a web framework called "SeaSide" (in Squeak Smalltalk, with a Ruby version called "[Borges]" that uses continuations to represent the discrete states encountered as a user navigates through a web application. This enables the framework to evoke previous states if required, also allowing the user to separate multiple what-if scenarios, each in a browser window. See [Use continuations to develop complex Web applications]
An explanation of how continuations are implemented in various languages would be interesting. They don't seem to fit very well into the standard stack-based single-exit-functions model.
--GavinSinclair, 2003-03-25
One easy way to implement continuations: Allocate each "stack" frame on the heap, and let the GC clean it up when (1) the function has returned and (2) no outstanding continuations exist. Each stack frame includes a pointer its calling frame, so the entire call chain will hang around as needed. Your stack is now a branching tree (which occasionally gets pruned), and your program counter jumps back and forth between the branches like an overenthusiastic monkey.
This approach is very similar to how you keep stack frames alive for closures.
You can also implement continuations by using longjmp and memcpy'ing your stack as needed. This tends to perform very badly (see PLT Scheme for a good example), and you need to be clever about how you handle local variables--if you change them on one copy of the stack, you typically want to change them on all copies.
There's a third hack, known as [Cheney on the MTA], which tends to be a little mind-bending, and which the Parrot folks were considering at the LL(n) conference a couple of years ago. Basically, you transform your program to ContinuationPassingStyle? and never return from any functions. When the stack's about to overflow, you just longjmp back to your main loop and continue.
Lots of clever optimizations are possible, of course.
--EricKidd?, 2005-10-10
Here's the best mental model I've found for how continuations work. Calling callcc creates a copy of the call stack. This stack copy remains disabled until such time as you call the Continuation (or return from callcc's block). When that happens, the copy of the stack (or really, another copy of it) is made the current stack (destroying whatever current stack you had at the time), and execution is resumed in that stack as if returning from callcc.
I don't know how they're actually implemented, but to have the correct semantics, some sort of (logical or physical) stack copying must be done.
Since another stack is being created, Continuations are somewhat like threads. However, unlike Threads, Continuations are still essentially sequential in nature. (Using Continuations, only one call stack is ever 'active' at once.) Also, Threads are not usually
created by making a copy of an existing Thread. (Maybe they should be?)
Continuations also bear a great similarity to the old unix syscall fork(). The major difference here is that fork() copies the heap and data segments as well as the stack.
class CircularArray < Array
def [](i)
index = i
if i >= size || i < -1
index = i % size
end
super(index)
end
end
May 20, 2006 { rails }
As I’ve done more development with Rails I’ve become more interested in refactoring and simplifying my code. One of the ways I’ve found to do that is to combine actions in object creation. This means going from having separate “new” and “create” actions to just having single “new” action.
How? The wonder of request.post?.
So let’s say I have an action “new” that lets a user fill out a form to create a new Widget. It might look something like this:
<%= start_form_tag :action => "create" %>
<%= text_field "widget", "name" %>
<%= submit_tag "create" %>
<%= end_form_tag %>
With the corresponding actions in the controller:
def new
@widget = Widget.new
end
def create
@widget = Widget.new(params[:widget])
@widget.save
end
So wouldn’t it be easier to just have one action for this whole process? I’d like to think so. Here’s the combined solution:
Change our form action:
<%= start_form_tag :action => "new" %>
And combine our controller actions:
def new
@widget = Widget.new
if request.post?
@widget = Widget.new(params[:widget])
@widget.save
end
end
Now our form redirects to itself, but it passes along the user entered form information as a post request. By checking for the presence of the post information in the request we can determine if the call of the “new” action is the first initial load or a user submit click.
The result is one less action in the controller and a simpler process for creating a new Widget. The process is the same for combining Edit/Update actions into a single Edit action.
Parameters vs arguments
In everyday usage, “parameter” and “argument” are used interchangeably to refer to the things that you use to define and call methods or functions.
Often this interchangeability doesn’t cause ambiguity. It should be noted, though, that conventionally, they refer to different things.
A “parameter” is the thing used to define a method or function while an “argument” is the thing you use to call a method or function.
Parameter:
def foo(param) ... end
Argument:
foo(arg) # => obj
Ultimately, it doesn’t really matter what you say. People will understand from the context.
Ruby Meta-Programming: Software Contracts
Update: I posted an updated version of this code sample on my projects page. The new version allows for custom contract definitions and looks a *lot* cleanerWhen I was an undergrad, I spent a summer working on PLT Scheme. My project was to add software-enforced contracts to the HtDP learning languages. Using the awesome power of Scheme's hygienic macros, I was able to let students specify their own contracts in their code, and signal runtime errors when they were violated. Anyway, on to what this post is really about: I've been teaching myself Ruby for about a week now. It has some pretty cool meta-programming features that let you write things that give you similar functionality as a macro in Scheme or Lisp would. Moreover, Ruby (like Scheme) is dynamically typed-- in other words, if I tried to pass a string into a function that expects a number, nothing will go wrong until I try to do something to it that only numbers can do. A software contracts system for Ruby would help programmers catch these bugs early. So I was thinking about this, and I wondered: "How hard would it be to build a similar system for Ruby?" The answer: not too hard at all. There are a few systems out there that do this already, such as the ruby-contract library, and a proof-of-concept by Andrew Hunt (of "Pragmatic Programmer" fame). Keeping with the hacker spirit, I decided to build a VERY simplified contract library as a to explore meta-programming in Ruby. Here's my approach: I'll define a module Contracts, that when mixed into your class, would let you call a function 'contract' that takes two arguments: the name of the function the contract applies to and the type of its argument. Ideally, it would work this way: # our testcase require "contracts" class TestContracts extend Contracts contract :hello, :number def hello(x) x.times { puts "hello!" } end end
t = Test_contracts.new t.hello(2) t.hello("asfsad") # output: # ruby test-contracts.rb #hello! #hello! #test-contracts.rb:18: contract violation: argument of #function 'hello' must be a(n) number (Contracts::ContractError)
Cool. Let's see if we can make it happen (it turns out I couldnt, exactly). Let's start with this contract function: It will rename the function given as its first argument to something else, and then define a new function that checks the contract and then calls the original function. For example, the above code would result in something like this being added to the TestContracts class: # our goal ... def hello(x) if (x.kind_of?(Integer)) then return __hello(x) end raise StandardError, "contract violation" end def __hello(x) x.times { puts "hello!" } end ...
The renaming aspect is simple: Ruby provides the "alias_method" function in the Module class that does this. Introducing the new bindings into the calling class can be accomplished by using the "class_eval" method from Module. All that is left is translating ":number" into the test for the if statement and generating the "hello" function's name, which is simple enough to do. Lets look at the resulting code: #implementation 1 module Contracts def contract(methodname, inputtype) renamed = rename(methodname) tester = parse_type(inputtype) class_eval <<-endofeval alias_method #{renamed.inspect}, #{methodname.inspect} def #{methodname}(arg) if #{tester}.call(arg) then return #{renamed}(arg) end raise ContractError, "contract violation: argument of function '#{methodname}' must be a(n) #{inputtype}", caller end endofeval end class ContractError < StandardError end def parse_type(s) case s when :number then "proc {|x| x.kind_of?(Integer)}" end end def rename(s) ("__" + s.id2name).intern end end
And everything works as planned: When contract is called, we compute the new name for the "hello" function and the test we need to call on the argument. Then using class_eval, we put all of the pieces togther, and produce code very similar to the "goal" code above. A slight problem: If we run the test case exactly as above, we get the following error message: ./contracts.rb:6:in `class_eval': (eval):1:in `alias_method': undefined method `hello' for class `Test_contracts' (NameError) from (eval):1:in `class_eval' from ./contracts.rb:6:in `class_eval' from ./contracts.rb:6:in `contract' from test-contracts.rb:6
??!?! But hello is right there! Actually, its not-- when the "contract" method is called, the definition of hello hasnt been executed yet... If we move the call to "contract" after the definition of "hello," the code works as we would like, and we are all happy. This probably isnt a big deal, but we would like to keep the contract call as close to the definition as possible, which is right BEFORE the definition, as in the test case. Thankfully Ruby provides lots of hooks that let you do what you want when certain events are triggered. One of these hooks, "method_added" is run when a method defined in the class. So our new approach: when contract is called, we will store its arguments in a hash. When a method is added to the class, we check in this hash if it has a contract attached to it. If so, we call the old contract function. Here is my final implementation, which executes the test case as we wanted: #final implementation module Contracts def contract(methodname, inputtype) if not @definedcontracts @definedcontracts = {} end @definedcontracts[methodname] = inputtype end def method_added(method) input = @definedcontracts[method] if input @definedcontracts[method] = nil contract_doit(method, input) end end
def contract_doit(methodname, inputtype) renamed = rename(methodname) tester = parse_type(inputtype) class_eval <<-endofeval alias_method #{renamed.inspect}, #{methodname.inspect} def #{methodname}(arg) if #{tester}.call(arg) then return #{renamed}(arg) end raise ContractError, "contract violation: argument of function '#{methodname}' must be a(n) #{inputtype}", caller end endofeval end # ..... the rest is unchanged end
Conclusion: We built a really simple contract checking library in Ruby to explore its cool meta-programming features. Ive been really impressed by Ruby so far-- its great to see these powerful meta-programming features in more mainstream languages.
More Ruby: method_missing
It's been a while since I've shared some cool stuff you can do with Ruby-- here's a trick I've been playing with recently. Ruby defines several hooks-- methods that it calls behind the scenes when something happens. For example, in a previous post on metaprogramming, I used 'method_added' to refer to a method before it was even defined. Another useful hook is method_missing: This one is called whenever someone tries to call a method in your object that doesn't exist. Using this hook, we can "pretend" to accept method calls that aren't defined. This is especially powerful when combined with object composition. Here's an example: class SimpleCallLogger def initialize(o) @obj = o end def method_missing(methodname, *args) puts "called: #{methodname}(#{args})" a = @obj.send(methodname, *args) puts "\t-> returned: #{a}" return a end end
This object "intercepts" all method calls made to it, prints out a message and forwards on the method call to an internal object using the 'send' method-- without knowing anything about the object passed to it. I've used this a few times to debug some code without littering it with print statements. Try that in your static language. Another cool application of this is caching: I'm writing an application with the Openomy APIs that I thought was going a little too slow. I made a class that I could wrap around my openomy-ruby objects and cache the results of some of the calls. Using method_missing, I did this without changing the library or worrying about the cache all over the place. I posted the caching class, as well as a slightly modified call logger, on my new projects page. An improved version of the Contract library from my previous Ruby metaprogamming post is on there as well (which I submitted to the upcoming Ruby Cookbook). If you have questions about things that are on there, shoot me an email.
Hash Auto-vivification
One of the more convenient (but sometimes bug-creating) features in Perl is autovivification: a variable is created just by referring to it (or, comes to life on its own). This is useful for creating composite data structures out of hashes or arrays-- for example, writing '$a{"a"}{"b"} = 3;' creates a hash of hashes called %a for you automatically. Ruby doesn't have this "feature" by default, but like many other things in Ruby, you can work around it. Here's one neat way to autovivify hash-of-hashes, using recursion and Hash default procs (posted by Bill Kelly on ruby-talk): HashFactory = lambda { Hash.new {|h,k| h[k] = HashFactory.call} }
irb(main):181:0> x = HashFactory.call => {} irb(main):182:0> x['abc']['def']['ghi'] = 123 => 123 irb(main):183:0> x => {"abc"=>{"def"=>{"ghi"=>123}}}
Because we all make mistakes
I designed co.mments, so it’s hard to accidentally delete conversations. I did what other Web developers do and added a confirmation popup. It looked good on paper.
Most times, I really do mean to delete stuff, and confirmation popups just get in the way. When I’m scattered, usually early morning before the coffee kicks in, I end up clicking (and confirming) the wrong stuff. If I’m not paying attention to what I delete, I can’t find it again. Especially when I’m deleting something I added a few days back.
Software that forces you to think twice before you do anything is software you can’t trust. And software you can’t trust isn’t fun to use.
So I got rid of the confirmation popup and added an undo button.
How it works
It’s surprisingly easy to do. Now I wonder why I didn’t do it before. I’d like to see that in all my favorite apps, so I’m going to share the recipe and code with you. If you’re building the next best Web app, please add this. Or get your favorite geek to add it to their site.
Each action you do has a URL with all the parameters. I created an undo stack that stores these URLs in the user’s session. Each time you perform an action, the server pushes another undo action URL to the stack. If you just created a new post, the server pushes a ‘delete that post’ URL to the stack.
An undo button at the top of the page uses the most recent undo action URL. Click on the button and it will perform the ‘delete that post’ action. It also removes that action from the stack, so you can undo the previous action. For co.mments, I set the undo stack to five actions, so you can undo more than one change, but it doesn’t take over the database.
Since you can perform an action directly or by undoing a previous button, the undo URL includes the parameter undo=true.
Example using Rails
Here’s the Rails controller with a create action that creates a new record, a delete action that will undo it, and a before_filter that removes undo actions from the stack:
# Remove undo action from stack.
before_filter do |controller|
controller.undo.pop(controller.params) if controller.params[:undo]
end
# The create action creates a new record, the undo action deletes it.
def create()
# Do something useful here
record = Record.create(@params)
# If this is not an undo, add an undo action to delete the record.
unless @params[:undo]
undo.push ("Delete newly created record",
:action=>"delete", :id=>record.id)
end
# Render page (see below).
. . .
end
def delete()
. . .
end
A third method renders the undo form and button, using the last undo action from the stack. The view creates a wrapper element and renders an action:
<div id="undo"><%= undo.render %></div>
For AJAX requests, I use RJS to instruct the browser to update the undo button without reloading:
render :update do |page|
page["undo"].replace_html undo.render
end
Get the code
I wrapped the code as a Rails plugin. To install the plugin into your Rails app:
./script/plugin source http://labnotes.org/svn/public/ruby/rails/
./script/plugin install undo_helper
You can find the source code here. You can also try it out on the co.mments server.
Update: I forgot to mention that GMail has had undo for a while, although only for the last action. Hans posted a screen shot of undo on Technorati.
File Downloads with Ruby on Rails
So you need to find a way to set up downloads for your site. No surprise rails makes this super simple for you. The most important thing to remember is to sanitize the path to the download if your path came from a web page. If you are getting any part of the path from a web form or link, the user can pass any path they want to the controllers method! This is as dangerous as anything you’ll ever do on the web so be very careful unless you want somebody to download your database configuration.
All of the download setup is done in the controller. Rails has a very simple but powerful method for this called send_file. Here’s how it works.
## this is a demo of how to create a download file link
def attachment
send_file('/tmp/images/header.gif',
:filename => 'My Filename!',
:type => 'image/gif',
:disposition => 'attachment',
:streaming => 'true',
:buffer_size => '4096')
end
You’ll notice that the first line is the path to the download. I’m embedding the path into the the controllers method, so I don’t have to worry about sanitizing the path. You MUST sanitize the path if any part of it comes from the end user! The second is where you give your download a name. This doesn’t have to match the actual filename. The browser will use this as a suggested name for the downloaded file. If you don’ |