Passing arguments while debugging - ruby-on-rails

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

Related

HTTParty::Response is cast into Hash when passed as argument to another method

I have a service method that makes api requests and if the response was not ok, it would notify Bugsnag. It looks like this:
def send_request
#response = HTTParty.get(api_endpoint, options)
return JSON.parse(#response.body, symbolize_names: true) if #response.ok?
raise StandardError.new(JSON.parse(#response.body))
rescue StandardError => exception
BugsnagService.notify(exception, #response)
end
My BugsnagService#notify looks something like this:
class BugsnagService
def self.notify(exception, response = nil, **options)
if response
response_body = if valid_json?(response.body) # Error right here
JSON.parse(response.body)
else
response.body
end
options[:response_body] = response_body
options[:response_code] = response.code
end
# Raising exception in test and development environment, or else the exception will be
# silently ignored.
raise exception if Rails.env.test? || Rails.env.development?
Bugsnag.notify(exception) do |report|
report.add_tab(:debug_info, options) if options.present?
end
end
def self.valid_json?(json_string)
JSON.parse(json_string)
true
rescue JSON::ParserError => e
false
end
end
I set response = nil in my notify method because not every error is an API error, so sometimes I would just call BugsnagService.notify(exception).
I found out that if I just call it like I am in the snippet above, it would raise an error saying it can't call .body on a Hash. Somehow, when I pass #response into BugsnagService#notify, the object turns from HTTParty::Response into Hash.
But if I pass something in for the **options parameter, it will work. So I can call it like this:
BugsnagService.notify(exception, #response, { })
I've been trying to figure this one out but I couldn't find anything that would explain this. I'm not sure if there's something wrong with the way I define my parameters or if this is some bug with the HTTParty gem. Can anyone see why this is happening? Thanks!
The problem is that your #response is being passed in as the options, as response can be nil. The double splat is converting it to a hash.
Try:
def testing(x, y = nil, **z)
puts "x = #{x}"
puts "y = #{y}"
puts "z = #{z}"
end
testing 1, 2, z: 3
#=> x = 1
#=> y = 2
#=> z = {:z=>3}
testing 1, y: 2
#=> x = 1
#=> y =
#=> z = {:y=>2}
testing 1, { y: 2 }, {}
#=> x = 1
#=> {:y=>2}
#=> {}
I'd suggest the best approach would be to have response be a keyword arg, as in:
def self.notify(exception, response: nil, **options)
...
end
That way, you can still omit or include the response as desired, and pass in subsequent options.

NoMethodError while MyMethod do exists

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)

Return members of a hashmap through a class get method

The following returns the default "client?":
class ClientMap
def initialize
##clients = {"DP000459": "BP"}
##clients.default = "client?"
end
def get(id)
return ##clients[:id]
end
end
clientMap = ClientMap.new
cKey = "DP000459"
puts clientMap.get(cKey)
Could anybody explain why I cannot retrieve anything but the 'default'?
You've got two problems. First, you are using the symbol syntax in your hash, which works only if your keys are symbols. If you want keys to be strings, you need to use hash-rocket syntax: ##clients = {'DP000459' => 'BP'}.
Second, your method returns clients[:id] regardless of what parameter is provided. The key is the symbol :id rather than the local variable id. You need to change this to ##clients[id].
Here's a cleaned-up version of what you want:
class ClientMap
def initialize
##clients = {'DP000459' => 'BP'}
##clients.default = 'client?'
end
def get(id)
##clients[id]
end
end
I've also taken the liberty of making the spacing more Ruby-idiomatic.
Finally, for variable names in Ruby, use snake_case:
>> client_map = ClientMap.new
>> c_key = 'DP000459'
>> client_map.get(c_key)
#> "BP"
Look at these code:
h = { foo: 'bar' } # => {:foo=>"bar"}
h.default = 'some default value' # => "some default value"
h[:foo] # => "bar"
h[:non_existing_key] # => "some default value"
You can read here about Hash#default method
Returns the default value, the value that would be returned by hsh if
key did not exist in hsh

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"

Resources