Posted by Matt Williams
While playing around in irb last night, I discovered something interesting Class does not always have the same superclass:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
>> Class.superclass
=> Module
>> Class.object_id
=> -605542778
>> class Fud
>> end
=> nil
>> Fud.class
=> Class
>> Fud.object_id
=> -606747838
>> Fud.superclass
=> Object
>> Class.class
=> Class
>> Class.class.superclass
=> Module
>> Fud.class.superclass
=> Module
>> Fud.class.object_id
=> -605542778
>> Fud.class
=> Class
>> Fud.class
=> Class
>> Fud.class.object_id
=> -605542778
>> Fud.superclass
=> Object
>> Fud.instance_of? Class
=> true
>> Class.instance_of? Class
=> true
>> Class.class.object_id
=> -605542778
>> Class.object_id
=> -605542778
|
Curious, huh? So, despite Fud being an instance of Class, it's superclass is Object. And Class is an instance of itself.
I believe that this has to do with the mysterious "class" object which every class has. This class object allows one to get at the guts, if you will, of a class in order to do metaprogramming.
Still it's pretty interesting...
Posted by Matt Williams
I had an instance where I'd written some code, using hashes, and knew that there had to be a better way to do it. Performing an inject and adding elements to a hash doesn't work:
h[a] = 1
returns the value, not the hash. Here's the smelly code (variables, etc., changed to protect the guilty):
# possibilities is an array
def intersectors(possibilities = [], interceptors = [])
return { } if possibilities.nil?
interceptors.inject({ }) do |h, interceptor|
# returns an array
z = foo(interceptor)
intersect = possibilities & z
h[interceptor] = intersect unless intersect.empty?
h
end
end
So, then I decided to RTFM and found merge. It makes it a lot tighter:
def intersectors(path = [], interceptors = [])
return { } if path.nil?
interceptors.inject({ }) do |h, interceptor|
h.merge(interceptor => x) unless (x=(path & foo(interceptor))).empty?
end
end
Better, too, I think; I don't have that extraneous h laying around..... I'm also using some syntactic sugar -- ruby is smart enough to make a hash of key/value pairs in a method call, so long as they're the last entities to be passed.
I'm not saying it's baby-fresh, but it certainly stinks less.....
Posted by Matt Williams
While writing some code last night, I realized that I'd left out something from my attributes -- the ability to do booleans such as obj.nil? and have the boolean method created for me. So, the new, improved attributes code -- now with boolean action follows:
class Object
def self.attribute(*arg,&block)
(name, default) = arg
short_name = name.to_s.sub(/\?/,"")
self.send(:define_method, name) {
if instance_variables.include? "@#{short_name}"
self.instance_eval "@#{short_name}"
else
if block_given?
instance_eval &block
else
default
end
end
}
self.send(:define_method, "#{short_name}="){ |value|
self.instance_eval "@#{short_name} = value"
}
end
end
I wonder what other behaviours I can add...... just kidding.
Posted by Matt Williams
Pecha Kucha is a presentation style consisting of 20 slides, with 20 seconds/slide. I got tapped last Tuesday for giving a presentation on Thursday. It was a bit of a rush job, and I was a bit harried, but all in all, a good experience.
Thoughts
- It's not an easy style. The slides go one to the other every 20 seconds, whether you're ready or not.
- I've found that I need to be much better prepared -- given only 20 seconds, I have to plan exactly and/or script out what I'm going to say on each slide, otherwise I'll certainly go over. I scripted it. Then I cut it. Then I cut it again. I could still cut it more.
- Limit the scope. In six minutes, there's a limit to what an audience can digest.
- Limit the text on the slides. This is good advice in general, but even more important in this style.
- I think it's a very, very good exercise. I found that having the constraint was very useful; it definitely kept me focused and on topic.
I'll definitely use this style again. The presentation can be found at: Into the Aether: Web 2.0 -- People, Conversations, and Community.
Posted by Matt Williams
While at Columbus Ruby Brigade's meeting last night there was a question of what's magick, what's a dsl, and when are you using too much magick.
I came up with the following:
DSL's make code easier to read. Magick makes it easier to write code. In specific, Magick makes it easier for good developers to write better code; you still need to know what's going on.
What do you think?
Posted by Matt Williams
I'm not talking about bad breath. Rather, I'm talking about scope in ruby.
Posted by Matt Williams
EDITED to fix bug: See Scope Kills for details.
While ruby has attr_accessor there's no good way to specify a default value which comes as part of the stock language. Thank goodness for metaprogramming. Based off of Create getter and setter on a valorized variable, I have a solution which takes either a value or a block as the default. It's based, in part, upon my solution for Ruby Quiz #67 Metakoans.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
class Object
def self.attribute(*arg,&block)
(name,default) = arg
self.send(:define_method, name) {
if instance_variables.include? "@#{name}"
self.instance_eval "@#{name}"
else
if block_given?
instance_eval &block
else
default
end
end
}
self.send(:define_method, "#{name}="){ |value|
self.instance_eval "@#{name} = value"
}
end
end
|
Given the following class:
1
2
3
4
5
6
|
class Foo
attribute :bar
attribute(:fud) {instance_variables.include?("@bar") ? @bar : ' '}
attribute :fi, 10
end
|
we can test as follows:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
>> t=Foo.new
=> #<Foo:0xb7b9cadc>
>> t.bar
=> nil
>> t.fud
=> " "
>> t.fi
=> 10
>> t.bar = 5
=> 5
>> t.fud
=> 5
>> t.fud = 20
=> 20
>> t.fud
=> 20
>>
|
I imagine it can be simplified some more, but it works, and I think it's kinda nifty.....
Posted by Matt Williams
Treetop is, as they say on their website, ... a language for describing languages. Combining the elegance of Ruby with cutting-edge parsing expression grammars, it helps you analyze syntax with revolutionarily ease.
In working on my game library, I've been experimenting with Treetop for parsing user input. At some point I am wanting to say: create Militia of size 100. In order to do this, I'd use a syntax like this (contrived):
1
2
3
4
5
6
7
8
9
10
11
12
|
grammar CreateUnit
rule create_unit
"create " unit_type " of size " number
end
rule number
([1-9] [0-9]* / '0')
end
rule unit_type
[A-Z][a-z]
end
end
|
Pretty simple rule, eh? There's one problem: my rule for unit_type will allow us to use unit types which don't actually exist. And since the unit types are defined at runtime by a yaml file (for information on how we're doing this see Classes on the Fly), we can't rightly say what we're going to use for our unit types ahead of time. Nor is Treetop set up very well for defining parsers on the fly -- it can read from files at runtime, but it can't without having to go to the filesystem. Hence this addition:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
module Treetop
module Compiler
class GrammarCompiler
def ruby_string(s)
parser = MetagrammarParser.new
result = parser.parse(s)
unless result
raise RuntimeError.new(parser.failure_reason)
end
result.compile
end
end
end
def self.compile_string(s)
compiler = Treetop::Compiler::GrammarCompiler.new
Object.class_eval(compiler.ruby_string(s))
end
end
|
To use it, we'd do something like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
$ irb
>> require 'rubygems'
=> false
>> require 'war'
=> true
>> require 'treetop'
=> false
>> War::Utils::Loader.load_classes("../config/units.yml",War::Unit)
militia
rifleman
scout
landship
walker
=> [{"name"=>"militia", "template"=>"soldier", "attributes"=>{"strength"=>50, "symbol"=>"m", "cost"=>50, "defence"=>10, "attack"=>-10}}, {"name"=>"rifleman", "template"=>"soldier", "attributes"=>{"strength"=>100, "symbol"=>"r", "cost"=>125, "defence"=>30, "attack"=>30}}, {"name"=>"scout", "template"=>"scout", "attributes"=>{"strength"=>20, "symbol"=>"s", "cost"=>200, "defence"=>10, "attack"=>10}}, {"name"=>"landship", "template"=>"tank", "attributes"=>{"strength"=>1500, "symbol"=>"L", "cost"=>2000, "defence"=>200, "attack"=>200}}, {"name"=>"walker", "template"=>"tank", "attributes"=>{"strength"=>500, "cost"=>1250, "symbol"=>"w", "movement"=>2, "defence"=>100, "vision"=>2, "attack"=>100}}]
>> s = <<EOF
grammar UnitType
rule unit_type
#{War::Unit.subclasses.map{|c|"'#{c.to_s}'"}.join(" / ")}
end
end
EOF
=> "grammar UnitType\nrule unit_type\n'Walker' / 'Landship' / 'Scout' / 'Rifleman' / 'Militia'\nend\nend\n"
>> require 'treetop_extensions'
=> true
>> Treetop.compile_string(s)
=> UnitTypeParser
>> parser = UnitTypeParser.new
=> #<UnitTypeParser:0xb76ee2c4 @consume_all_input=true>
>> parser.parse("Foo")
=> nil
>> parser.parse("Militia")
=> SyntaxNode offset=0, "Militia"
>>
|
So, we can now parse unit types -- let's change our grammar now to reflect this:
1
2
3
4
5
6
7
8
9
10
|
grammar CreateUnit
include UnitType
rule create_unit
"create " unit_type " of size " number
end
rule number
([1-9] [0-9]* / '0')
end
end
|
Let's see it in practice (assume everything's been loaded...):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
>> p = CreateUnitParser.new
=> #<CreateUnitParser:0xb76c845c @consume_all_input=true>
>> p.parse("create Militia of size 100")
=> SyntaxNode+CreateUnit0 offset=0, "... Militia of size 100" (number,unit_type):
SyntaxNode offset=0, "create "
SyntaxNode offset=7, "Militia"
SyntaxNode offset=14, " of size "
SyntaxNode+Number0 offset=23, "100":
SyntaxNode offset=23, "1"
SyntaxNode offset=24, "00":
SyntaxNode offset=24, "0"
SyntaxNode offset=25, "0"
>> p.parse("create Foo of size -1")
=> nil
>>
|
So, there you have it, we've created a parser grammar on the fly, which we can use....
Posted by Matt Williams
The hardest thing to do while programming is not the programming itself. It is the "Not Programming".
That is to say, the hardest part is not adding feature #65353, which you're probably never going to use, but it makes the code ever so much more flexible. It is the question of "Why am I doing this? Do I really need it now (or ever)?"
Lately I've very much been on a simplicity kick; looking around at a lot of projects around me and how they're buzzword compliantover architected, I can't help but to think of all the ways in which people are prone to feeping creaturitis and Big Mac Programming.
I think that there are several things which can help -- in fact, I've coined a phrase "Just Enough Programming" -- more about that will follow, but here are a few things to help with "Not Programming":
- BDD and/or TDD, although I think that BDD is better about understanding why we are doing something.
- DRY code
- Lighter frameworks -- ones which do not require as many lines of configuration as they do of code. Infinite scalability is for imps and monkeys with typewriters.
- Declarative code -- it can be easier to limit ourselves this way (by the same token, do not fall into the trap of Rococo Programming)
A student once approached the master and asked, "Does a Foo have the Bar nature?", to which the master replied "Groo" and returned to his meditations.
- Shorter integration cycles
Occam's Razor: "Shaves as close as a blade or your money back".
Posted by Matt Williams
I recently had a case (a game) where I had the potential for many classes which were similar, but have enough differences it made sense to dry them up, so I developed a method to use yaml to define a class.
Posted by Matt Williams
The ruby2ruby gem allows you to peer into a class's methods and watch changes due to metaprogramming.
Posted by Matt Williams
Remember #defined FOO = 1; from your C days? Or public static int FOO = 1; from java? Well, I recently had reason to use this functionality within Ruby when I wanted to define a series of types which would each have a unique id used in a database, but I really didn't want to waste the space on a string.
I ended up with two methods, the first which takes a name and an enumerable, sets class variables for each element of an array (such as %w(The fat cat sat on a hat)) as well as a method for resolving an integer to a string.
Taking the following class:
1
2
3
4
5
6
|
require 'definer'
class Project
define_class_types :states, %w(planning development testing production)
define :myself, 1
end
|
you would have the following definitions:
Project.planning
Project.development
Project.testing
Project.prodution
Project.@@states(not accessible outside of class, but that's easily remedied)
Project.myself
Additionally, you would have a method Project.state_name(state) which would take a integer and return the string associated with that state. So, Project.state_name(0) or Project.state_name(Project.planning) would both return "planning".
In the define_class_types method, the name can be either pluralized or singular. The array of the types will be pluralized (as in states above) and the method for getting the string singularized (as in Project.state_name above.
There is one dependency -- it uses ActiveSupport's pluralize and singularize. The code follows below or you can download it here: definer.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
require 'active_support'
class Module
def class_attr_reader(*symbols)
symbols.each do |symbol|
self.class.send(:define_method, symbol) do
self.class_eval "@@#{symbol}"
end
end
end
def define(name,value)
name = name.to_s
class_variable_set("@@#{name}",value)
class_attr_reader(name)
end
def add_class_types(name, types = [])
name = name.to_s
class_variable_set("@@#{name.pluralize}",types)
types.each_with_index do |type, index|
class_variable_set("@@#{type}", index)
class_attr_reader(type)
end
self.class.send(:define_method,"#{name.singularize}_name") do |arg|
self.class_eval "@@#{name.pluralize}[#{arg}]"
end
end
end
|
Posted by Matt Williams
Yes, yesterday was April Fool's day. But Forth and other stack-based languages (like postscript) are fun....
Posted by Matt Williams
Recently I re-read Rick Cook's Wizard's Bane and The Wizardry Compiled. I strongly recommend that series to anyone who wishes to be a serious developer.
And I realized that I'd been missing out on a lot of fun lately. Ruby's become so dry and boring, it isn't funny. I really miss the days of having a function named corned_beef which uses such wondrous numbers as 65353. And stack based programming really is da-bomb! I remember all of the fun I used to have with it and have realized it's time to go back to it.
Posted by Matt Williams
I missed most of yesterday due to sickness. But... It's still been a good conference thus far. The location is great, and I really like the feel of it. I'm about to present in about 15 min. Should be interesting.