NoMethodError while MyMethod do exists - ruby-on-rails

I can't understand how can I solve this error since the method do really exists, I have tested it with the 'Hi' method.
irb(main):001:0> require 'yaml'
=> true
irb(main):002:0> require 'tm'
=> true
irb(main):003:0> Tm.hi
Hey Tm here for duty!
=> nil
irb(main):004:0> hashfile = YAML.load_file('fr.yaml')
=> {"fr"=>{"colors"=>{"yellow"=>"Jaune", "white"=>"Blanc"}, "hello"=>"Bonjour"}}
irb(main):005:0> t = load_translation(hashfile)
Traceback (most recent call last):
2: from /Users/abderrahmane/.rbenv/versions/2.5.1/bin/irb:11:in `<main>'
1: from (irb):5 NoMethodError (undefined method `load_translation' for main:Object)
With my class:
class Tm
def self.hi
puts "Hey Tm here for duty!"
end
def auxload(hash, lang, concat='')
ans = {}
hash.each do |key, val|
if val.class == Hash
aux = auxload(val, lang, concat+key+'.')
aux.each do |k, v|
ans[k]=v
end
else
ans[concat+key]={lang => val}
end
end
return ans
end
# load the translation from the yaml files
def load_translation(hash)
key,value = hash.first
return auxload(value,key)
end
end

To call the method on your class you first need to create an instance of the class and then call the method on that instance. e.g.
tm = Tm.new
t = tm.load_translation(hashfile)

You are not calling the load_translation method from the object of class Tm.
You need an instance of the class:
tm = Tm.new
tm.load_translation(hashfile)

It is needed to call the load_translation method at first by creating an instance of the class ~
tm = Tm.new
tm.load_translation(hashfile)

Related

Passing arguments while debugging

I'm attempting to debug a Ruby script, but I am unable to access one method as seen below.
def move_objects(target_folder)
s3_objects.each do |obj|
binding.pry
new_key = s3_folder ? obj.key.sub(s3_folder, target_folder) : "#{target_folder}/#{obj.key}"
obj.put(metadata: { 'new_key' => 'ok' })
obj.move_to(bucket: bucket_name, key: new_key)
end
self
end
When I call the method as so in Rails C:
Courts::Aws::S3Util.new('bucket_name').move_objects(target_folder)
I receive the following error.
NameError: undefined local variable or method `target_folder' for main:Object
What is the appropriate way to access this function to debug and read the new_key data?
You should initialize target_folder variable before passing it as an argument to the method
target_folder = "target-folder-name" # Initialize with appropriate folder name or leave it empty "" (i.e., target_folder = "")
Courts::Aws::S3Util.new('bucket_name').move_objects(target_folder)
Have you tried wrapping the method around a begin / rescue block ?
def move_objects(target_folder)
begin
s3_objects.each do |obj|
binding.pry
new_key = s3_folder ? obj.key.sub(s3_folder, target_folder) : "#{target_folder}/#{obj.key}"
obj.put(metadata: { 'new_key' => 'ok' })
obj.move_to(bucket: bucket_name, key: new_key)
end
self
rescue => e
binding.pry
end
end

OpenStruct issue with Ruby 2.3.1

In Ruby 2.1.5 and 2.2.4, creating a new Collector returns the correct result.
require 'ostruct'
module ResourceResponses
class Collector < OpenStruct
def initialize
super
#table = Hash.new {|h,k| h[k] = Response.new }
end
end
class Response
attr_reader :publish_formats, :publish_block, :blocks, :block_order
def initialize
#publish_formats = []
#blocks = {}
#block_order = []
end
end
end
> Collector.new
=> #<ResourceResponses::Collector>
Collector.new.responses
=> #<ResourceResponses::Response:0x007fb3f409ae98 #block_order=[], #blocks= {}, #publish_formats=[]>
When I upgrade to Ruby 2.3.1, it starts returning back nil instead.
> Collector.new
=> #<ResourceResponses::Collector>
> Collector.new.responses
=> nil
I've done a lot of reading around how OpenStruct is now 10x faster in 2.3 but I'm not seeing what change was made that would break the relationship between Collector and Response. Any help is very appreciated. Rails is at version 4.2.7.1.
Let's have a look at the implementation of method_missing in the current implementation:
def method_missing(mid, *args) # :nodoc:
len = args.length
if mname = mid[/.*(?==\z)/m]
if len != 1
raise ArgumentError, "wrong number of arguments (#{len} for 1)", caller(1)
end
modifiable?[new_ostruct_member!(mname)] = args[0]
elsif len == 0
if #table.key?(mid)
new_ostruct_member!(mid) unless frozen?
#table[mid]
end
else
err = NoMethodError.new "undefined method `#{mid}' for #{self}", mid, args
err.set_backtrace caller(1)
raise err
end
end
The interesting part is the block in the middle that runs when the method name didn't end with an = and when there are no addition arguments:
if #table.key?(mid)
new_ostruct_member!(mid) unless frozen?
#table[mid]
end
As you can see the implementation first checks if the key exists, before actually reading the value.
This breaks your implementation with the hash that returns a new Response.new when a key/value is not set. Because just calling key? doesn't trigger the setting of the default value:
hash = Hash.new { |h,k| h[k] = :bar }
hash.has_key?(:foo)
#=> false
hash
#=> {}
hash[:foo]
#=> :bar
hash
#=> { :foo => :bar }
Ruby 2.2 didn't have this optimization. It just returned #table[mid] without checking #table.key? first.

In a method, how to use a default argument in abscence of second parameter in ruby?

def alphabetize(arr,rev=false)
if rev
arr.sort!{|a,b| b<=>a}
else
arr.sort!
end
puts arr
end
alphabetize([5,3,8,1],false)
This is a code which I am supposed to submit on a codecademy exercise, but upon submission I get the following error:
It looks like your method doesn't default to alphabetizing an array when it doesn't receive a second parameter.
Remove the false argument so you have on your last line:
alphabetize([5,3,8,1])
or this worked for me:
def alphabetize(arr, rev=false)
arr.sort!
if rev
arr.reverse!
else
arr
end
end
numbers = [5,7,2,3]
alphabetize(numbers)
puts numbers
You should puts return value from this method outside
def alphabetize(arr,rev=false)
if rev
arr.sort!{|a,b| b<=>a}
else
arr.sort!
end
arr
end
puts alphabetize(['d', 'c', 'a', 'b'])
If you puts the arr inside the method, the method will return nil, not arr itself. For example:
irb(main):001:0> def test()
irb(main):002:1> puts "hello"
irb(main):003:1> end
=> :test
irb(main):004:0> a = test()
hello
=> nil
irb(main):005:0> a
=> nil
irb(main):006:0> def test()
irb(main):007:1> "hello"
irb(main):008:1> end
=> :test
irb(main):009:0> a = test()
=> "hello"

Class variable access from top level

I am a complete beginner to Ruby. I am working on Lesson 45 of Learn Ruby the Hard Way currently and am creating a game similar to Zork and Adventure.
I have created a structure where I am creating 'scenes' in different files and requiring all the scenes in one file where I have an engine/map that ensures if the current scene does not equal 'finished' that it runs 'X' scene's 'enter' method.
However I have two issues:
1) I keep getting a error saying 'Warning class variable access from top level'
2) Even though the script is running I get
ex45.rb:30:in `play': undefined method `enter' for nil:NilClass (NoMethodError) from ex45.rb:59:in
The following is all of my code from each file. My apologies if it's a long read, but I would love to know why I am getting these two errors and what I can do to fix them.
Ex45.rb:
require "./scene_one.rb"
require "./scene_two.rb"
require "./scene_three.rb"
##action = SceneOne.new
##action_two = SceneTwo.new
##action_three = SceneThree.new
class Engine
def initialize(scene_map)
#scene_map = scene_map
end
def play()
current_scene = #scene_map.opening_scene()
last_scene = #scene_map.next_scene('finished')
while current_scene != last_scene
next_scene_name = current_scene.enter()
current_scene = #scene_map.next_scene(next_scene_name)
end
current_scene.enter()
end
end
class Map
##scenes = {
'scene_one' => ##action,
'scene_two' => ##action_two,
'scene_three' => ##action_three
}
def initialize(start_scene)
#start_scene = start_scene
end
def next_scene(scene_name)
val = ##scenes[scene_name]
return val
end
def opening_scene()
return next_scene(#start_scene)
end
end
a_map = Map.new('scene_one')
a_game = Engine.new(a_map)
a_game.play()
scene_one.rb:
class SceneOne
def enter
puts "What is 1 + 2?"
print "> "
answer = $stdin.gets.chomp
if answer == "3"
puts "Good job"
return 'scene_two'
else
puts "try again"
test
end
end
end
scene_two.rb
class SceneTwo
def enter
puts "1 + 3?"
print "> "
action = $stdin.gets.chomp
if action == "4"
return 'scene_three'
else
puts "CANNOT COMPUTE"
end
end
end
scene_three.rb
class SceneThree
def enter
puts "This is scene three"
end
end
Thanks in advance!
Answer to your first question:
You need to move the class variable definitions inside your Map class to get rid of these warnings:
Ex45.rb:5: warning: class variable access from toplevel
Ex45.rb:6: warning: class variable access from toplevel
Ex45.rb:7: warning: class variable access from toplevel
So, your Map class would look like this:
class Map
##action = SceneOne.new
##action_two = SceneTwo.new
##action_three = SceneThree.new
##scenes = {
'scene_one' => ##action,
'scene_two' => ##action_two,
'scene_three' => ##action_three
}
def initialize(start_scene)
#start_scene = start_scene
end
def next_scene(scene_name)
val = ##scenes[scene_name]
return val
end
def opening_scene()
return next_scene(#start_scene)
end
end
To answer your 2nd question:
You are getting undefined method 'enter' for nil:NilClass (NoMethodError) because your current_scene becomes nil at some point and then you try to call: current_scene.enter() i.e. nil.enter and it fails with that error message.
To solve this problem, you have to make sure you always have some value in your current_scene i.e. make sure it's not nil.
I think, you can just remove current_scene.enter() line from the end of your play method in the Engine class. So, your Engine class will look like this:
class Engine
def initialize(scene_map)
#scene_map = scene_map
end
def play()
current_scene = #scene_map.opening_scene()
last_scene = #scene_map.next_scene('finished')
while current_scene != last_scene
next_scene_name = current_scene.enter()
current_scene = #scene_map.next_scene(next_scene_name)
end
# current_scene.enter()
end
end
And, you won't get that error anymore.
Just so you know:
##y = 20
p Object.class_variables
--output:--
1.rb:1: warning: class variable access from toplevel
[:##y]
And:
class Object
def self.y
##y
end
end
puts Object.y
--output:--
20
But:
class Dog
##y = "hello"
def self.y
##y
end
end
puts Dog.y #=>hello
puts Object.y #=>What do you think?
The output of the last line is the reason that class variables are not used in ruby. Instead of class variables, you should use what are known as class instance variables:
class Object
#y = 10 #class instance variable
def self.y
#y
end
end
puts Object.y
class Dog
#y = "hello"
def self.y
#y
end
end
puts Dog.y #=> hello
puts Object.y #=> 10
A class instance variable is just an #variable that is inside the class, but outside any def. And instead of there being one ##variable that is shared by all the subclasses, each subclass will have its own #variable.

NoMethodError: undefined method `add_a_file' for Testmethod:Class

What is causing this error? It doesn't matter whether or not self. is specified.
class Testmethod < ApplicationController
def add_a_file(files, next1, f)
true
end
next1 = 0
files = Array.new
Dir.chdir("db/testdir")
Dir["*.xls*"].each do |f|
self.add_a_file(files, next1, f) # Error thrown here.
next1 += 1
end
Dir["*.csv*"].each do |f|
self.add_a_file(files, next1, f)
next1 += 1
end
sfiles = files.sort { |first, second| first[1] <=> second[1] } # ASC by mdate
puts sfiles.inspect
end
You try to call add_a_file method on Testmethod class (Testmethod.add_a_file) instead of on its instance. Either you should create this class' instance with:
instance = self.new
or make add_a_file a singleton method (class method):
def self.add_a_file(files, next1, f)
true
end

Resources