ruby syntactic sugar for core only arg parsing - ruby

Syntactic sugar for counting the non-switch args?
argn = 0
ARGV.each do |arg|
mission = Mission_DB.new unless mission
if(arg.match(/^\-.*=/)) then
fsav = arg.split('=')[1] if arg.match(/\-save=/)
skip = arg.split('=')[1].split(',') if arg.match(/\-skip=/)
mission.mission = arg.split('=')[1].downcase if arg.match(/\-mission=/)
else
argn += 1
mission.parseP1SCLRV(arg) if argn == 1
template = arg if argn == 2
mission.parseP1SCLRVPLV(arg) if argn == 3
mission.parseDbUserLimits(arg) if argn == 4
end
end
I've tried to find a reusable, clean core only arg parsing syntax that is intuitive (readable without comments). This isn't bad ... but its not best good enough.
I hate the argn crap
I hate the if /-.*=/ followed by each individual switch
each_with_index won't really accomplish what I'm looking for as switches could be embedded anywhere within the non switched args
Feel free to revamp entirely provided we are sufficing the "clean, core only, intuitive" requirements.
Thanks and Cheers --Reed

Have you considered using optparse? It comes with the Ruby standard library.
Here's a class that uses optparse to do option parsing for the arguments in your example. It handles errors, prints its usage if "-h" or "--help" is given, and encapsulates the parsed arguments into a nice class you can pass around:
require 'optparse'
class Arguments
POSITIONAL_ARGS = %w(p1sclrv template p1sclrvplv db_user_limits)
attr_reader :mission
attr_reader :save_path
attr_reader :what_to_skip
attr_reader *POSITIONAL_ARGS
def initialize(argv)
option_parser.parse!(argv)
POSITIONAL_ARGS.each do |positional_arg|
value = argv.shift
instance_variable_set("##{positional_arg}", value)
raise OptionParser::MissingArgument, positional_arg.to_s unless value
end
raise OptionParser::NeedlessArgument, argv.first unless argv.empty?
rescue OptionParser::ParseError => e
puts e
puts option_parser
exit(1)
end
private
def option_parser
OptionParser.new do |op|
op.banner += ' ' + POSITIONAL_ARGS.join(' ')
op.on('--save=PATH', 'Save to PATH') do |value|
#save_path = value
end
op.on('--skip=WHAT', 'Skip WHAT') do |value|
#what_to_skip = value
end
op.on('--mission=NAME', 'Do mission NAME') do |value|
#mission = value
end
end
end
end
In real use, pass ARGV to it:
args = Arguments.new(ARGV)
Here's an example passing a made-up ARGV and printing the parsed arguments:
args = Arguments.new(%w(--skip=FOO alpha bravo charley delta))
p args.p1sclrv # => "alpha"
p args.template # => "bravo"
p args.p1sclrvplv # => "charley"
p args.db_user_limits # => "delta"
p args.mission # => nil
p args.save_path # => nil
p args.what_to_skip # => "FOO"
Here's what the help looks like:
Arguments.new(%w(--help))
# => Usage: foo [options] p1sclrv template p1sclrvplv db_user_limits
# => --save=PATH Save to PATH
# => --skip=WHAT Skip WHAT
# => --mission=NAME Do mission NAME

My own iterating:
fsav = nil
skip = []
template = p1sclrv = p1sclrvplv = dbuserlimits = nil
ARGV.each do |arg|
case arg
when /\-save=/ then fsav = arg.split('=')[1]
when /\-skip=/ then skip = arg.split('=')[1].split(',')
when /\-mission=/ then mission.mission = arg.split('=')[1].downcase
else
unless(p1sclrv) then p1sclrv = arg; next; end
unless(template) then template = arg; next; end
unless(p1sclrvplv) then p1sclrvplv = arg; next; end
unless(dbuserlimits) then dbuserlimits = arg; next; end
end
end
EDIT: Getting better.

Related

Get local variables of previous scope

I am making a Ruby REPL to be used inside an application. I made code:
a = 1
b = 2
currentScope = []
Kernel.local_variables.each do |var|
currentScope << [var,Kernel.eval(var.to_s)]
end
launchREPL(currentScope)
Inside the REPL, I can execute the following code:
#a #=>1
#a+#b #=>3
Ideally I wouldn't have to write the four lines of code before I launch the REPL, and instead I would like to run them inside the launchREPL function. However this would require access to the previous scope from inside the launchREPL function.
Test1
Most notably I tried:
launchREPL(Kernel)
When I do the following:
def launchREPL(scope)
F = 0
puts scope.local_variables # => [:F]
end
it is apparent that this method is not valid.
Test2
launchREPL(Kernel.binding)
def launchREPL(scope)
Kernel.binding.local_variables #= Error: private method 'local_variables' called for #<Binding>
end
Is there any way to do what I'm trying to do?
Edit: P.S. This is currently the code inside launchREPL:
def launchREPL(scope=nil,winName="Ruby REPL")
# ICM RB file Begin:
puts "\"Starting REPL...\""
__b = binding #Evaluating in a binding, keeps track of local variables
__s = ""
###############################################################################
# SEND INSTANCE VARIABLES TO REPL
###############################################################################
#
#How to prepare scope
# currentScope = []
# Kernel.local_variables.each do |var|
# currentScope << [var,Kernel.eval(var.to_s)]
# end
# launchREPL(currentScope)
if scope != nil
scope.each do |varDef|
__b.instance_variable_set "##{varDef[0].to_s}" , varDef[1]
__b.eval("##{varDef[0].to_s} = __b.instance_variable_get(:##{varDef[0].to_s})")
end
end
# to get instance variables: __b.instance_variable_get(__b.instance_variables[0])
# or better: __b.instance_variable_get(:#pipe1)
#
###############################################################################
bStartup = true
while bStartup || __s != ""
# If startup required skip evaluation step
if !bStartup
#Evaluate command
begin
__ret = __s + "\n>" + __b.eval(__s).to_s
rescue
__ret = __s + "\n> Error: " + $!.to_s
end
puts __ret
else
#REPL is already running
bStartup = false
end
#Read user input & print previous output
__s = WSApplication.input_box(__ret,winName,"")
__s == nil ? __s = "" : nil
end
end
Although what you are trying to achieve is unclear and there are definitely many ways to do it properly, every ruby method might be called with Object#send approach:
def launchREPL(scope)
scope.send :local_variables #⇒ here you go
end
a = 42
launchREPL(binding).include?(:a)
#⇒ true
Sidenote: this is how your “4 lines” are usually written in ruby:
local_variables.map { |var| [var, eval(var.to_s)] }
And this is how they should be written (note Binding#local_variable_get):
local_variables.map { |var| [var, binding.local_variable_get(var)] }
The summing up:
def launchREPL(scope)
vars = scope.send(:local_variables).map do |var|
[var, scope.local_variable_get(var)]
end
# some other code
end
a = 42
launchREPL(binding).to_h[:a]
#⇒ 42
This won’t fit the comment, so I would post it as an answer.
def launchREPL(scope = nil, winName = "Ruby REPL")
puts '"Starting REPL..."'
scope.eval('local_variables').each do |var|
instance_variable_set "##{var}", scope.eval(var.to_s)
end if scope
s = ""
loop do
ret = begin
"#{s}\n> #{eval(s)}"
rescue => e
"#{s}\n> Error: #{e.message}"
end
puts ret
# s = WSApplication.input_box(ret, winName, "")
# break if s.empty?
s = "100 * #a" # remove this line and uncomment 2 above
end
end
a = 42
launchREPL(binding)
This is how your function should be written (I have just make it looking as ruby code.) The above works (currently it has no break at all, but you can see as it’s calculating 4200 infinitely.)

What distinguishes .new from .new(true)? "Indent" XML document in Test-First-Ruby

I'm working through these problems and am a little stuck on how to finish this out. This is the RSPEC and what's specifically troubling me is the last "it indents" test:
# # Topics
#
# * method_missing
# * blocks
# * strings
# * hashes
require "13_xml_document"
describe XmlDocument do
before do
#xml = XmlDocument.new
end
it "renders an empty tag" do
#xml.hello.should == "<hello/>"
end
it "renders a tag with attributes" do
#xml.hello(:name => 'dolly').should == "<hello name='dolly'/>"
end
it "renders a randomly named tag" do
tag_name = (1..8).map{|i| ('a'..'z').to_a[rand(26)]}.join
#xml.send(tag_name).should == "<#{tag_name}/>"
end
it "renders block with text inside" do
#xml.hello do
"dolly"
end.should == "<hello>dolly</hello>"
end
it "nests one level" do
#xml.hello do
#xml.goodbye
end.should == "<hello><goodbye/></hello>"
end
it "nests several levels" do
xml = XmlDocument.new
xml.hello do
xml.goodbye do
xml.come_back do
xml.ok_fine(:be => "that_way")
end
end
end.should == "<hello><goodbye><come_back><ok_fine be='that_way'/></come_back></goodbye></hello>"
end
it "indents" do
#xml = XmlDocument.new(true)
#xml.hello do
#xml.goodbye do
#xml.come_back do
#xml.ok_fine(:be => "that_way")
end
end
end.should ==
"<hello>\n" +
" <goodbye>\n" +
" <come_back>\n" +
" <ok_fine be='that_way'/>\n" +
" </come_back>\n" +
" </goodbye>\n" +
"</hello>\n"
end
end
I feel like I understand the problem and the solution would be something like
"<#{method}>\n" + " #{yield}" + "</#{method}>\n"
given my code:
class XmlDocument
#use method missing so that arbitrary methods
#can be called and converted to XML
def method_missing(method, hash=nil, &block)
if (hash == nil && block == nil)
"<#{method}/>"
elsif hash.is_a?(Hash)
#renders tag with attributes (from hash)
my_key = nil
my_val = nil
hash.each do |key, value|
my_key = key
my_val = value
end
"<#{method} #{my_key}='#{my_val}'/>"
else
#passes whatever to between tags including nested methods.
"<#{method}>#{yield}</#{method}>"
end
end
end
My problem is I don't know how to distinguish the "it nests several levels" test from the "it indents" test so I can fit it into my "if" statement. The only thing that seems to distinguish them is the the "it indents" test has
#xml = XmlDocument.new(true)
What does it mean to have "true" as an argument of #new? Is it relevant to my problem?
You can pass an argument to the object when it is initialized. In this case, you want the value to default to false. This way, the code for indents only runs when XmlDocument.new(true) is called.
class XmlDocument
def initialize(indent = false)
#indent = indent
end
def method_missing(method, args=nil, &block)
if #indent == true
#run with indents
else
#run without indents
end
end
end

Need help indenting tags in the output in Ruby

UPDATE: OK, so I implemented your code, but now the indentation is not showing up! Any ideas what might be wrong? I modified the code so that it would attempt to pass my original test (this is only an exercise so in real life I would not be overriding the XmlDocument class) and here is the modified code:
class XmlDocument
attr_reader :indent_depth, :bool
def initialize(bool = false, indent_depth = 0)
#indent_depth = indent_depth
#bool = bool
end
def method_missing(name, *args)
indentation = ' '*indent_depth
attrs = (args[0] || {}).map { |k, v| " #{k}='#{v}'" }.join(' ')
if block_given?
puts indent_depth
opening = "#{indentation}<#{name}#{attrs}>"
contents = yield(XmlDocument.new(true,indent_depth+1))
closing = "#{indentation}</#{name}>"
bool ? opening + "\n" + contents + "\n" + closing : opening + contents + closing
else
"#{indentation}<#{name}#{attrs}/>"
end
end
end
I'm trying to get the method to pass this test:
it "indents" do
#xml = XmlDocument.new(true)
#xml.hello do
#xml.goodbye do
#xml.come_back do
#xml.ok_fine(:be => "that_way")
end
end
end.should ==
"<hello>\n" +
" <goodbye>\n" +
" <come_back>\n" +
" <ok_fine be='that_way'/>\n" +
" </come_back>\n" +
" </goodbye>\n" +
"</hello>\n"
...but I'm unsure as to where to go with my code, below. I was thinking of using a counter to keep track of how far indented we have to go. I tried some code, but then deleted it because it was getting too messy and I have a feeling that the indentation should not be too complicated to implement.
class XmlDocument
def initialize(bool = false)
#bool = bool
end
def send(tag_name)
"<#{tag_name}/>"
end
def method_missing(meth, arg={}, &block)
arbitrary_method = meth.to_s
tag_string = ''
# 1) test for block
# 2) test for arguments
# 3) test for hash
if block_given? # check for #xml.hello do; #xml.goodbye; end
if yield.class == String # base case: #xml.hello do; "yellow"; end
"<#{arbitrary_method}>#{yield}</#{arbitrary_method}>"
else # in the block we do not have a string, we may have another method
method_missing(yield)
end
elsif arg.empty? # no arguments e.g. #xml.hello
send(arbitrary_method)
else # hash as argument e.g. #xml.hello(:name => 'dolly')
send("#{arbitrary_method} #{arg.keys[0]}='#{arg.values[0]}'")
end
end
end
Your code needs a lot of work - some pointers:
Do not override the send method!
Don't call yield over and over - you don't know what side effects you might cause, not to mention a performance hit - call it once, and remember the return value.
You might want to read up on how to write a DSL (here is a blogpost on the subject), to see how it was done correctly in other places.
Ignoring the above, I will try to answer your question regarding indentation.
In a DSL use case, you might want to use a context object which holds the indentation depth as state:
class Indented
attr_reader :indent_depth
def initialize(indent_depth = 0)
#indent_depth = indent_depth
end
def method_missing(name, *args)
indentation = ' ' * indent_depth
attrs = (args[0] || {}).map { |k, v| "#{k}='#{v}'" }.join(' ')
if block_given?
"#{indentation}<#{name} #{attrs}>\n" +
yield(Indented.new(indent_depth + 1)) +
"\n#{indentation}</#{name}>"
else
"#{indentation}<#{name} #{attrs}/>"
end
end
end
xml = Indented.new
puts xml.hello do |x|
x.goodbye do |x|
x.come_back do |x|
x.ok_fine(:be => "that_way")
end
end
end
# => <hello >
# => <goodbye >
# => <come_back >
# => <ok_fine be='that_way'/>
# => </come_back>
# => </goodbye>
# => </hello>

Ruby: Using StringScanner causes infinite loop

I have the following class:
require 'strscan'
class ConfParser
include Enumerable
class Error < StandardError; end
VERSION = '0.0.1'
SECTION_REGEX = /^\[ # Opening bracket
([^\]]+) # Section name
\]$ # Closing bracket
/x
PARAMETER_REGEX = /^\s*([^:]+) # Option
:
(.*?)$ # Value
/x
attr_accessor :filename, :sections
CONFIG_DIRECTORY = "./config"
ENCODING = "UTF-8"
def self.read(filename, opts = {})
new(opts.merge(:filename => filename))
end
def initialize(opts = {})
#filename = opts.fetch(:filename)
#separator = opts.fetch(:separator, ":")
#file = "#{CONFIG_DIRECTORY}/#{#filename}"
#content = nil
#config = Hash.new { |h,k| h[k] = Hash.new }
load
end
def load
raise_error("First line of config file contain be blank") if first_line_empty?
f = File.open(#file, 'r')
#content = f.read
parse!
ensure
f.close if f && !f.closed?
end
def sections
#config.keys
end
def [](section)
return nil if section.nil?
#config[section.to_s]
end
def []=( section, value )
#config[section.to_s] = value
end
private
def parse!
#_section = nil
#_current_line = nil
property = ''
string = ''
#config.clear
scanner = StringScanner.new(#content)
until scanner.eos?
#_current_line = scanner.check(%r/\A.*$/) if scanner.bol?
if scanner.scan(SECTION_REGEX)
#_section = #config[scanner[1]]
else
tmp = scanner.scan_until(%r/([\n"#{#param}#{#comment}] | \z | \\[\[\]#{#param}#{#comment}"])/mx)
raise_error if tmp.nil?
len = scanner[1].length
tmp.slice!(tmp.length - len, len)
scanner.pos = scanner.pos - len
string << tmp
end
end
process_property(property, string)
logger #config
end
def process_property( property, value )
value.chomp!
return if property.empty? and value.empty?
return if value.sub!(%r/\\\s*\z/, '')
property.strip!
value.strip!
parse_error if property.empty?
current_section[property.dup] = unescape_value(value.dup)
property.slice!(0, property.length)
value.slice!(0, value.length)
nil
end
def logger log
puts "*"*50
puts log
puts "*"*50
end
def first_line_empty?
File.readlines(#file).first.chomp.empty?
end
def raise_error(msg = 'Error processing line')
raise Error, "#{msg}: #{#_current_line}"
end
def current_section
#_section ||= #config['header']
end
end
The above class parses files that are setup like so:
[header]
project: Hello World
budget : 4.5
accessed :205
[meta data]
description : This is a tediously long description of the Hello World
project that you are taking. Tedious isn't the right word, but
it's the first word that comes to mind.
correction text: I meant 'moderately,' not 'tediously,' above.
[ trailer ]
budget:all out of budget.
You start running it like this:
require 'conf_parser'
cf = ConfParser.read "/path/to/conf/file"
For some reason when the parse! method runs, an infinite loop occurs and I can't figure out why. Any reason why this would be happening? I have never used StringScanner before, so it may be my lack of knowledge of the class
At the risk of stating the obvious, you are most likely never satisfying scanner.eos?, which in turn would mean that you're not advancing the scan pointer to the end of the string. Since the only change to scanner.pos in the else branch of parse! is to decrement it (i.e. by len), this would be understandable. If the if branch doesn't advance it to the end, you'll never terminate.

Compare the Content, Not the Results, of Procs

Using Ruby 1.9.2
Problem
Compare the content, not the results, of two procs. I understand the results can't be tested because of the halting problem but that's OK; I don't want to test the results anyway.
For instance
proc {#x == "x"} == proc {#x == "x"} => false # doh!
That returns false because the objects inside the procs are not the same.
My clunky solution
I have a work around solution that kinda sorta does what I want but it doesn't really test that the proc is "equal" to what I put in it. In my specific case the format of my procs will always be boolean tests on instance variables like this:
{#x == "x" && #y != "y" || #z == String}
I wrote a method that builds classes dynamically and creates instance variables set to specified values:
def create_proc_tester(property_value_hash)
new_class = Class.new.new
new_class.class.class_eval do
define_method(:xql?) { |&block| instance_eval &block }
end
property_value_hash.each do |key, value|
new_class.instance_variable_set("##{key}", value)
end
new_class
end
Which could be used something like this:
class Foo
attr_accessor :block
end
foo = Foo.new
foo.block = proc {#x == "x" && #y != "y" || #z == String}
tester = create_proc_tester(:x => "x", :y => "y", :z => Fixnum)
puts "Test #1: #{tester.xql? &foo.block}"
tester = create_proc_tester(:x => "x", :y => "x", :z => String)
puts "Test #2: #{tester.xql? &foo.block}"
> Test #1: false
> Test #2: true
.
.
That's all great and wonderful but I want to know if there is a better, more meta, way to do this that actually tests the contents of the proc not just a work around that solves my specific problem; something that could be used to test any proc.
I was thinking there might be a way to use the Ruby parser to get something to compare but I have no idea how. I'm researching it now but thought I'd try to see if anyone here has done this before and knows how. That might be a dead-end though because of the dynamic nature of Ruby but that's where I'm looking now.
If you're using Ruby 1.9, you may be able to use the sourcify gem.
$ irb
> require 'sourcify'
=> true
> a = proc {#x == "x"}
=> #<Proc:0x9ba4240#(irb):2>
> b = proc {#x == %{x}}
=> #<Proc:0x9ba23f0#(irb):3>
> a == b
=> false
> a.to_source == b.to_source
=> true
> RUBY_VERSION
=> "1.9.2"
We also ran into the ParseTree/Ruby 1.9 incompatibility problem at my company.
$ sudo gem install ruby2ruby ParseTree
require 'parse_tree'
require 'ruby2ruby'
require 'parse_tree_extensions'
# All of these are the same:
proc { puts 'a' }.to_ruby # => "proc { puts(\"a\") }"
lambda { puts "a" }.to_ruby # => "proc { puts(\"a\") }"
Proc.new { puts %{a} }.to_ruby # => "proc { puts(\"a\") }"
# If you need to do this with classes:
class Bar; define_method(:foo) { 'a' }; end
puts Ruby2Ruby.new.process(Unifier.new.process(ParseTree.translate(Bar)))
# will print this:
# class Bar < Object
# def foo
# "a"
# end
# end

Resources