Ruby Sinatra storing variables - ruby

In the code below, the initial get '/' contains a form, whose action is post '/'. when the user inputs a number, it should be converted to a variable that will be used to call the Game class, for which I have generated another action to reveal a new form at get '/game'. the variable generated in the post method is not being stored. how can I both store the variable created in post and then link into the get '/game' action?
require 'sinatra'
require 'sinatra/reloader'
##count = 5
Dict = File.open("enable.txt")
class Game
attr_accessor :letters, :number, :guess, :disp
##count = 5
def initialize (number)
letters = find(number)
end
def find (n)
words =[]
dictionary = File.read(Dict)
dictionary.scan(/\w+/).each {|word| words << word if word.length == n}
letters = words.sample.split("").to_a
letters
end
def counter
if letters.include?guess
correct = check_guess(guess, letters)
else
##count -= 1
end
end
end
get '/' do
erb :index
end
post '/' do
n = params['number'].to_i
#letters = Game.new(n)
redirect '/game'
end
get "/game" do
guess = params['guess']
letters = #letters
if guess != nil
correct = check_guess(guess, letters)
end
disp = display(letters, correct)
erb :game, :locals => {:letters => letters, :disp => disp}
end
def display(letters, correct)
line = "__"
d=[]
letters.each do |x|
if correct == nil
d << line
elsif correct.include?x
d << x
else
d << line
end
end
d.join(" ")
end
def check_guess(guess, letters)
correct = []
if guess != nil
if letters.include?guess
correct << guess
end
end
correct
end

You cannot do this:
#letters = Game.new(n)
each time you create a request, and new Request instance created and so the #letters attribute no longer exists.
It's the equivalent of
r = Request.new()
r.letters = Game.new()
r = Request.new()
r.letters # not defined anymore!!
You could achieve what you want using a class variable instead
##letters = Game.new(n)
Although this will become a nightmare when you have multiple users and will only work when you have a single ruby server process.
A more advanced approach would be to store params['number'] in a session cookie or in a database.

Related

ruby trie implementation reference issue

I am trying to implement a trie in Ruby but can't figure out what the problem is with my print + collect methods.
I just implemented the same in JS and working fine. I guess the issue could be that Ruby is passed by reference (unlike JS) and how variable assignment works in Ruby.
So if I run the code with string.clone as argument when I recursively call the collect function then I get:
["peter", "peter", "petera", "pdanny", "pdjane", "pdjanck"]
and if I pass string then:
["peterradannyjaneck", "peterradannyjaneck", "peterradannyjaneck", "peterradannyjaneck", "peterradannyjaneck", "peterradannyjaneck"]
Any ideas how to fix this?
the code:
class Node
attr_accessor :hash, :end_node, :data
def initialize
#hash = {}
#end_node = false
#data = data
end
def end_node?
end_node
end
end
class Trie
def initialize
#root = Node.new
#words = []
end
def add(input, data, node = #root)
if input.empty?
node.data = data
node.end_node = true
elsif node.hash.keys.include?(input[0])
add(input[1..-1], data, node.hash[input[0]])
else
node.hash[input[0]] = Node.new
add(input[1..-1], data, node.hash[input[0]])
end
end
def print(node = #root)
collect(node, '')
#words
end
private
def collect(node, string)
if node.hash.size > 0
for letter in node.hash.keys
string = string.concat(letter)
collect(node.hash[letter], string.clone)
end
#words << string if node.end_node?
else
string.length > 0 ? #words << string : nil
end
end
end
trie = Trie.new
trie.add('peter', date: '1988-02-26')
trie.add('petra', date: '1977-02-12')
trie.add('danny', date: '1998-04-21')
trie.add('jane', date: '1985-05-08')
trie.add('jack', date: '1994-11-04')
trie.add('pete', date: '1977-12-18')
print trie.print
Ruby's string concat mutates the string and doesn't return a new string. You may want the + operator instead. So basically change the 2 lines inside collect's for-loop as per below:
stringn = string + letter
collect(node.hash[letter], stringn)
Also, you probably want to either always initialize #words to empty in print before calling collect, or make it a local variable in print and pass it to collect.

How to make a program that allows the user to label what type of input is provided?

I have a program in Ruby that takes a phone number and is able to pull information from that number. It can tell me the area code, the root, and prefix of the number.
I now want to add another parameter where the user can input that this particular phone number is their cell, work number, etc.
For example:
MyContact1 = Info.new ('(555) 555-5555', :home)
=>(555) 555-5555 (home)
MyContact1.label
=>"home"
MyContact2. = Info.new (' 555-555-5555, "work")
=>(555) 555-5555 (work)
Here is my code:
class PhoneNumber
def initialize (ph)
#ph = ph
#ph.insert(0, '(')
#ph.insert(4, ')')
#ph.insert(5, ' ')
#ph.insert(9, "-")
end
def to_s
#ph
end
def area_code
#ph.slice(0..5)
end
def prefix
#ph.slice(6..8)
end
def root
#ph.slice(10..13)
end
end
print "Please enter a phone number: "
puts a = PhoneNumber.new(gets.strip)
puts a.area_code
puts a.prefix
puts a.root
Just extend your initialize method to accept more arguments:
class PhoneNumber
attr_reader :label
def initialize(ph, label)
#ph = ph
#label = label
# ...
end
# ...
end
Now you can pass a number and a label, and you have a method defined to retrieve this label:
a = PhoneNumber.new('555-555-5555', :home)
#=> #<PhoneNumber:0x007fd7cb149fb0 #label=:home, #ph="(555) -55-5-5555">
[8] pry(main)> a.label
#=> :home

How can I find acronym in a text?

My project reads many files (these files have title text and sections) and should find the title of the files that contain an acronym. This is my docs class:
class Doc
def initialize(id, secciones)
#id, #secciones = id, secciones
end
def to_s
result = "" + #id.to_s + "\n" + #secciones.to_s
return result
end
def tiene_acronimo(acr)
puts "a ver si tiene acronimos el docu.."
tiene_acronimo = false
secciones.each do |seccion|
if seccion.tiene_acronimo(acr)
tiene_acronimo = true
end
end
return tiene_acronimo
end
attr_accessor :id
attr_accessor :secciones
end
And this my sections class:
class Section
def initialize ()
#title = ""
#text = ""
end
def tiene_acronimo(acr)
return title.include?(acr) || text.include?(acr)
end
end
And this my method:
def test()
results = Array.new
puts "Dame el acronimo"
acr = gets
documentos_cientificos.each do |d|
if d.tiene_acronimo(acr)
results << d
end
end
The method gets an acronym and should find all documents that contain it. The method inclue? [sic] ingores the upcase and returns true if the docs contain any substring like the acronym. For example:
Multiple sclerosis (**MS**), also known as # => `true`
Presenting signs and sympto**ms** # => `false` (but `include?` returns `true`)
How I can find an acronym more easily?
You could use some regex with the match function. The following regex will find a match if the content contains the FULL word provided. It will ignore substrings, and it will be case sensitive.
arc = "MS"
title = "Multiple sclerosis (MS), also known as"
text = "Presenting signs and symptoms"
title.match(/\b#{Regexp.escape(acr)}\b/) # => #<MatchData "MS">
text.match(/\b#{Regexp.escape(acr)}\b/) # => nil
or equivalently
title.match(/\b#{Regexp.escape(acr)}\b/).to_a.size > 0 # => true
text.match(/\b#{Regexp.escape(acr)}\b/).to_a.size > 0 # => false
...so you could redefine your function as:
def tiene_acronimo(acr)
regex_to_match = /\b#{Regexp.escape(acr)}\b/
has_acr = false
if (title.match(regex_to_match)) || (text.match(regex_to_match))
has_acr = true
end
return has_acr
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.

Resources