0

I have this source file structure:

-- root.rb
|
|- root -- |- a.rb
           |- b.rb
           |- c.rb

In root.rb, there is a class named Root, a.rb contains a class named A, and so on.

I want the Root class to iterate the root fold, and dynamically create instance of class A, B, C.

class Root 
  def initialize
    objs = []
    dir = Dir.new('root')
    dir.each { |file|
        # Here file should be a.rb, b.rb, c.rb, etc.
        # I want to dynamically require "a.rb, and create and an object of A.
        # I know the following two lines must be wrong,
        # Could anybody give me the correct code?
        require file    
        objes << ClassFromFilename(file).new 
    }
  end
end

I'm looking for correct code to do "require" and creating object from file name. Please see code comment for details.

4
  • 1
    What is the problem you are having? Commented Nov 19, 2013 at 14:32
  • Question is in code comment. Commented Nov 19, 2013 at 14:33
  • 1
    There are probably better ways to do what you're trying to do, this approach seems wrong. Couldn't the class definitions have something that register the class to some module for example like class Foo CollectionModule.register(self) end Commented Nov 19, 2013 at 14:48
  • Question should not be in code comment. Write that in the main text. Commented Nov 19, 2013 at 14:54

3 Answers 3

4

The following code should do the trick:

class Root
    attr_reader :objs
    def initialize
        @objs = []
        Dir.glob('root/*.rb').each do |file|
            require file
            @objs << eval(File.basename(file, ".rb").capitalize + ".new")
        end
    end
end

To test it, do the following in irb:

require 'root.rb'
x = Root.new
x.objs.inspect

Note: depending on your file names, Ruby's String#capitalize method may be insufficient. In that case, you need to write your own string manipulation method to convert from file basename (in snake-case) eg "file_header" to class name (in camel-case) "FileHeader".

If you're using Rails, try the following instead, it handles string snake-case to camel-case conversion for you.

@objs << File.basename(file, ".rb").classify.constantize.new

Please note that initializer may not the best place to require files in this manner. But Ruby allows you to do it, and be careful with the power Ruby gives you. ;)

Sign up to request clarification or add additional context in comments.

1 Comment

constantize comes with rails wich I doubt OP is using.
3

For a more modular approach, maybe something like:

# root.rb 
class Root
  class SubClass
    def self.inherited(where)
      Root.register(where)
    end
  end

  def self.descendants
    @descendants ||= []
  end

  def self.register(klass)
    descendants << klass
  end

  def instantiate_descendants
    self.class.descendants.collect(&:new)
  end
end

Dir[File.expand_path('../root/*.rb', __FILE__)].each {|path| require path}

and

class Foo < Root::SubClass
  def foo
    puts "hello!"
  end
end

And then you can do something like :

$ irb -r ./root.rb
irb(main):001:0> r = Root.new
=> #<Root:0x7f6261951558>
irb(main):002:0> things = r.instantiate_descendants
=> [#<Foo:0x2317893asdf>]

Comments

3

This should do the trick

# we need some way to camel case strings
class String
  def camel_case
    return self if self !~ /_/ && self =~ /[A-Z]+.*/
    split('_').map{|e| e.capitalize}.join
  end    
end

def klass_from_filename(filename)
  Object.const_get(filename.split('.').first.camel_case)
end

require file
objects << klass_from_filename(file).new 

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.