Why do I lose my instance variables when I invoke Shoes#'visit'? - ruby

I'm sure this has to do with the intricacies mentionned in Shoes > Manual > Rules but I just don't get it. If someone would care to explain why #var == nil in the following code ...
I thought I could use visit to switch between different views in my application but that won't work if I lose all state.
class MyShoe < Shoes
url '/', :index
url '/somewhere', :somewhere
def index
#var = para link( "go somewhere", :click => "/somewhere" )
end
def somewhere
para "var = #{#var.inspect}"
end
end
Shoes.app

_why himself has answered this issue, and I'll get to that in a minute. First, the simplest way to pass data (specifically, strings) between different urls is like this:
class MyShoe < Shoes
url '/', :index
url '/somewhere/(\d+)', :somewhere
def index
#var = para link( "What is 2 + 2?", :click => "/somewhere/4" )
end
def somewhere(value)
para "2 + 2 = #{value}"
end
end
Shoes.app
It will match the subgroups of the regex and pass the matching strings as parameters to the method. Occasionally useful, but it gets unwieldy in a hurry. The other solution is to use constants or class variables, as _why explains here:
OK, so fooling around further it looks like all instance variables get
wiped at the beginning of every method within a Shoes subclass.
That's OK I guess. So what's the preferred way to have some data
that's shared from one Shoes URL to another? Passing it from one page
to the next in the URL itself works -- if it's a string. If it's not
a string, what should you use -- ##class_variables?
Sure you could use a class var. Those
are guaranteed to persist throughout
the life of the app. Or a constant.
Also, Shoes comes with SQLite3, data
can be passed there.
In your example, it would look like this:
class MyShoe < Shoes
url '/', :index
url '/somewhere', :somewhere
def index
##var = para link( "go somewhere", :click => "/somewhere" )
end
def somewhere
para "var = #{##var.inspect}"
end
end
Shoes.app

Related

Ruby Class Output

I am trying my hands at Ruby, below is the code that I am writing in 2 different ways to understand Ruby Classes. In first block I am using accessor method (combination of accessor read & write) and I want to print final line as "lord of the rings is written by Tolkien and has 400 pages". How can I make that happen? I understand that adding string and integer will throw an error. I can get them to print on separate lines, its just that I can't get them in a sentence.
class Book
attr_accessor :title, :author, :pages
end
book1 = Book.new()
book1.title = 'lord of the rings'
book1.author = 'Tolkien'
book1.pages = 400
puts book1.title
puts book1.author
puts book1.pages
#puts book1.title + " is written by " + book1.author + " and has " + book1.pages + " pages" <<<this errors out for known reason>>>
Second piece of code doing the same thing but I am using instance variable and have figured out how to get desired output. However, please advise if there's a better way of doing this?
class Novel
def initialize(title, author, pages)
#title = title
#author = author
#pages = pages
end
def inspect
"#{#title} is written by #{#author} and has #{#pages} pages"
end
end
novel1 = Novel.new('harry potter', 'JK Rowling', 300)
puts novel1.inspect
In your first example you are providing access the info you want and leaving it up to the client to format the output. For example you could have gotten what you wanted by adding this line in place of your commented line.
puts "#{book1.title} is written by #{book1.author} and has #{book1.pages} pages"
In your second example you are "pushing" that code down into the Novel class and proving a method to produce the output you want. BTW, don't use inspect as a method name, inspect is already a defined method
For example the following will print the same info twice.
class Novel
attr_accessor :title, :author, :pages
def initialize(title, author, pages)
#title = title
#author = author
#pages = pages
end
def info
"#{#title} is written by #{#author} and has #{#pages} pages"
end
end
novel = Novel.new('harry potter', 'JK Rowling', 300)
puts novel.info
puts "#{novel.title} is written by #{novel.author} and has #{novel.pages} pages"

Variable URL with Instance Variables

I know I'm being an idiot here, but I can't think of how this is done. I am creating an app with certain interests and am using a a Wikipedia scrape set up using Nokogiri. I have two inputs: Title and Wikipedia, but want to fill Summary and Content in the data model using the scrape. I want to use the Wikipedia attribute as a variable in a url within a method, but keep getting the error dynamic constant assignment PAGE_URL = "http://en.wikipedia.org/w/i....
I thought that the methods should go in the model, with reference to them in the Create definition under the controller, but this doesn't seem to work.
EDIT
I've just tried taking the constants out of the methods as suggested, but I am still getting a dynamic constant assignment error. My model currently looks like this:
PAGE_URL1 = "http://en.wikipedia.org/w/index.php?title="
PAGE_URL2 = "&printable=yes"
def get_PAGE_URL
PAGE_URL = PAGE_URL1 + self.wikipedia + PAGE_URL2
end
def get_page
page = Nokogiri::HTML(open(PAGE_URL))
end
def get_summary
get_PAGE_URL
self.summary = page.css("p")[0].text
end
def get_full_page
get_PAGE_URL
puts page.css('div#content.mw-body div#bodyContent div#mw-content-text.mw-content-ltr p').each do |p|
self.content = puts p.text
end
end
Constants can't go inside of methods, they must be defined inside of the class' direct scope.
Edit:
For example:
class WikiScraper
PAGE_URL = "http://www.wikipedia.org/"
def scrape
page_num = '5'
my_url = PAGE_URL + page_num
end
end

Can you choose watir commands through a variable?

I'm fairly new to programming, and I'm not sure what keywords I should be looking for.
I'm doing something like this right now:
def click(text, type)
b.span(:text=> text).click if type == 'span'
b.button(:name=> text).click if type == 'button'
b.image(:src=>text).click if type == 'image'
b.button(:title=>text).click if type == 'title'
end
I don't like it because it isn't scaling very well. I want to do something like:
def click(text,type)
b.type(:text=> text).click
end
It throws an undefined method error if I try to enter the type without quotes, but it's definitely not a string. How do I tell the script to use watir-webdriver span/button/image/etc?
It's hard to figure out exactly what it is you want to do with this method or why it's even necessary--or why your type parameter would ever be anything other than a string--but here's a way to help you clean up your code that's similar to what orde suggested.
Note that it's unclear what you're implying when you say "it's definitely not a string." If it's not a string, what is it? Where is it coming from that you are sticking it into this method's parameters without knowing what type of Object it is?
So... I'm assuming your type doesn't have to be a String object, so I made it so it takes symbols...
def click(text, type)
types={span: :text, button: :name, image: :src, title: :title }
#b.send(type, {types[type]=>text}).click
end
I'm not sure how you are calling your click method in your scripts, but here is a contrived example that seems to work:
require 'watir-webdriver'
def click_method(element, text)
#b.element(:text => "#{text}").click
end
#b = Watir::Browser.new
#b.goto "http://www.iana.org/domains/reserved"
click_method("link", "Domains")
EDIT:
require 'watir-webdriver'
def method_not_named_click(el, locator, locator_val)
if locator_val.is_a? String
#b.send(el, locator => "#{locator_val}").click
elsif locator_val.is_a? Integer
#b.send(el, locator => locator_val).click
end
end
#b = Watir::Browser.new
#b.goto "http://www.iana.org/domains/reserved"
method_not_named_click(:a, :text, "Domains")
method_not_named_click(:a, :index, 3)

Accessing/Dealing with Variables in Ruby

Let me preface by stating I'm a "new" programmer - an IT guy trying his hand at his first "real" problem after working through various tutorials.
So - here is what I'm trying to do. I'm watching a directory for a .csv file - it will be in this format: 999999_888_filename.csv
I want to return each part of the "_" filename as a variable to pass on to another program/script for some other task. I have come up w/ the following code:
require 'rubygems'
require 'fssm'
class Watcher
def start
monitor = FSSM::Monitor.new(:directories => true)
monitor.path('/data/testing/uploads') do |path|
path.update do |base, relative, ftype|
output(relative)
end
path.create do |base, relative, ftype|
output(relative)
end
path.delete { |base, relative, ftype| puts "DELETED #{relative} (#{ftype})" }
end
monitor.run
end
def output(relative)
puts "#{relative} added"
values = relative.split('_',)
sitenum = values[0]
numrecs = values[1]
filename = values[2]
puts sitenum
end
end
My first "puts" gives me the full filename (it's just there to show me the script is working), and the second puts returns the 'sitenum'. I want to be able to access this "outside" of this output method. I have this file (named watcher.rb) in a libs/ folder and I have a second file in the project root called 'monitor.rb' which contains simply:
require './lib/watcher'
watcher = Watcher.new
watcher.start
And I can't figure out how to access my 'sitenum', 'numrecs' and 'filename' from this file. I'm not sure if it needs to be a variable, instance variable or what. I've played around w/ attr_accessible and other things, and nothing works. I decided to ask here since I've been spinning my wheels for a couple of things, and I'm starting to confuse myself by searching on my own.
Thanks in advance for any help or advice you may have.
At the top of the Watcher class, you're going to want to define three attr_accessor declarations, which give the behavior you want. (attr_reader if you're only reading, attr_writer if you're only writing, attr_accessor if both.)
class Watcher
attr_accessor :sitenum, :numrecs, :filename
...
# later on, use # for class variables
...
#sitenum = 5
...
end
Now you should have no problem with watcher.sitenum etc. Here's an example.
EDIT: Some typos.
In addition to Jordan Scales' answer, these variable should initialized
class Watcher
attr_accessor :sitenum, :numrecs, :filename
def initialize
#sitenum = 'default value'
#numrecs = 'default value'
#filename = 'default value'
end
...
end
Otherwise you'll get uninformative value nil

Update value displayed based on instance variable?

As far as I know, if all I wanted to do was do "puts" in a console, then I would not be having to ask this question. (However, finally I am asking you all at StackOverflow myself, though I've been visiting for years.)
Here is my issue:
I am trying to create a variable which will be "set" to a specific value upon user click
I am then trying to display that value after it is changed
I can set the value, but it does not get displayed
(Of course, this should work if I am not using Shoes.)
Here is the relevant portion of my code:
class Inits
# Declares all global vars used
def init1
...
end
def initattrib
#id = "###"
#name = "name undef"
alert("Alert: default attrib set")
end
def id
#id
#alert("Alert: id call")
end
def name
#name
#alert("Alert: name call")
end
def id=(newid)
#id = newid
#alert("Alert: id set")
end
def name=(newname)
#name = newname
#alert("Alert: name set")
end
end
Then I am trying to call the id and set it as so:
Shoes.app :width => 800, :height => 600, :resizable => false do
currclass = Inits.new
currclass.init1
currclass.initattrib
.
.
.
id = "123"
name = "new name"
# I declare something to click here
click { currclass.id = id, currclass.name = name }
# Then I try to display it as so:
para currclass.id
para currclass.name
# But of course the value is not displayed -- just the default value
end
... As an aside, I am pretty sure I should be using instance variables and not class variables (#x, not ##x).
Is there some way I can "update on change" ("clock rising edge" is a good analogy) or some other way to call this?
Anyhow, thank you in advance for any advice on what I am not doing correctly. Perhaps there is a misunderstanding.
The first thing you learn using Shoes: It is a metaprogramming jungle.
Shoes has hooks to default procedures, and I'm sure it has one for Class creation as well so Shoes can add its own wizardry to that Class being specified.
Which would mean that a Class - defined outside of Shoes.app - might now work the same as if defined inside Shoes.app.
Try moving everything inside Shoes.app block, I had a lot of problems with pieces of code laying outside of Shoes.app (usually scope problems).
If I understood correctly what you want to do you should do it like that:
class Cos
attr_accessor :co
def initialize(cos)
#co=cos
end
end
Shoes.app :width => 800, :height => 600, :resizable => false do
#cosik = Cos.new("ddd")
#ap=para(#cosik.co)
button "click" do
#cosik.co = "oj"
#ap.text=#cosik.co
end
end
K

Resources