How to use classes in Shoes?

757 views Asked by At

I'm a somewhat beginner programmer who has a background in using Processing. I'm currently trying to make an app with Shoes, but I'm confused over how objects and classes work.

I understand that the following would run in Ruby:

class Post
    def self.print_author
      puts "The author of all posts is Jimmy"
    end
end

Post.print_author

But why won't the following run in Shoes? How would I make it run?

class Post
    def self.print_author
      para "The author of all posts is Jimmy"
    end
end

Shoes.app do
    Post.print_author
end
2

There are 2 answers

1
Chris Heald On BEST ANSWER

I'm not too familiar with Shoes, but the problem you're likely having here is that you're trying to call a method called para on the Post class, and no such method exists.

When you call Shoes.app do ..., I suspect Shoes is changing the current execution context to one that includes those methods. That is, you should expect this to work:

Shoes.app do
  para "The author of all posts is Jimmy"
end

This is equivalent to:

Shoes.app do
  self.para("The author of all posts is Jimmy")
end

When you call Post.print_author, self is no longer the Shoes object, but rather, is the Post class. You have a few options at that point:

  1. Pass in the Shoes instance, and call your Shoes-specific methods on that. You should probably do it this way when you don't need any state from Post:

    class Post
      def self.print_author(shoes)
        shoes.para "The author of all posts is Jimmy"
      end
    end
    
    Shoes.app do
      Post.print_author(self)
    end
    
  2. Create a Post class which accepts a Shoes object, so you don't have to keep passing it around. You should do it this way if Post is going to have any substantial amount of state:

    class Post
      def initialize(shoes)
        @shoes = shoes
      end
    
      def print_author
        @shoes.para "The author of all posts is Jimmy"
      end
    end
    
    Shoes.app do
      post = Post.new(self)
      post.print_author
    end
    
  3. You could use a variant on the 2. option to automatically pass calls to the @shoes object. This starts to get into Ruby metaprogramming, which I'd recommend you avoid until you're more comfortable in Ruby, but I'm leaving it here to pique your interest:

    class Post
      def initialize(shoes)
        @shoes = shoes
      end
    
      def print_author
        para "The author of all posts is Jimmy"
      end
    
      def method_missing(method, *args, &block)
        @shoes.send(method, *args, &block)
      end
    end
    
    Shoes.app do
      post = Post.new(self)
      post.print_author
    end
    

What this does is tell Ruby "if a method isn't found on the Post instance, try sending it to the @shoes instance instead". As you can imagine, this can allow for some very nice DSLs, but you have to use it carefully, as it can make code difficult to follow if you abuse it.

0
Eric Watson On

A much simpler way to do this is to have Post provide the content, and then, in your Shoes app, render that content however you want it. Side benefit: you can re-use your Post class in another class that prints to the console.

class Post
  def self.print_author
    "The author of all posts is Jimmy"
  end
end

Shoes.app do
  para Post.print_author
end

class ConsoleApp
  def run
    puts Post.print_author
  end
end