Can't call ruby module method - ruby

cat module1.rb =>
module Module1
def add(a,b)
return a+b
end
def subtract(a,b)
return a-b
end
end
cat call.rb =>
#!/home/user1/.rvm/rubies/ruby-1.9.2-p180/bin/ruby
include './Module1.rb
temp = add(5,2)
print temp
print "\n"
ruby call.rb =>
<internal:lib/rubygems/custom_require>:29:in `require': no such file to load -- Module1 (LoadError)
from <internal:lib/rubygems/custom_require>:29:in `require'
from call.rb:3:in `<main>'
Can anyone fix it ?

Place two files in the same directory. Call the first one module1.rb and make it look exactly like this:
module Module1
def add(a, b)
return a + b
end
def subtract(a, b)
return a - b
end
end
Call the second one call.rb and make it look exactly like this
require './module1.rb'
include Module1
temp = add(5,2)
print temp
print "\n"
At the commandline, run ruby call.rb. You should see an output of 7.

I assume you're using Ruby 1.9?
Then try
require_relative 'module1'
include Module1
temp = add(5,2)
puts temp
That should do it.

require loads a file from Ruby's $LOAD_PATH. If you want to load a file relative to the current file, then you need to use require_relative instead.

You should require the file before including.
require 'module1.rb'
include Module1
And make sure the two file are in same directory.

Related

How to automatically call classes in Ruby script from a specific directory

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.
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.

Opening relative paths from gem

I'm writing a simple gem that can load from and save data to text files and zip archives. So, it has four methods: load_from_file, load_from_zip, save_to_file and save_to_zip respectfully. The problem is that I can't figure out how to specify relative paths for loading and saving for these methods. Here they go:
def load_from_file(filename)
File.open(filename) do |f|
f.each { |line| add(line) } # `add` is my another class method
end
end
def load_from_zip(filename)
Zip::File.open("#{filename}.zip") do |zipfile|
zipfile.each { |entry| read_zipped_file(zipfile, entry) } # my private method
end
end
def save_to_file(filename)
File.write("#{filename}.txt", data)
end
def save_to_zip(filename)
Zip::File.open("#{filename}.zip", Zip::File::CREATE) do |zipfile|
zipfile.get_output_stream('output.txt') { |f| f.print data }
end
end
private
def read_zipped_file(zipfile, entry)
zipfile.read(entry).lines.each { |line| add(line) }
end
So what I want basically is to allow this gem to load and save files by relative paths whereever it is used in system, e.g. I have an app located in /home/user/my_app with two files - app.rb and data.txt, and I could be able to read the file from this directory without specifying absolute path.
Example:
# app.rb
require 'my_gem'
my_gem = MyGem.new
my_gem.load_from_file('data.txt')
(Sorry for bad English)
UPD: This is not Rails gem and I'm not using Rails. All this is only pure Ruby.
Short answer
If I understand it correctly, you don't need to change anything.
Inside app.rb and your gem, relative paths will be understood relatively to Dir.pwd.
If you run ruby app.rb from inside /home/user/my_app :
Dir.pwd will be /home/user/my_app
both app.rb and my_gem will look for 'data.txt' inside /home/user/my_app.
Useful methods, just in case
Dir.chdir
If for some reason Dir.pwd isn't the desired folder, you could change directory :
Dir.chdir('/home/user/my_app') do
# relative paths will be based from /home/user/my_app
# call your gem from here
end
Get the directory of current file :
__dir__ will help you get the directory :
Returns the canonicalized absolute path of the directory of the file
from which this method is called.
Get the current file :
__FILE__ will return the current file. (Note : uppercase.)
Concatenate file paths :
If you need to concatenate file paths, use File.expand_path or File.join. Please don't concatenate strings.
If you don't trust that the relative path will be correctly resolved, you could send an absolute path to your method :
my_gem.load_from_file(File.expand_path('data.txt'))

NameError: uninitialized constant error

Given the following code:
module Backup
module Destination
class Base
def initialize
puts 'Base'
end
end
end
end
module Backup
module Destination
class Test < Base
def initialize
puts 'Test'
super
end
end
end
end
Backup::Destination::Test.new
This works as expected, outputting:
Test
Base
However if I split things up like this:
# lib/backup.rb
require_relative 'backup/destination/base'
module Backup; end
# lib/backup/destination/base.rb
require_relative 'test'
module Backup
module Destination
class Base
def initialize
puts 'Base'
end
end
end
end
# lib/backup/destination/test.rb
module Backup
module Destination
class Test < Base
def initialize
puts 'Test'
super
end
end
end
end
And execute with the following (from irb):
require_relative 'lib/backup'
I get this error:
NameError: uninitialized constant Backup::Destination::Base
from /lib/backup/destination/test.rb:3:in `<module:Destination>'
from /lib/backup/destination/test.rb:2:in `<module:Backup>'
from /lib/backup/destination/test.rb:1:in `<top (required)>'
from /lib/backup/destination/base.rb:1:in `require_relative'
from /lib/backup/destination/base.rb:1:in `<top (required)>'
from /lib/backup.rb:1:in `require_relative'
from /lib/backup.rb:1:in `<top (required)>'
from (irb):1:in `require_relative'
What am I missing?
Note: I couldn't post the above without adding more details. Stupid feature because in this case code is worth a thousand words. (this text allowed the question to be posted)
The problem is that you are requiring test.rb before your Base class is defined. One possible solution is to move your require to the bottom of base.rb.
Another possible solution is to remove your require from base and require both files in the correct order from backup.
Made the following changes to fix the problem:
# lib/backup.rb
require_relative 'backup/destination/base'
require_relative 'backup/destination/test'
module Backup; end
And removed the require_relative statement from lib/backup/destination/base.rb. This fixed the order of the require_relative statements. I mistakenly thought the files were required before anything was executed.

Cannot load data file in Sinatra

I created the following parser:
require "./artist"
require "./song"
require "./genre"
require "debugger"
class Parser
attr_accessor :artists, :genres, :song
attr_reader :mp3
REGEX = /(?<artist>.*)\s\-\s(?<song>.*)\s\[(?<genre>.*)\]/
def initialize(directory="data")
debugger
#mp3 = Dir.entries(directory).select {|f| !File.directory? f}
debugger
end
def parse
#mp3.map do |file|
match = REGEX.match(file)
artist = Artist.find_by_name(match[:artist]) || Artist.new.tap {|artist| artist.name = match[:artist]}
song = Song.new
song.name = match[:song]
song.genre = Genre.find_by_name(match[:genre]) || Genre.new.tap {|genre| genre.name = match[:genre]}
#debugger
artist.add_song(song)
end
end
end
a = Parser.new.parse
I tried running it by calling parser.rb in the directory, lib, where it is located. I get the following error messages:
Parser.rb:47:in `open': No such file or directory - data (Errno::ENOENT)
from parser.rb:47:in `entries'
from parser.rb:47:in `initialize'
from parser.rb:68:in `new'
from parser.rb:68:in `<main>'
This is my file structure:
Can anyone please tell me why it cannot recognize my data directory? I have been staring for a while now and cannot figure it out. It was working like 10 mins ago and I cannot remember what I change to get it all messed up.
Appreciate your feedback! Thanks
You should be able to run your example like ruby -I/lib lib/parser.rb from the directory above lib. The -I will set the "include path", such that the ruby interpreter will find the other required ruby files like (lib/)song.rb.

Inheritance within iron workers when using the iron_worker_ruby gem

I'm considering using IronWorker a project so that I can scale it easily (high traffic expected, with lots of background jobs).
In order to stay DRY, I'm trying to define workers using inheritance but I keep getting the following error:
/usr/local/lib/site_ruby/1.9.1/rubygems/custom_require.rb:36:in `require': cannot load such file -- base_worker.rb (LoadError)
from /usr/local/lib/site_ruby/1.9.1/rubygems/custom_require.rb:36:in `require'
from /task/child_worker.rb:3:in `<top (required)>'
from /task/runner.rb:344:in `require_relative'
from /task/runner.rb:344:in `<main>'
Here is the base worker class:
# app/workers/base_worker.rb
require 'net/http'
require 'uri'
require 'json'
class BaseWorker < IronWorker::Base
attr_accessor :params
# The run method is what IronWorker calls to run your worker
def run
data = custom_run(params)
common_post_process(data)
end
def custom_run(params)
#to be overwritten in the child class
end
def common_post_process(data)
# some common post processing => DRY
....
end
end
And here is a child class :
# app/workers/child_worker.rb
require 'net/http'
require 'uri'
require 'base_worker.rb'
class ChildWorker < BaseWorker
merge "base_worker.rb"
def custom_run(params)
#custom work
end
end
Any idea on how to fix this?
I'd recommend using our next generation gem, iron_worker_ng: https://github.com/iron-io/iron_worker_ruby_ng . The iron_worker gem is deprecated. And if you want to keep it similar style to what you have, your child_worker.rb might look like this:
require 'net/http'
require 'uri'
require_relative 'base_worker.rb'
class ChildWorker < BaseWorker
def custom_run(params)
#custom work
end
end
# NG gem doesn't run anything in particular, so to run your method:
cw = ChildWorker.new
cw.custom_run(params)
And in a child_worker.worker file:
runtime 'ruby'
file 'base_worker.rb'
exec 'child_worker.rb'
Then to upload it to IronWorker:
iron_worker upload child_worker
Then you can start queuing jobs for it:
worker = IronWorkerNG::Client.new
worker.tasks.create("child_worker", params)
If you use iron_worker_ng, it also possible to define a run method. This method will be called when the IronWorker runs. You have to specify the Class within the .worker file.
# child_worker.rb
class ChildWorker
def run
puts "doing the hard work"
end
end
And the child_worker.worker file:
# child_worker.worker
runtime 'ruby'
name 'ChildWorker'
exec 'child_worker.rb', 'ChildWorker'

Resources