1

I have a directory /lib where I store *.rb files. Each one of them contains a class with a single class method remove_user().

How can I make the main script automatically go over those files and call the same method on all of them? I want to just drop-in files in that directory in the future without modifying the main script in any way.

I do know how to require all the files from a directory based on "Best way to require all files from a directory in ruby?", but I'm not very sure how to invoke the classes "in a loop" from here.

Update

I've tried a the code suggested in "How do I create automatically a instance of every class in a directory?"

files = Dir.glob("lib/*.rb")

def load_modules(class_files)
  puts class_files
  before = ObjectSpace.each_object(Class).to_a
  class_files.each {|file| require_relative file }
  after = ObjectSpace.each_object(Class).to_a
  (after - before).each {|klass| klass.new.delete_user('myemail@mail.com', 'Audit', 'Test')}

  load_modules(files)
end

It produces an error:

/Users/apinchuk/RubymineProjects/autoaudit/init.rb:16:in `new': can't create instance of singleton class (TypeError)
from /Users/RubymineProjects/autoaudit/init.rb:16:in `block in load_modules'
from /Users/RubymineProjects/autoaudit/init.rb:16:in `each'
from /Users/RubymineProjects/autoaudit/init.rb:16:in `load_modules'
from /Users/RubymineProjects/autoaudit/init.rb:20:in `<top (required)>'
from -e:1:in `load'
from -e:1:in `<main>'

And there is nothing I could find about this error.

The create_uat_robot.rb has a structure like this:

class CreateUatRobot
  def self.delete_user(email, first_name, last_name)
    ...
  end
end

The name of the file is create_uat_robot.rb

Trying @moveson suggestion as follows:

files = Dir.glob("lib/*.rb")
files.each {|file| require_relative file }

klasses = Dir["lib/*.rb"].map {|file| File.basename(file, '.rb').camelize.constantize}
klasses.each { |klass| klass.delete_user(arguments) }

worked for me.

Community
  • 1
  • 1
user3081519
  • 2,659
  • 5
  • 25
  • 35
  • Please read "[ask]" including the linked pages, "[mcve]" and "[How much research effort is expected of Stack Overflow users?](http://meta.stackoverflow.com/questions/261592)". We'd like to see evidence of your effort. What did you try? Did you search and not find anything? Did you find stuff but it didn't help? Did you try writing code? If not, why? If so, what is the smallest code example that shows what you tried and why didn't it work? Without that it looks like you didn't try and want us to write it for you. – the Tin Man Mar 23 '17 at 23:51

1 Answers1

1

First you need to create an array of class names:

>> klasses = Dir["lib/*.rb"].map {|file| File.basename(file, '.rb').camelize.constantize }

Then you can call your method on each of them in turn:

>> klasses.each { |klass| klass.remove_user }

If you are not using Rails, you can require ActiveSupport's String extension methods (require 'active_support/core_ext/string'), which will give you the camelize and constantize methods.

moveson
  • 5,103
  • 1
  • 15
  • 32
  • `file.split('/').last.split('.').first` seems like a really complicated way to do `File.basename(file, ".rb")`. – Jordan Running Mar 23 '17 at 21:34
  • `File.basename` won't work here because `Dir["lib/*.rb"]` returns an array of strings, not File objects. If you know how to return an array of File objects, I will edit the answer to reflect that. – moveson Mar 23 '17 at 21:42
  • [`File.basename`](http://ruby-doc.org/core-2.4.0/File.html#method-c-basename) is a class method, not an instance method, and it takes a string as its first argument: `File.basename("/foo/bar/baz/my_module.rb", ".rb") # => "my_module"`. Take a look: https://repl.it/Gb4V – Jordan Running Mar 23 '17 at 21:44
  • Ah. Yes, now I understand. I've updated the answer. Thanks. – moveson Mar 23 '17 at 21:49