How to Conditionally Rollback a Transaction Using the PG Ruby Gem - ruby

I am currently taking a databases course and one of the lab questions has me stumped as to how to implement the above and indeed, if it was even possible. I have tried searching the docs but the transaction method defined is quite vague.
This is the first time I have tried to do any database manipulation WITHOUT the comfort blanket of Rails and so I am a little lost. I have managed to create a connection to my postgresql database and can execute statements, the final thing that I need to do is rollback a transaction based on a simple condition.
Allow me to show you the code:
require 'pg'
#conn = PG::Connection.open(:dbname => 'db_15_11_labs')
#conn.prepare('insert', 'INSERT INTO house (housenumber, street) VALUES ($1, $2) returning id')
#words = ["Foo", "Bar", "Bash", "Bang"]
def populate
100.times.each_with_index do |i|
# cycle through the #words array for street names, use i as house number
ins = #conn.exec_prepared('insert', [i, "#{#words1[i % 4]} street"])
end
end
Basically, the condition is that if the returned id (ins[0]['id']) is even, rollback the transaction. I figure that if ins[0]['id'] % 2 == 0 I need to throw some sort of exception but how do I do that and more importantly, how do I encode that information into the syntax given in the docs?
Thank you for your time.
EDIT 1
I have now managed to get the syntax that will allow for the conditions to be placed within the defined transaction as follows:
#conn.transaction do |conn|
ins = #conn.exec_prepared('insert', [i, "#{#words1[i % 100]} street"])
end
and so this question really becomes more "how do I throw and catch an exception when ins[0]['id'] % 2 == 0
I have tried following a couple of simple tutorials on raising exceptions but doing the following:
throw :id_exception if (#ins[0]['id'].to_i % 2) == 0
catch :id_exception do
puts "caught :id_exception #{}"
end
inside the transaction results in an 'throw': uncaught throw :id_exception (ArgumentError)

Transactions rollback automatically if an exception is raised inside the block. So you just need to create a little Exception class for yourself, raise one inside the block, and then arrange to rescue it so your program doesn't error-exit due to the raised exception.
Try something like this:
class MyLittleIdError < StandardError
end
begin
#conn.transaction do |conn|
ins = conn.exec_prepared('insert', [i, "#{#words1[i % 100]} street"])
raise MyLittleIdError if (ins[0]['id'].to_i % 2) == 0
end
rescue MyLittleIdError
puts "aha! we have rolled back."
end

Related

Ruby console - Multiline command with exit

I have a simple ruby script I would like to run in the rails console, using bundle exec rails c
ids = [1, 2]
if ids.length() > 5
puts "More than 5 ids, quitting"
exit
end
ids.each do |id|
puts id
end
The rails console quits the program as soon as it sees the exit command. What would be the best way around this?
One way to do this is to use throw/catch:
ids = [1, 2]
catch(:bail) do
if ids.length > 5
puts "More than 5 ids, quitting"
throw :bail
end
ids.each do |id|
puts id
end
end
catch :bail defines a block that is labeled with the symbol :bail (the name doesn't really matter). It will execute normally until you throw :bail at which point Ruby zips up the call stack until its finds a corresponding catch. If no matching catch is found an UncaughtThrowError will be raised.
This is somewhat like break but can be used in any context and to break out of deeply nested constructs.
Another alternative would be to simply define a method or raise (and rescue) an exception if the input is invalid.
Raise ArgumentError When Passed Arguments are Somehow Invalid
There are a lot of ways to fix your code, but your essential problem is that Kernel#exit will exit both a standard REPL session and an application, not simply return from a method or break out of an if-statement. Since your code doesn't show that you're inside a method, you can't use return because there's no appropriate method or closure to return from. You could possibly work around calling #exit with nested irb sessions or the like, but that's ultimately solving the wrong problem.
In most cases, you want to raise an exception with a useful message rather than simply exit when something goes unfixably wrong. The following would do that in your application, while in an irb or pry console it will raise the exception but remain within your REPL session:
ids = [1, 2]
raise ArgumentError, "More than 5 ids: #{ids}" if ids.length > 5
ids.each { |id| puts id }
If you were inside a method, you could use #warn and then return an appropriate return value instead. However, since you are presumably inside the top-level object of your REPL, there's nothing to return from, so this is likely your best general-purpose option.

How to know what exceptions to rescue

I want a class that will load and save settings in yaml file, and displays an error if something happens. I must catch exceptions and I must know what to catch. There is no guarantee what exception will be raised; user might press CTRL+C or memory may run out, but YAML.load_file can raise only a limited number of exceptions. There is nowhere listed what exceptions a function YAML.load_file might raise.
How can I catch only them when I don't know what those exceptions are?
This question has been asked, but there is no real answer:
How to know what exceptions to rescue? Answer is about exceptions in general
How to deal with not knowing what exceptions can be raised by a library method in Ruby?
Ruby How to know what to rescue?
Where to find (or how to read) ruby documentation?
Documentation for Ruby exceptions
Sometimes you just don't know which kind of exception can be thrown, for that the generic rescue catching exists.
begin
do_something
rescue KnownException
treat_exception
# generic exception
rescue Exception => e
# you don't know which exception has been raised but all info is in e
print "Ups I don't know this Exception:#{e.class} error: #{e.message}"
raise
end
How can I catch only them when I don't know what those exceptions are?
What are you going to do when you catch them, I wonder? It makes little sense to catch exceptions for the sake of catching. Swallowing exceptions is especially bad practice.
My rule of thumb is: catch only those I can recover from (this implies already knowing what they are). Let the rest bubble up and crash the program or possibly be catched in one of outer scopes (which will know how to recover from this concrete one).
How to discover currently loaded exception classes
This almost 10 year old code snippet still works today:
exceptions = []
tree = {}
ObjectSpace.each_object(Class) do |cls|
next unless cls.ancestors.include? Exception
next if exceptions.include? cls
exceptions << cls
cls.ancestors.delete_if {|e| [Object, Kernel].include? e }.reverse.inject(tree) {|memo,cls| memo[cls] ||= {}}
end
indent = 0
tree_printer = Proc.new do |t|
t.keys.sort { |c1,c2| c1.name <=> c2.name }.each do |k|
space = (' ' * indent); space ||= ''
puts space + k.to_s
indent += 2; tree_printer.call t[k]; indent -= 2
end
end
tree_printer.call tree
Run it in the rails console and you'll see a lot of exception classes. :)
The source code is the documentation.
-- Matz

ruby "on error resume next" function

Is there a way of doing the old "on error resume next" routine in ruby?
I've got array of value filled in dynamically from elsewhere (read from MQTT topics to be precise) then I want to do a bunch of numeric calculations on them and publish the results. The values SHOULD be numeric but are possibly missing or non-numeric.
At the moment my code looks something like
values=[]
//values get loaded here
begin
Publish('topic1',value[0]*10+value[1])
rescue TypeError,NoMethodError,ZeroDivisionError
end
begin
Publish('topic2',value[3]/value[4])
rescue TypeError,NoMethodError,ZeroDivisionError
end
//etc etc
If the calculation fails for any reason the program should just skip that step and go on.
It works but surely theres a better way than all those identical begin..rescue blocks? Ruby is about "DRY" after all..
Is there a way of re-writing the above so that a single begin..rescue construct is used while still allowing all calculations to be attempted?
UPDATED
How safe to do something like
def safe_Publish(topic,value)
return if value.nil?
Publish(topic,value)
end
and call with
safe_Publish('topic2',(value[3]/value[4] rescue nil))
The main problem is that the above catches ALL exceptions not just the ones I'm expecting which makes me a little nervous.
The on error resume next coding style is really dangerous - as it makes finding new bugs you accidentally introduce to your program very hard to find. Instead, I would just write a different version of publish that doesn't throw those exceptions:
def try_publish(topic_name)
begin
Publish('topic1',yield)
rescue TypeError,NoMethodError,ZeroDivisionError
# are you sure you don't want to do anything here? Even logging the errors
# somewhere could be useful.
end
end
You can then call this with:
try_publish('topic1') { value[0]*10+value[1] }
If TypeError,NoMethodError or ZeroDivisionError are thrown by the expression, they will be caught and ignored.
Now your original method won't require any rescues.
If you really wanted an on error resume next, you could possibly do it by monkey patching the raise method in Kernel, but that would be a horrible idea.
If you think a bit more carefully about what you are doing, and why you want on error resume next, I think you will see that you don't really need to suppress all exceptions. As the other posters pointed out, that would make it hard to find and fix bugs.
Your problem is that you have a bunch of numbers scraped from the Internet, and want to run some calculations on them, but some may be invalid or missing. For invalid/missing numbers, you want to skip over any calculations which would use those numbers.
A few possible solutions:
Pre-filter your data and remove anything which is not a valid number.
Put each calculation you want to do into a method of its own. Put a rescue Exception on the method definition.
Define "safe" wrappers for the numeric classes which don't raise exceptions on divide by zero, etc. Use these wrappers for your calculations.
The "wrappers" might look something like this (don't expect complete, tested code; this is just to give you the idea):
# This is not designed for "mixed" arithmetic between SafeNumerics and ordinary Numerics,
# but if you want to do mixed arithmetic, that can also be achieved
# more checks will be needed, and it will also need a "coerce" method
class SafeNumeric
attr_reader :__numeric__
def initialize(numeric)
#__numeric__ = numeric.is_a?(String) ? numeric.to_f : numeric
end
def zero?
#__numeric__.zero?
end
def /(other)
if other.zero? || #__numeric__.nil? || other.__numeric__.nil?
SafeNumeric.new(nil) # could use a constant for this to reduce allocations
else
SafeNumeric.new(#__numeric__ / other.__numeric__)
end
end
def to_s; #__numeric__.to_s; end
def inspect; #__numeric__.inspect; end
# methods are also needed for +, -, *
end
Then use it like:
numbers = scraped_from_net.map { |n| SafeNumeric.new(n) }
# now you can do arithmetic on "numbers" at will
This shows how to wrap a bunch of quick operations into a loop with each one being protected by a begin/rescue:
values = [1,2,3,0,4]
ops = [ ->{values[0]/values[1]}, ->{values[2]/values[3]} ]
ops.each do |op|
begin
puts "answer is #{op.call}"
rescue ZeroDivisionError
puts "cannot divide by zero"
end
end
I prefer the safe_publish method, however, as you can unit test that and it encapsulates the logic of making safe calls and handling errors in a single place:
def safe_publish(topic, &block)
begin
value = block.call
publish(topic, value)
rescue
# handle the error
end
end
and then you can call this with code like:
safe_publish 'topic0' do
value[0]*10+value[1]
end

Can I jump back to the beginning of a method using 'redo' in Ruby?

In the Poignant Guide this example of the redo keyword is given:
class LotteryTicket
def self.new_random
new(rand(25) + 1, rand(25) + 1, rand(25) + 1)
rescue ArgumentError
redo
end
end
It's supposed to keep calling new until all three random numbers are unique. But after I typed this code in and ran it a few times, I got this error: LocalJumpError: unexpected redo. I looked up the redo keyword elsewhere and it looks like it is only supposed to work for loops and iterators. So why did why try to use it like this in his example? How should this method be rewritten to work correctly?
He must have meant to use retry, not redo.
redo restarts a block:
l = lambda {puts "hi"; redo}
l.call

How can I get source and variable values in ruby tracebacks?

Here's the last few frames of a typical Ruby on Rails traceback:
And here are the last few frames of a typical Nevow traceback in Python:
It's not just the web environment either, you can make similar comparisons between ipython and irb. How can I get more of these sorts of details in Ruby?
AFAIK, once an exception has been caught it's too late to grab the context in which it was raised. If you trap the exception's new call, you could use evil.rb's Binding.of_caller to grab the calling scope, and do
eval("local_variables.collect { |l| [l, eval(l)] }", Binding.of_caller)
But that's quite a big hack. The right answer is probably to extend Ruby to allow some inspection of the call stack. I'm not sure if some of the new Ruby implementations will allow this, but I do remember a backlash against Binding.of_caller because it will make optimizations much harder.
(To be honest, I don't understand this backlash: as long as the interpreter records enough information about the optimizations performed, Binding.of_caller should be able to work, although perhaps slowly.)
Update
Ok, I figured it out. Longish code follows:
class Foo < Exception
attr_reader :call_binding
def initialize
# Find the calling location
expected_file, expected_line = caller(1).first.split(':')[0,2]
expected_line = expected_line.to_i
return_count = 5 # If we see more than 5 returns, stop tracing
# Start tracing until we see our caller.
set_trace_func(proc do |event, file, line, id, binding, kls|
if file == expected_file && line == expected_line
# Found it: Save the binding and stop tracing
#call_binding = binding
set_trace_func(nil)
end
if event == :return
# Seen too many returns, give up. :-(
set_trace_func(nil) if (return_count -= 1) <= 0
end
end)
end
end
class Hello
def a
x = 10
y = 20
raise Foo
end
end
class World
def b
Hello.new.a
end
end
begin World.new.b
rescue Foo => e
b = e.call_binding
puts eval("local_variables.collect {|l| [l, eval(l)]}", b).inspect
end

Resources