Search code examples
rubyclassobjectshoes

How to use classes in Shoes?


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

Solution

  • 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.