Answers.Body: <p>A documentation system similar to Python's, where documentation isn't stored in the source text, but as dynamic data inside of instance variables of the documented objects themselves.</p>
  
  <p>This also allows you to talk about Ruby's object model, e.g. the fact that methods aren't objects and thus you can't store the documentation of a method inside the method, you have to store it somewhere else (e.g. in the module the method is defined in).</p>
  
  <p>The API looks a bit like this:</p>
  
  <pre><code>class Foo
    doc 'This method does something totally interesting.'
    def bar(*) end
  end
  
  Foo.show_doc(:bar)
  #=&gt; 'This method does something totally interesting.'
  </code></pre>
  
  <p>The implementation looks something like this:</p>
  
  <pre><code>module MethodAddedHook
    private
  
    def method_added(meth)
      (@__doc__ ||= {})[meth] = @__last_doc__ if @__last_doc__
      @__last_doc__ = nil
      super
    end
  end
  
  class Module
    prepend MethodAddedHook
  
    def show_doc(meth)
      instance_variable_get(:@__doc__)[meth]
    end
  
    private
  
    def doc(str)
      @__last_doc__ = str
    end
  end
  </code></pre>
  
Answers.ContentLicense: CC BY-SA 3.0
Answers.CreationDate: 2017-06-19T06:33:21.837Z
Answers.LastActivityDate: 2017-06-19T06:33:21.837Z
Answers.UserDisplayName: Jörg W Mittag
Answers.Body: <p>During lecture, my students and I recreate Javascript objects in Ruby. In Javascript, these two lines are synonymous:</p>
  
  <pre><code>config['indent'] = 2;
  config.indent = 2;
  </code></pre>
  
  <p>Ruby doesn't support this duality natively, but metaprogramming makes it possible.</p>
  
  <p>We create a class <code>AutoObject</code> that internally stores a dictionary to hold all of the object's properties. Its <code>method_missing</code> consults this dictionary when something like <code>config.indent</code> is executed. If the method name suggests an assignment, it updates the dictionary. If it's a read of an existing property, it returns the value. Otherwise we raise an exception.</p>
  
  <p>Once the automatic properties are in place, it's not much more work to add serialization and file I/O. By the end, we are reading and writing configuration files that are useful for any application.</p>
  
  <p>ActiveRecord does something similar when it automatically creates an object and its interface based only on the database schema.</p>
  
Answers.ContentLicense: CC BY-SA 3.0
Answers.CreationDate: 2017-06-07T01:12:45.097Z
Answers.LastActivityDate: 2017-06-07T01:12:45.097Z
Answers.UserDisplayName: kaerimasu
Answers.Body: 
  
  <p>The obvious introductory example would be re-implementing <a href="http://ruby-doc.org/core/Module.html#method-i-attr_reader" rel="nofollow noreferrer"><code>Module#attr_reader</code></a> / <a href="http://ruby-doc.org/core/Module.html#method-i-attr_writer" rel="nofollow noreferrer"><code>Module#attr_writer</code></a> / <a href="http://ruby-doc.org/core/Module.html#method-i-attr_accessor" rel="nofollow noreferrer"><code>Module#attr_accessor</code></a>, which after all are typically the first example of metaprogramming a Ruby programmer comes in contact with:</p>
  
  
  
  <pre><code>module MyAttrAccessor
    def attr_reader(*attrs)
      attrs.each do |attr|
        define_method(attr) do p instance_variable_get(:"@#{attr}") end
      end
    end
  
    def attr_writer(*attrs)
      attrs.each do |attr|
        define_method(:"#{attr}=") do |val| p instance_variable_set(:"@#{attr}", val) end
      end
    end
  
    def attr_accessor(*attrs)
      attrs.each do |attr|
        p attr
        attr_reader attr
        attr_writer attr
      end
    end
  
    Module.prepend self
  end
  </code></pre>
  
  <p>Then, you could add validation, for example, which dynamically looks for validator methods named after the attribute, i.e. </p>
  
  
  
  <pre><code>validated_attr_writer :foo
  </code></pre>
  
  <p>would create a method <code>foo=</code> which sets an instance variable <code>@foo</code>, but only if it passes validation, which is defined as a set of methods named <code>foo_must_be_*</code>.</p>
  
  <p>The result could look something like this:</p>
  
  
  
  <pre><code>class Module
    def validated_attr_writer(*attrs)
      attrs.each do |attr|
        define_method(:"#{attr}=") do |val|
          raise ArgumentError unless methods.grep(/^#{attr}_must_be/).all? {|meth| 
            send(meth, val) }
          instance_variable_set(:"@#{attr}", val)
        end
      end
    end
  end
  </code></pre>
  
Answers.ContentLicense: CC BY-SA 3.0
Answers.CreationDate: 2017-06-19T06:21:58.223Z
Answers.LastActivityDate: 2017-06-19T06:21:58.223Z
Answers.UserDisplayName: Jörg W Mittag
Body: <p>When I teach metaprogramming like dynamically defining methods and method delegation, students usually understand what it is fairly quickly because it's simply a dynamic way of doing something they've already done. I now want to assign them a project that utilizes the metaprogamming I've taught. I'm having trouble thinking of a project to assign them which would be made significantly easier by using metaprogramming. The only use I know of is in a DSL. Creating a DSL is a rather large project to assign a group of high school students, so I'd prefer something smaller.</p>
  
  <p>Specifically this project is for fairly advanced students working with ruby, but more general answers are better.</p>
  
  <p>So, what projects could you assign to illustrate the utility of metaprogramming?</p>
  
Comments.ContentLicense: CC BY-SA 3.0
Comments.CreationDate: 2017-06-07T05:50:17.550Z
Comments.Text: Could you not create a self-learning maze navigator? Natural lead in to machine learning and AI.
Comments.UserDisplayName: Gypsy Spellweaver
Tags: <ruby><metaprogramming>
Title: What are good projects for metaprogramming?
ThreadText: # What are good projects for metaprogramming?
  
  (Asked by: username_0 on 2017-06-07)
  
  When I teach metaprogramming like dynamically defining methods and method delegation, students usually understand what it is fairly quickly because it's simply a dynamic way of doing something they've already done. I now want to assign them a project that utilizes the metaprogamming I've taught. I'm having trouble thinking of a project to assign them which would be made significantly easier by using metaprogramming. The only use I know of is in a DSL. Creating a DSL is a rather large project to assign a group of high school students, so I'd prefer something smaller.
  
  Specifically this project is for fairly advanced students working with ruby, but more general answers are better.
  
  So, what projects could you assign to illustrate the utility of metaprogramming?
  
  **Tags:** ruby, metaprogramming
  
  > **Comments:**
  > **username_1**: Could you not create a self-learning maze navigator? Natural lead in to machine learning and AI.  
  
  ---
  
  ## 3 answers
  
  ### Answer by username_3 on 2017-06-07. Score: 4 | Accepted answer
  
  During lecture, my students and I recreate Javascript objects in Ruby. In Javascript, these two lines are synonymous:
  
  ```
  config['indent'] = 2;
  config.indent = 2;
  
  ```
  
  Ruby doesn't support this duality natively, but metaprogramming makes it possible.
  
  We create a class `AutoObject` that internally stores a dictionary to hold all of the object's properties. Its `method_missing` consults this dictionary when something like `config.indent` is executed. If the method name suggests an assignment, it updates the dictionary. If it's a read of an existing property, it returns the value. Otherwise we raise an exception.
  
  Once the automatic properties are in place, it's not much more work to add serialization and file I/O. By the end, we are reading and writing configuration files that are useful for any application.
  
  ActiveRecord does something similar when it automatically creates an object and its interface based only on the database schema.
  
  
  ---
  
  ### Answer by username_2 on 2017-06-19. Score: 2
  
  The obvious introductory example would be re-implementing [`Module#attr_reader`](http://ruby-doc.org/core/Module.html#method-i-attr_reader) / [`Module#attr_writer`](http://ruby-doc.org/core/Module.html#method-i-attr_writer) / [`Module#attr_accessor`](http://ruby-doc.org/core/Module.html#method-i-attr_accessor), which after all are typically the first example of metaprogramming a Ruby programmer comes in contact with:
  
  ```
  module MyAttrAccessor
    def attr_reader(*attrs)
      attrs.each do |attr|
        define_method(attr) do p instance_variable_get(:"@#{attr}") end
      end
    end
  
    def attr_writer(*attrs)
      attrs.each do |attr|
        define_method(:"#{attr}=") do |val| p instance_variable_set(:"@#{attr}", val) end
      end
    end
  
    def attr_accessor(*attrs)
      attrs.each do |attr|
        p attr
        attr_reader attr
        attr_writer attr
      end
    end
  
    Module.prepend self
  end
  
  ```
  
  Then, you could add validation, for example, which dynamically looks for validator methods named after the attribute, i.e.
  
  ```
  validated_attr_writer :foo
  
  ```
  
  would create a method `foo=` which sets an instance variable `@foo`, but only if it passes validation, which is defined as a set of methods named `foo_must_be_*`.
  
  The result could look something like this:
  
  ```
  class Module
    def validated_attr_writer(*attrs)
      attrs.each do |attr|
        define_method(:"#{attr}=") do |val|
          raise ArgumentError unless methods.grep(/^#{attr}_must_be/).all? {|meth| 
            send(meth, val) }
          instance_variable_set(:"@#{attr}", val)
        end
      end
    end
  end
  
  ```
  
  
  ---
  
  ### Answer by username_2 on 2017-06-19. Score: 1
  
  A documentation system similar to Python's, where documentation isn't stored in the source text, but as dynamic data inside of instance variables of the documented objects themselves.
  
  This also allows you to talk about Ruby's object model, e.g. the fact that methods aren't objects and thus you can't store the documentation of a method inside the method, you have to store it somewhere else (e.g. in the module the method is defined in).
  
  The API looks a bit like this:
  
  ```
  class Foo
    doc 'This method does something totally interesting.'
    def bar(*) end
  end
  
  Foo.show_doc(:bar)
  #=> 'This method does something totally interesting.'
  
  ```
  
  The implementation looks something like this:
  
  ```
  module MethodAddedHook
    private
  
    def method_added(meth)
      (@__doc__ ||= {})[meth] = @__last_doc__ if @__last_doc__
      @__last_doc__ = nil
      super
    end
  end
  
  class Module
    prepend MethodAddedHook
  
    def show_doc(meth)
      instance_variable_get(:@__doc__)[meth]
    end
  
    private
  
    def doc(str)
      @__last_doc__ = str
    end
  end
  
  ```
  
  
  ---
  
  