Is it possible to write this excerpt of code without using an assignment?
self.name = self.name.to_s.squeeze(' ').strip
I have tried using bang versions of the methods, but couldn't use very well since they return nil if the operation didn't perform any changes (instead of returning self).
You'd have to tap the whole thing. So it would be something like:
str.tap {|x| x.squeeze!(' ')}.tap(&:strip!)
This is not something I would generally recommend doing. Even if you have a dire need for mutation, the best code clarity comes from using the methods the way they were designed:
str.squeeze!(' ')
str.strip!
If this is inconvenient, consider whether you really need the mutation.
If you really want to avoid assignment you could do this:
self.name = 'This is a test '
[['squeeze!', ' '], 'strip!'].each { |cmd| self.name.send(*cmd) }
self.name
# => "This is a test"
For a one-liner with no (Ruby) attributions and without tap:
a.name && (a.name.squeeze!(' ') || a.name).strip!
e.g.:
$ irb
2.1.1 :001 > class A
2.1.1 :002?> def name=(name)
2.1.1 :003?> puts "setting name=#{name.inspect}"
2.1.1 :004?> #name = name
2.1.1 :005?> end
2.1.1 :006?> attr_reader :name
2.1.1 :007?> end
=> nil
2.1.1 :008 > a = A.new
=> #<A:0x007fdc909d6df8>
2.1.1 :009 > a.name = ' and she was '
setting name=" and she was "
=> " and she was "
2.1.1 :010 > a.name && (a.name.squeeze!(' ') || a.name).strip!
=> "and she was"
2.1.1 :011 > a.name
=> "and she was"
2.1.1 :012 > a.name = 'and she was'
setting name="and she was"
=> "and she was"
2.1.1 :013 > a.name && (a.name.squeeze!(' ') || a.name).strip!
=> nil
2.1.1 :014 > a.name = 'and she was '
setting name="and she was "
=> "and she was "
2.1.1 :015 > a.name && (a.name.squeeze!(' ') || a.name).strip!
=> "and she was"
Related
I seem to be getting something elementary wrong when trying to set and or access these variables in my Quote instance when i call Quote#scrape_quote. I can see that the values are scraped and saved to the scraped_values hash just fine, I just can't access the variables, when I call quote.lives for e.g. I get nil.
What am i getting wrong here?
quote.rb
class Quote < ApplicationRecord
require 'watir'
attr_accessor :lives, :benefit, :payment
def scrape_quote
rows = #browser.trs
quote_rows = rows[1..8]
scraped_values = {}
quote_rows.each do |row|
scraped_values[row.tds[0].text] = row.tds[1].text
end
#lives = scraped_values[0]
#benefit = scraped_values[1]
#payment = scraped_values[2]
puts scraped_values
end
end
scraped_values is a hash not an array. You are trying to access it as if it were an array.
Use whatever is in row.tds[0].text to reference the hash:
h = {a:1,b:2}
h[:a]
=> 1
The general case
If you just want the values of the hash sequentially assigned to member variables, you can use parallel assignment from the hash#values return value like this:
2.4.1 :001 > h = {}
=> {}
2.4.1 :002 > h[:one] = 1
=> 1
2.4.1 :003 > h[:two] = 2
=> 2
2.4.1 :004 > h[:three] = 3
=> 3
2.4.1 :005 > #one, #two, #three = h.values
=> [1, 2, 3]
2.4.1 :006 > #one
=> 1
2.4.1 :007 > #two
=> 2
2.4.1 :008 > #three
=> 3
2.4.1 :009 >
Specific application
The specific code in your case would turn into:
class Quote < ApplicationRecord
require 'watir'
attr_accessor :lives, :benefit, :payment
def scrape_quote
rows = #browser.trs
quote_rows = rows[1..8]
scraped_values = {}
quote_rows.each do |row|
scraped_values[row.tds[0].text] = row.tds[1].text
end
#lives, #benefit, #payment = scraped_values.values
puts scraped_values
end
end
The idiomatic way to build the hash would be to use map instead of each and do not use upfront declarations of local variables.
scraped_values = quote_rows.map do |row|
[row.tds[0].text, row.tds[1].text]
end.to_h
Instead of scraped_values[0] you need something like this: scraped_values[scraped_values.keys[0]], because scraped_values isn't array and 0, 1, 2 is like any other missing key, so hash returns nil.
I thought that (in a while...do loop)
gets.chomp != ''
might match a carriage return from a terminal. It doesn't. What am I not understanding? Thank you.
String#chomp removes carriage returns from the string it is being called on.
If you remove chomp it should give you the expected output. See below:
2.1.2 :001 > def foo
2.1.2 :002?> while true do
2.1.2 :003 > puts gets != ''
2.1.2 :004?> end
2.1.2 :005?> end
=> :foo
2.1.2 :006 > foo
a
true
b
true
c
true
1
true
2
true
# about to press enter
true
true
Hope this helps
Below is the code which I tried in ruby console. Can anyone tell me why the output is different in this two cases for the same input.
2.1.4 :014 > def a_method
2.1.4 :015?> puts "enter"
2.1.4 :016?> a = gets.chomp
2.1.4 :018?> puts a
2.1.4 :019?> puts a.to_i
2.1.4 :020?> end
=> :a_method
2.1.4 :021 > a_method
enter
"12"
"12"
0 (output of a.to_i)
=> nil
2.1.4 :022 > "12".to_i
=> 12
Here I'm just converting a string number into integer by reading from console using gets, which is giving 0 as output. If I do the same by just giving "12".to_i then I'm getting proper output. Why is that?
Inspect the intermediate variable a when entering "12" (with quotes)
a = gets.chomp
# a => "\"12\""
a.to_i # => 0
"\"12\"".to_i # => 0
If you want to enter the actual number, not a string representation of it, do not use the quotes.
This output might help explain the issue:
2.1.1 :001 > def a_method
2.1.1 :002?> puts "enter"
2.1.1 :003?> a = gets.chomp
2.1.1 :004?> puts a.class.name
2.1.1 :005?> puts a
2.1.1 :006?> puts a.to_i
2.1.1 :007?> end
=> :a_method
2.1.1 :008 > a_method
enter
"12"
String
"12"
0
=> nil
2.1.1 :009 > a_method
enter
12
String
12
12
=> nil
gets is short for get string, so if you enter 12 it turns it into "12". As Jiří Pospíšil pointed out, if you enter "12", it turns it into "\"12\"", which to_i is unable to understand.
I have a ruby hash which looks like
{"10.1.1.6"=>"nick", "127.0.0.1"=>"nick1"}
But I can't manage to check if a certain string is already in the Hash. I tried has_value?, getting array of values using values then using include? to check if it contains it, but always returns false, when I know that it exists. For example, I try to add "172.16.10.252"=>"nick" to the hash and I do:
class SomeClass
def initialize(*args)
super(*args)
#nicks = Hash.new
end
def serve(io)
loop do
line = io.readline
ip = io.peeraddr[3]
begin
if /NICK (.*)/ =~ line
nick = $1
if #nicks.has_value?(nick) # it fails here
puts "New nick #{$1}"
#nicks[ip] = nick.gsub("\r", "")
io.puts "Your new nick is #{nick}"
else
message = {:ERROR => "100", :INFO=>"#{nick}"}.to_json
io.puts message
end
end
rescue Exception => e
puts "Exception! #{e}-#{e.backtrace}"
end
end
end
end
On irb it works fine, but on my script it doesn't
1.9.3p125 :001 > h = {"10.1.1.6"=>"nick", "127.0.0.1"=>"nick1"}
=> {"10.1.1.6"=>"nick", "127.0.0.1"=>"nick1"}
1.9.3p125 :002 > h.has_value?('nick')
=> true
1.9.3p125 :003 > if h.has_value?('nick')
1.9.3p125 :004?> puts "yes"
1.9.3p125 :005?> else
1.9.3p125 :006 > puts "no"
1.9.3p125 :007?> end
yes
=> nil
1.9.3p125 :008 >
What I'm doing wrong?
I'm not sure if you're using "$1" the way you intend to.
In your code at this line:
if /NICK (.*)/ =~ line
nick = $1
if #nicks.has_value?(nick) # it fails here
puts "New nick #{$1}"
if line is "NICK says a bunch of things", $1 will be "says a bunch of things". So you're not really looking for the value 'nick' in your hash, but for 'says a bunch of things'.
You should check how your regex is working, I wouldn't say anything is wrong with a hash.
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