Little Red-Cap revisited

Once upon a time little red-cap opened the stomach of the huntsman.  
The huntsman
thought to himself "what a tender young creature". Soon he gave a  
pair of
scissors to grandmother. Little red-cap went into the wood. And so  
the huntsman
swallowed up the wolf. Grandmother gave a piece of cake to the wolf.  
Mother ran
straight to grandmother's house. And so grandmother was on her guard  
although
the wolf was not afraid of grandmother until mother swallowed up the  
wolf.
Grandmother was not afraid of herself. The wolf gave a pair of  
scissors to the
huntsman. Little red-cap fell asleep. She said: "oh, the huntsman,  
what big ears
you have".
The End.


### stories.rb

require 'enumerator'

class Base
   def self.constructor *args
     attr_accessor(*args)
     define_method :initialize do |*values|
       (0...args.size).each do |i|
         self.instance_variable_set("@#{args[i]}", values[i])
       end
     end
   end
   def to_s
     @name
   end
end

class Character < Base
   constructor :name, :gender
end

class Action < Base
   constructor :name, :objects_or_types
end

class Item < Base
   constructor :name
end

class Place < Base
   constructor :name
end

class PronounBase < Base
   constructor :gender
   class << self
     attr_accessor :cases
   end
   def to_s
     cases = self.class.cases
     @gender == :female ? cases[0] : cases[1]
   end
end

class PossessiveAdjective < PronounBase
   self.cases = ['her', 'his']
end

class Pronoun < PronounBase
   self.cases = ['she', 'he']
end

class ReflexivePronoun < PronounBase
   self.cases = ['herself', 'himself']
end

class Bridge < Base
   constructor :name
end

class Entities
   def initialize klass
     @entities = []
     @klass  = klass
     yield(self)
   end
   def create *args
     @entities << @klass.new(*args)
   end
   def pick
     @entities[rand(@entities.size)]
   end
end

CAST = Entities.new(Character) do |c|
   c.create 'little red-cap', :female
   c.create 'mother',         :female
   c.create 'grandmother',    :female
   c.create 'the wolf',       :male
   c.create 'the huntsman',   :male
end

ACTIONS = Entities.new(Action) do |a|
   a.create 'met', [Character]
   a.create 'gave', [Item, 'to', Character]
   a.create 'took', [Item]
   a.create 'ate', [Item]
   a.create 'saw', [Item]
   a.create 'told', [Character, 'to be careful']
   a.create 'lived in', [Place]
   a.create 'lied in', [Place]
   a.create 'went into', [Place]
   a.create 'ran straight to', [Place]
   a.create 'raised', [PossessiveAdjective, 'eyes']
   a.create 'was on', [PossessiveAdjective, 'guard']
   a.create 'thought to', [ReflexivePronoun, '"what a tender young  
creature"']
   a.create 'swallowed up', [Character]
   a.create 'opened the stomach of', [Character]
   a.create 'looked very strange', []
   a.create 'was delighted', []
   a.create 'fell asleep', []
   a.create 'snored very loud', []
   a.create 'said: "oh,', [Character, ', what big ears you have"']
   a.create 'was not afraid of', [Character]
   a.create 'walked for a short time by the side of', [Character]
   a.create 'got deeper and deeper into', [Place]
end

ITEMS = Entities.new(Item) do |i|
   i.create 'a piece of cake'
   i.create 'a bottle of wine'
   i.create 'pretty flowers'
   i.create 'a pair of scissors'
end

PLACES = Entities.new(Place) do |p|
   p.create 'the wood'
   p.create 'the village'
   p.create 'bed'
   p.create "grandmother's house"
   p.create 'the room'
end

BRIDGES = Entities.new(Bridge) do |b|
   5.times{b.create '.'}
   b.create ', because'
   b.create ', while'
   b.create '. Later'
   b.create '. Then'
   b.create '. The next day'
   b.create '. And so'
   b.create ', but'
   b.create '. Soon'
   b.create ', and'
   b.create ' until'
   b.create ' although'
end

ALL = { Character => CAST, Action => ACTIONS, Place => PLACES, Item  
=> ITEMS }

class Sentence
   attr_accessor :subject
   def initialize
     @subject = CAST.pick
     @verb    = ACTIONS.pick
     @objects = []
     @verb.objects_or_types.each do |obj_or_type|
       if String === obj_or_type
         @objects << obj_or_type
       else
         if obj_or_type == PossessiveAdjective or obj_or_type ==  
ReflexivePronoun
           @objects << obj_or_type.new(@subject.gender)
         else
           thingy = ALL[obj_or_type].pick
           thingy = ReflexivePronoun.new(thingy.gender) if thingy ==  
@subject
           @objects << thingy
         end
       end
     end
   end

   def to_s
     [@subject, @verb, @objects].flatten.map{|e| e.to_s}.join(' ')
   end
end

class Story
   def initialize
     @sentences = []
     1.upto(rand(10)+10) do
       @sentences << Sentence.new
     end
     combine_subjects
   end

   # When the last sentence had the same subject, replace subject  
with 'he' or 'she':
   def combine_subjects
     @sentences.each_cons(2) do |s1, s2|
       if s1.subject == s2.subject
         s2.subject = Pronoun.new(s1.subject.gender)
       end
     end
   end

   # Combine sentences to a story:
   def to_s
     text = 'Once upon a time ' + @sentences[0].to_s
     @sentences[1..-1].each do |sentence|
       bridge = BRIDGES.pick.to_s
       text += bridge + ' ' + (bridge[-1,1] == '.' ?  
sentence.to_s.capitalize : sentence.to_s)
     end
     text.gsub!(/ ,/, ',') # a little clean-up
     text.gsub!(/(.{70,80}) /, "\\1\n")
     text + ".\nThe End.\n"
   end
end

puts Story.new.to_s