ericraio
ericraio

Reputation: 1469

Ruby - Creating a class based off the filename?

This is my first time making a custom rails generator and I want to be able to create a dynamic class in the template based off the argument passed to the generator but I can't figure out how and to have it formatted properly.

   class Achievements::__FILE__ < Achievement
   end

This is the generated class I want to create and below is the generator. Also on a side note, am I creating the directory 'achievement' right in my generator?

module Achiever
  module Generators
    class AchievementGenerator < Rails::Generators::Base
      source_root File.expand_path('../templates', __FILE__)
      argument :award, :type => :string

      def generate_achievement
        copy_file "achievement.rb", "app/models/achievement/#{file_name}.rb"
      end

      private

      def file_name
        award.underscore
      end

    end
  end
end

Upvotes: 2

Views: 2037

Answers (2)

ericraio
ericraio

Reputation: 1469

I figured out the problem with this issue. Instead of the copy_file method I should be using the template method. This then allows me to use erb tags inside the templated view and I can call the file_name.classify inside the views and it will dynamically create the model.

argument :award, :type => :string

What ever is set here above as the argument will define the class below in the generated model.

class Achievements::<%= file_name.classify %> < Achievement
end

Upvotes: 0

Jwosty
Jwosty

Reputation: 3634

Use Module#const_set. It is automatically included into Object, so you can do things like:

foo.rb

# Defining the class dynamically. Note that it doesn't currently have a name
klass = Class.new do
  attr_accessor(:args)
  def initialize(*args)
    @stuff = args
  end
end
# Getting the class name
class_name = ARGV[0].capitalize
# Assign it to a constant... dynamically. Ruby will give it a name here.
Object.const_set(class_name, klass)
# Create a new instance of it, print the class's name, and print the arguments passed to it. Note: we could just use klass.new, but this is more fun.
my_klass = const_get(class_name).new(ARGV[1..-1])
puts "Created new instance of `" << my_klass.class << "' with arguments: " << my_klass.args.join(", ")

I haven't tried this code out yet, but it should produce something like:

$ ruby foo.rb RubyRules pancakes are better than waffles
Created new instance of `RubyRules' with arguments: pancakes, are, better, than, waffles

Also, the first argument to const_set absolutely must begin with a capitalized alphanumeric character (just like defining constants statically), or Ruby will produce an error something along the lines of:

NameError: wrong constant name rubyRules
        --- INSERT STACKTRACE HERE ---

Upvotes: 2

Related Questions