How to use Ruby metaprogramming to reorganize this common code?

I have inherited a project with many poorly written Rake tasks that I need to clean up a bit. Since Rakefiles are huge and often prone to bizarre, pointless dependencies, I simplify things a bit and isolate things by reorganizing everything into classes.

In particular, this template is as follows:

namespace :foobar do
  desc "Frozz the foobar."
  task :frozzify do
    unless Rake.application.lookup('_frozzify')
      require 'tasks/foobar'
      Foobar.new.frozzify
    end
    Rake.application['_frozzify'].invoke
  end

  # Above pattern repeats many times.
end

# Several namespaces, each with tasks that follow this pattern.

As tasks/foobar.rbI have something like this:

class Foobar
  def frozzify()
    # The real work happens here.
  end

  # ... Other tasks also in the :foobar namespace.
end

, , . Rakefile require, . , .

, . :

  • :xyz_abc tasks/[namespace].rb tasks/... , XyzAbc.

  • . , :foo_bar :apples, def apples() ... FooBar, tasks/foo_bar.rb.

  • :t "-" _t ( ), .

desc , , . , , , , Rakefile.

, - , , , , . - ?

+3
1

- .

# Declaration of all namespaces with associated tasks.
task_lists = {
  :foobar => {
    :task_one => "Description one",
    :task_two => "Description two",
    :frozzify => "Frozz the foobar",
    :task_three => "Description three" },
  :namespace_two => {
    :task_four => "Description four",
    :etc => "..."} }

# For every namespace with list of tasks...
task_lists.each do |ns, tasks|
  # In the given the namespace...
  namespace ns do
    # For every task in the task list...
    tasks.each do |task_name, description|
      # Set the task description.
      desc description
      # Define the task.
      task task_name do
        unless Rake.application.lookup("_#{task_name}")
          # Require the external file identified by the namespace.
          require "tasks/#{ns}"
          # Convert the namespace to a class name and retrieve its associated
          # constant (:foo_bar will be converted to FooBar).
          klass = Object.const_get(ns.to_s.gsub(/(^|_)(.)/) { $2.upcase })
          # Create a new instance of the class and invoke the method
          # identified by the current task.
          klass.new.send(task_name)
        end
        Rake.application["_#{task_name}"].invoke
      end
    end
  end
end

: .

(, , .)

+4

Source: https://habr.com/ru/post/1742405/


All Articles