Syntax error unexpected token '(' - ruby

I kept running to this unexpected error token. I wanted the program to run without invoking ruby. For instances, instead of ruby program1.rb, i should be able to program1.rb poem.txt.
This is the error message:
program1.rb --backwards poem.txt
./program1.rb: line 1: syntax error near unexpected token `('
./program1.rb: line 1: `def backlines(line_array)'
This is my code:
def backlines(*line_array)
end
def backwards(line_array)
end
def backchars(line_array)
end
def main
file = File.new(ARGV[1], "r") do |file|
line_array = file.readlines
*line_array = line_array.reverse
if ARGV[0] == "--backlines"
*backwards_poem = backlines(line-array)
#you can manipulate "backwards_poem" however you want
elsif ARGV[0] == "--backwards"
backwards(line_array)
elsif ARGV[0] == "--backchars"
backchars(line_Array)
end
# passing a *line_array into a function
end
main

Have you executed ruby in your script at the top? eg:
#!/usr/bin/ruby

#!/usr/bin/ruby
def backlines(*line_array)
end
def backwards(line_array)
end
def backchars(line_array)
end
def main
puts ARGV
# File open not new ... this block requires the end below.
File.open(ARGV[1], "r") do |file|
line_array = file.readlines
*line_array = line_array.reverse
if ARGV[0] == "--backlines"
*backwards_poem = backlines(line_array)
#you can manipulate "backwards_poem" however you want
elsif ARGV[0] == "--backwards"
backwards(line_array)
elsif ARGV[0] == "--backchars"
backchars(line_array)
end
end
# passing a *line_array into a function
end
this should start you off if your call it like ./program1.rb --backwards file
you also had line-array , line_array and line_Array which should al be one variable i think.

Related

Create directory with ARGV (ruby)

I created this program to create a folder in the program directory if there is an ARGV.
def check_if_user_gave_input
abort("mkdir: missing input") if ARGV.empty?
end
def get_folder_name
return folder_name = ARGV.first
end
def create_folder(name)
Dir.mkdir(name)
end
def perform
folder_name = get_folder_name
create_folder(folder_name)
end
perform
So, if I run this program in my terminal everything is OK. But, if I try it in my terminal and don't write anything after $ ruby app.rb I get a nice error message like this and I don't see the string "mkdir: missing input"
Traceback (most recent call last):
3: from app.rb:18:in `<main>'
2: from app.rb:15:in `perform'
1: from app.rb:10:in `create_folder'
app.rb:10:in `mkdir': no implicit conversion of nil into String (TypeError)
How to fix this? Thank you.
Just add check_if_user_gave_input method in perform
def check_if_user_gave_input
abort("mkdir: missing input") if ARGV.empty?
end
def get_folder_name
ARGV.first
end
def create_folder(name)
Dir.mkdir(name)
end
def perform
check_if_user_gave_input
folder_name = get_folder_name
create_folder(folder_name)
end
perform

How do I get ruby to print a full backtrace that includes arguments passed to functions?

Sometimes backtrace is enough to diagnose problem. But sometimes reason of crash is not obvious without knowledge what was passed to function.
Getting information what was passed to function that caused crash would be quite useful, especially in cases where reproducing is not obvious because it was caused by for example exception in network connection, weird user input or because program is depends on randomisation or processes data from external sensor.
Lets say that there is following program
def handle_changed_input(changed_input)
raise 'ops' if changed_input =~ /magic/
end
def do_something_with_user_input(input)
input = "#{input.strip}c"
handle_changed_input(input)
end
input = gets
do_something_with_user_input(input)
where user typed "magic" as input. Normally one has
test.rb:2:in `handle_changed_input': ops (RuntimeError)
from test.rb:7:in `do_something_with_user_input'
from test.rb:11:in `<main>'
as output. What one may do to show also what was passed to function? Something like
test.rb:2:in `handle_changed_input("magic")': ops (RuntimeError)
from test.rb:7:in `do_something_with_user_input("magi\n")'
from test.rb:11:in `<main>'
It would be useful in many situations (and not truly useful where parameters are not representable as strings of reasonable legth, there is a good reason why it is not enabled by default).
How one may add this functionality? It is necessary that program works as usually during normal operation and preferably there is no additional output before crash.
I tried for example
def do_something_with_user_input(input)
method(__method__).parameters.map do |_, name|
puts "#{name}=#{binding.local_variable_get(name)}"
end
raise 'ops' if input =~ /magic/
end
input = gets
found in Is there a way to access method arguments in Ruby? but it would print on every single entrance to function what both would flood output and make program significantly slower.
I don't have a complete solution but... But you can get method arguments of all called methods in controlled environment with TracePoint class from Ruby core lib.
Look at the example:
trace = TracePoint.new(:call) do |tp|
puts "===================== #{tp.method_id}"
b_self = tp.binding.eval('self')
names = b_self.method(tp.method_id).parameters.map(&:last)
values = names.map { |name| tp.binding.eval(name.to_s) }
p names.zip(values)
end
trace.enable
def method_a(p1, p2, p3)
end
method_a(1, "foobar", false)
#=> ===================== method_a
#=> [[:p1, 1], [:p2, "foobar"], [:p3, false]]
To print exception backtraces, Ruby uses the C function exc_backtrace from error.c (exc_backtrace on github). Unless you patch Ruby with the functionality you need, I don't think there a way to change exception backtrace outputs.
Here is a snippet (trace.rb) you might find useful:
set_trace_func -> (event, file, line, id, binding, classname) do
if event == 'call' && meth = binding.eval('__method__')
params = binding.method(meth).parameters.select{|e| e[0] != :block}
values = params.map{|_, var| [var, binding.local_variable_get(var)]}
printf "%8s %s:%-2d %15s %8s %s\n", event, file, line, id, classname, values.inspect
else
printf "%8s %s:%-2d %15s %8s\n", event, file, line, id, classname
end
end
def foo(a,b = 0)
bar(a, foo: true)
end
def bar(c, d = {})
puts "!!!buz!!!\n"
end
foo('lol')
The output of that snippet is:
c-return /path/to/trace.rb:1 set_trace_func Kernel
line /path/to/trace.rb:12
c-call /path/to/trace.rb:12 method_added Module
c-return /path/to/trace.rb:12 method_added Module
line /path/to/trace.rb:16
c-call /path/to/trace.rb:16 method_added Module
c-return /path/to/trace.rb:16 method_added Module
line /path/to/trace.rb:20
call /path/to/trace.rb:12 foo Object [[:a, "lol"], [:b, 0]]
line /path/to/trace.rb:13 foo Object
call /path/to/trace.rb:16 bar Object [[:c, "lol"], [:d, {:foo=>true}]]
line /path/to/trace.rb:17 bar Object
c-call /path/to/trace.rb:17 puts Kernel
c-call /path/to/trace.rb:17 puts IO
c-call /path/to/trace.rb:17 write IO
!!!buz!!!
c-return /path/to/trace.rb:17 write IO
c-return /path/to/trace.rb:17 puts IO
c-return /path/to/trace.rb:17 puts Kernel
return /path/to/trace.rb:18 bar Object
return /path/to/trace.rb:14 foo Object
I hope that helps you as much as it helped me.
I think that it is possible. The code below is not perfect and would require some additional work, but it caputers the primary idea of a stacktrace with argument values. Please note, that in order to know the call site, I am zipping the original stacktrace with the entry sites catched by trace function. To distinguishe these entries I use '>' and '<' respectively.
class Reporting
def self.info(arg1)
puts "*** #{arg1} ***"
end
end
def read_byte(arg1)
Reporting.info(arg1)
raise Exception.new("File not found")
end
def read_input(arg1)
read_byte(arg1)
end
def main(arg1)
read_input(arg1)
end
class BetterStacktrace
def self.enable
set_trace_func -> (event, file, line, id, binding, classname) do
case event
when 'call'
receiver_type = binding.eval('self.class')
if receiver_type == Object
meth = binding.eval('__method__')
params = binding.method(meth).parameters.select{|e| e[0] != :block}
values = params.map{|_, var| [var, binding.local_variable_get(var)]}
self.push(event, file, line, id, classname, values)
else
self.push(event, file, line, id, classname)
end
when 'return'
self.pop
when 'raise'
self.push(event, file, line, id, classname)
Thread.current[:_keep_stacktrace] = true
end
end
end
def self.push(event, file, line, id, classname, values=nil)
Thread.current[:_saved_stacktrace] = [] unless Thread.current.key?(:_saved_stacktrace)
unless Thread.current[:_keep_stacktrace]
if values
values_msg = values.map(&:last).join(", ")
msg = "%s:%d:in `%s(%s)'" % [file, line, id, values_msg]
else
msg = "%s:%d:in `%s'" % [file, line, id]
end
Thread.current[:_saved_stacktrace] << msg
end
end
def self.pop()
Thread.current[:_saved_stacktrace] = [] unless Thread.current.key?(:_saved_stacktrace)
unless Thread.current[:_keep_stacktrace]
value = Thread.current[:_saved_stacktrace].pop
end
end
def self.disable
set_trace_func nil
end
def self.print_stacktrace(calls)
enters = Thread.current[:_saved_stacktrace].reverse
calls.zip(enters).each do |call, enter|
STDERR.puts "> #{enter}"
STDERR.puts "< #{call}"
end
Thread.current[:_saved_stacktrace] = []
end
end
BetterStacktrace.enable
begin
main(10)
rescue Exception => ex
puts "--- Catched ---"
puts ex
BetterStacktrace.print_stacktrace(ex.backtrace)
end
BetterStacktrace.disable
begin
main(10)
rescue Exception
puts "--- Catched ---"
puts ex
puts ex.backtrace
end
The output of the above code is as follows:
*** 10 ***
--- Catched ---
File not found
> work/tracing_with_params.rb:10:in `read_byte'
< work/tracing_with_params.rb:10:in `read_byte'
> work/tracing_with_params.rb:8:in `read_byte(10)'
< work/tracing_with_params.rb:14:in `read_input'
> work/tracing_with_params.rb:13:in `read_input(10)'
< work/tracing_with_params.rb:18:in `main'
> work/tracing_with_params.rb:17:in `main(10)'
< work/tracing_with_params.rb:82:in `<main>'
*** 10 ***
--- Catched ---
File not found
work/tracing_with_params.rb:10:in `read_byte'
work/tracing_with_params.rb:14:in `read_input'
work/tracing_with_params.rb:18:in `main'
work/tracing_with_params.rb:82:in `<main>'
EDIT:
The calls to class functions are not recorded. This has to be fixed in order for the stacktrace printing function not to get invalid output.
Moreover I used the STDERR as output to easily get one or the other output. You can change it if you wish.
MAX_STACK_SIZE = 200
tracer = proc do |event|
if event == 'call' && caller_locations.length > MAX_STACK_SIZE
fail "Probable Stack Overflow"
end
end
set_trace_func(tracer)

Ruby Error: wrong number of arguments (1 for 0) (ArgumentError)

I am run this code but I am getting an error.
code here :-
class Text
def post(success, error)
if authenticate?(#user, #password)
success.call
else
erro.call
end
end
end
text = Text.new('Ruby Bits!')
success = ->{ puts "Sent!"}
error = ->{ raise 'Auth error'}
text.post(success,error)
Please tell us. How to solve this problem?
Look closely at which line number the ArgumentError is raised from (in your case, line 25).
You are passing one argument to the Text#initialize, but have not defined a version of initialize that takes one argument.
Try this instead (calling the default, zero argument Text constructor):
class Text
def post(success, error)
if authenticate?(#user, #password)
success.call
else
error.call
end
end
end
text = Text.new
success = ->{ puts "Sent!"}
error = ->{ raise 'Auth error'}
text.post(success, error)
Or define initialize with one argument:
class Text
def initialize(your_meaningful_argument)
# do stuff
end
def post(success, error)
if authenticate?(#user, #password)
success.call
else
error.call
end
end
end

Ruby: Chatterbot can't load bot data

I'm picking up ruby language and get stuck at playing with the chatterbot i have developed. Similar issue has been asked here Click here , I did what they suggested to change the rescue in order to see the full message.But it doesn't seem right, I was running basic_client.rb at rubybot directory and fred.bot is also generated at that directory . Please see the error message below: Your help very be very much appreciated.
Snailwalkers-MacBook-Pro:~ snailwalker$ cd rubybot
Snailwalkers-MacBook-Pro:rubybot snailwalker$ ruby basic_client.rb
/Users/snailwalker/rubybot/bot.rb:12:in `rescue in initialize': Can't load bot data because: No such file or directory - bot_data (RuntimeError)
from /Users/snailwalker/rubybot/bot.rb:9:in `initialize'
from basic_client.rb:3:in `new'
from basic_client.rb:3:in `<main>'
basic_client.rb
require_relative 'bot.rb'
bot = Bot.new(:name => 'Fred', :data_file => 'fred.bot')
puts bot.greeting
while input = gets and input.chomp != 'end'
puts '>> ' + bot.response_to(input)
end
puts bot.farewell
bot.rb:
require 'yaml'
require './wordplay'
class Bot
attr_reader :name
def initialize(options)
#name = options[:name] || "Unnamed Bot"
begin
#data = YAML.load(File.read('bot_data'))
rescue => e
raise "Can't load bot data because: #{e}"
end
end
def greeting
random_response :greeting
end
def farewell
random_response :farewell
end
def response_to(input)
prepared_input = preprocess(input).downcase
sentence = best_sentence(prepared_input)
reversed_sentence = WordPlay.switch_pronouns(sentence)
responses = possible_responses(sentence)
responses[rand(responses.length)]
end
private
def possible_responses(sentence)
responses = []
#data[:responses].keys.each do |pattern|
next unless pattern.is_a?(String)
if sentence.match('\b' + pattern.gsub(/\*/, '') + '\b')
if pattern.include?('*')
responses << #data[:responses][pattern].collect do |phrase|
matching_section = sentence.sub(/^.*#{pattern}\s+/, '')
phrase.sub('*', WordPlay.switch_pronouns(matching_section))
end
else
responses << #data[:responses][pattern]
end
end
end
responses << #data[:responses][:default] if responses.empty?
responses.flatten
end
def preprocess(input)
perform_substitutions input
end
def perform_substitutions(input)
#data[:presubs].each {|s| input.gsub!(s[0], s[1])}
input
end
# select best_sentence by looking at longest sentence
def best_sentence(input)
hot_words = #data[:responses].keys.select do |k|
k.class == String && k =~ /^\w+$/
end
WordPlay.best_sentence(input.sentences, hot_words)
end
def random_response(key)
random_index = rand(#data[:responses][key].length)
#data[:responses][key][random_index].gsub(/\[name\]/, #name)
end
end
I'm assuming that you are trying to load the :data_file passed into Bot.new, but right now you are statically loading a bot_data file everytime. You never mentioned about bot_data in the question. So if I'm right it should be like this :
#data = YAML.load(File.read(options[:data_file]))
Instead of :
#data = YAML.load(File.read('bot_data'))

Thor::Group do not continue if a condition is not met

I'm converting a generator over from RubiGen and would like to make it so the group of tasks in Thor::Group does not complete if a condition isn't met.
The RubiGen generator looked something like this:
def initialize(runtime_args, runtime_options = {})
super
usage if args.size != 2
#name = args.shift
#site_name=args.shift
check_if_site_exists
extract_options
end
def check_if_site_exists
unless File.directory?(File.join(destination_root,'lib','sites',site_name.underscore))
$stderr.puts "******No such site #{site_name} exists.******"
usage
end
end
So it'd show a usage banner and exit out if the site hadn't been generated yet.
What is the best way to recreate this using thor?
This is my task.
class Page < Thor::Group
include Thor::Actions
source_root File.expand_path('../templates', __FILE__)
argument :name
argument :site_name
argument :subtype, :optional => true
def create_page
check_if_site_exists
page_path = File.join('lib', 'sites', "#{site_name}")
template('page.tt', "#{page_path}/pages/#{name.underscore}_page.rb")
end
def create_spec
base_spec_path = File.join('spec', 'isolation', "#{site_name}")
if subtype.nil?
spec_path = base_spec_path
else
spec_path = File.join("#{base_spec_path}", 'isolation')
end
template('functional_page_spec.tt', "#{spec_path}/#{name.underscore}_page_spec.rb")
end
protected
def check_if_site_exists # :nodoc:
$stderr.puts "#{site_name} does not exist." unless File.directory?(File.join(destination_root,'lib','sites', site_name.underscore))
end
end
after looking through the generators for the spree gem i added a method first that checks for the site and then exits with code 1 if the site is not found after spitting out an error message to the console. The code looks something like this:
def check_if_site_exists
unless File.directory?(path/to/site)
say "site does not exist."
exit 1
end
end

Resources