To install it, just:
gem install hebruby
Thank you to everyone who has helped with Hebruby, especially Joshua Harvey, and enjoy!
To the day that the programmers start running the asylum. Carpe Codex!
gem install hebruby
person "Dorothy" do
temperament :nice
food :sunflower_seeds, :carrot_juice
end
shop = PetShop.create do
pet "Toto" do
friend_test do |person|
true unless person.temperament == :mean
end
end
pet "Tweety" do
friend_test do |person|
person.has_food?(:sunflower_seeds)
end
end
pet "Slugworth" do
friend_test do |person|
true # I like anyone
end
end
person "Dorothy" do
temperament :nice
food :sunflower_seeds, :carrot_juice
end
person "Witch" do
temperament :mean
food :cheetos, :soda
end
end
shop = PetShop.create do
pet "Toto" do
friend_test do |person|
true unless person.temperament == :mean
end
end
...
end
def self.pet(name, &blk)
@pets = Hash.new
p = Pet.new(name)
p.class.class_eval(&blk) if block_given?
@pets[name] = p
p.copyvars
end
dog = Toto.new
bird = Tweety.new
snail = Slugworth.new
person = Dorothy.new
puts "#{dog.class.name} is a friend of #{person.class.name}: #{dog.is_friend?(person)}"
puts "#{bird.class.name} is a friend of #{person.class.name}: #{bird.is_friend?(person)}"
puts "#{snail.class.name} is a friend of #{person.class.name}: #{snail.is_friend?(person)}"
puts
person = Witch.new
puts "#{dog.class.name} is a friend of #{person.class.name}: #{dog.is_friend?(person)}"
puts "#{bird.class.name} is a friend of #{person.class.name}: #{bird.is_friend?(person)}"
puts
shop.people.each_value do person
shop.pets.each_value do pet
puts "Is #{pet.name} a friend of #{person.name}? #{pet.is_friend?(person)}"
end
end
Is Toto a friend of Witch? false
Is Slugworth a friend of Witch? true
Is Tweety a friend of Witch? false
Is Toto a friend of Dorothy? true
Is Slugworth a friend of Dorothy? true
Is Tweety a friend of Dorothy? true
class DSLThing
def copyvars
self.class.instance_variables.each do |var|
instance_variable_set(var, self.class.instance_variable_get(var))
end
end
end
class PetShop < DSLThing
attr_accessor :pets, :people
def self.create(&block)
f = PetShop.new
f.class.class_eval(&block) if block_given?
f.copyvars
return f
end
def self.pet(name, &blk)
@pets ||= Hash.new
p = Pet.new(name)
p.class.class_eval(&blk) if block_given?
@pets[name] = p
p.copyvars
end
def self.person(name, &blk)
@people ||= Hash.new
p = Person.new(name)
p.class.class_eval(&blk)
@people[name] = p
p.copyvars
end
end
class Animal < DSLThing
attr_accessor :name
def initialize(name=nil)
@name = name
end
end
class Person < Animal
attr_accessor :temperament
def initialize(name=nil)
super
end
def self.temperament(type)
@temperament = type
end
def self.food(*types_of_food)
@food = []
types_of_food.each do |food|
@food << food
end
end
def has_food?(type_of_food)
@food.include?(type_of_food)
end
end
class Pet < Animal
def initialize(name=nil)
super
end
def self.friend_test(&test)
@friend_test = test
end
def is_friend?(person)
@friend_test.call(person) == true
end
end
shop = PetShop.create do
pet "Toto" do
friend_test do |person|
true unless person.temperament == :mean
end
end
pet "Tweety" do
friend_test do |person|
person.has_food?(:sunflower_seeds)
end
end
pet "Slugworth" do
friend_test do |person|
true # I like anyone
end
end
person "Dorothy" do
temperament :nice
food :sunflower_seeds, :carrot_juice
end
person "Witch" do
temperament :mean
food :cheetos, :soda
end
end
shop.people.each_value do |person|
shop.pets.each_value do |pet|
puts "Is #{pet.name} a friend of #{person.name}? #{pet.is_friend?(person)}"
end
end
class Animal
attr_accessor :number_of_legs
def self.number_of_legs(number_of_legs)
@number_of_legs = number_of_legs
end
def initialize
self.class.instance_variables.each do |var|
instance_variable_set(var, self.class.instance_variable_get(var))
end
end
end
class Dorothy < Person
temperament :nice
food :sunflower_seeds, :carrot_juice
end
class Witch < Person
temperament :mean
food :cheetos, :soda
end
class Person < Animal
attr_accessor :temperament
def self.temperament(type)
@temperament = type
end
def self.food(*types_of_food)
@food ||= []
types_of_food.each do |food|
@food << food
end
end
def has_food?(type_of_food)
@food.include?(type_of_food)
end
end
class Toto < Pet
friend_test do |person|
true unless person.temperament == :mean # I like anyone who is not mean
end
end
class Tweety < Pet
friend_test do |person|
true if person.has_food?(:sunflower_seeds) # I like anyone who has sunflower seeds
end
end
class Slugworth < Pet
friend_test do |person|
true # I like anyone
end
end
class Pet < Animal
def self.friend_test(&test)
@friend_test = test
end
def is_friend?(person)
@friend_test.call(person) == true
end
end
dog = Toto.new
bird = Tweety.new
snail = Slugworth.new
person = Dorothy.new
puts "#{dog.class.name} is a friend of #{person.class.name}: #{dog.is_friend?(person)}"
puts "#{bird.class.name} is a friend of #{person.class.name}: #{bird.is_friend?(person)}"
puts "#{snail.class.name} is a friend of #{person.class.name}: #{snail.is_friend?(person)}"
puts
person = Witch.new
puts "#{dog.class.name} is a friend of #{person.class.name}: #{dog.is_friend?(person)}"
puts "#{bird.class.name} is a friend of #{person.class.name}: #{bird.is_friend?(person)}"
puts "#{snail.class.name} is a friend of #{person.class.name}: #{snail.is_friend?(person)}"
Toto is a friend of Dorothy: true
Tweety is a friend of Dorothy: true
Slugworth is a friend of Dorothy: true
Toto is a friend of Witch: false
Tweety is a friend of Witch: false
Slugworth is a friend of Witch: true
class Animal
attr_accessor :number_of_legs
def self.number_of_legs(number_of_legs)
@number_of_legs = number_of_legs
end
def initialize
self.class.instance_variables.each do |var|
instance_variable_set(var, self.class.instance_variable_get(var))
end
end
end
class Person < Animal
attr_accessor :temperament
def self.temperament(type)
@temperament = type
end
def self.food(*types_of_food)
@food ||= []
types_of_food.each do |food|
@food << food
end
end
def has_food?(type_of_food)
@food.include?(type_of_food)
end
end
class Pet < Animal
def self.friend_test(&test)
@friend_test = test
end
def is_friend?(person)
@friend_test.call(person) == true
end
end
class Toto < Pet
friend_test do |person|
true unless person.temperament == :mean
end
end
class Tweety < Pet
friend_test do |person|
person.has_food?(:sunflower_seeds)
end
end
class Slugworth < Pet
friend_test do |person|
true
end
end
class Dorothy < Person
temperament :nice
food :sunflower_seeds, :carrot_juice
end
class Witch < Person
temperament :mean
food :cheetos, :soda
end
dog = Toto.new
bird = Tweety.new
snail = Slugworth.new
person = Dorothy.new
puts "#{dog.class.name} is a friend of #{person.class.name}: #{dog.is_friend?(person)}"
puts "#{bird.class.name} is a friend of #{person.class.name}: #{bird.is_friend?(person)}"
puts "#{snail.class.name} is a friend of #{person.class.name}: #{snail.is_friend?(person)}"
puts
person = Witch.new
puts "#{dog.class.name} is a friend of #{person.class.name}: #{dog.is_friend?(person)}"
puts "#{bird.class.name} is a friend of #{person.class.name}: #{bird.is_friend?(person)}"
puts "#{snail.class.name} is a friend of #{person.class.name}: #{snail.is_friend?(person)}"
I have been working on a prototype of a Ruby domain specific language for one of my clients, a very large financial services company. I have learned a whole bunch of really interesting lessons, which I will share over a short series of posts as I make more progress.
The first thing I tried to do was create a basic bit of DSL tastiness like this:
class Dog < Animal
number_of_legs 4
end
class Bird < Animal
number_of_legs 2
end
class Snail < Animal
number_of_legs 0
end
My first implementation of the Animal class looked like this:
class Animal
attr_accessor :number_of_legs
def self.number_of_legs(number_of_legs)
@number_of_legs = number_of_legs
end
end
Just looking at this bit of code, it seems like it should work. However, when trying this, it doesn't work as expected.
pet = Dog.new
p pet.number_of_legs # prints nil
In Ruby EVERYTHING is an object. This includes the class objects themselves, not just the instances of class objects! This means that when the number_of_legs method is called, it is actually being called before the actual instance object itself has been created. We need to get to our instance variable, and one way is to copy the class-level instance variable to the instance itself when the actual instance is being initialized like this:
class Animal
attr_accessor :number_of_legs
def self.number_of_legs(number_of_legs)
@number_of_legs = number_of_legs
end
def initialize
instance_variable_set("@number_of_legs", self.class.instance_variable_get("@number_of_legs"))
end
end
pet = Dog.new
p pet.number_of_legs # prints 4
As you add new things to your DSL, having to copy each variable manually is boring. The final change is to copy every class instance variable automatically at initialization, as follows:
class Animal
attr_accessor :number_of_legs
def self.number_of_legs(number_of_legs)
@number_of_legs = number_of_legs
end
def initialize
self.class.instance_variables.each do var
instance_variable_set(var, self.class.instance_variable_get(var))
end
end
end
I just installed the brand new Gmail for Mobile on my Motorola RAZR. In just a matter of a couple moments, I was looking at my Gmail inbox! Gmail for Mobile is a Java-applet that runs on your Java enabled phone. Keeping the network traffic down this way is a lot better option that a WAP-based email solution, which is what I had tried and abandoned previously.
Not having a QWERTY keyboard is a big limitation for smartphones especially where something heavily textual like email is concerned. Gmail for Mobile deals with this thru a clever mapping of hotkeys to the most commonly used functions (delete, in my case).
Between Google Reader, and now this Gmail for Mobile, another bit of my online existence has been sucked into the Googlesphere...and I like it!