I am beyond confused on where the :find is coming from line 17, as well as :findcity... is that how you call a fucntion within a predefined method call from ruby???
cities = {'CA' => 'San Francisco',
'MI' => 'Detroit',
'FL' => 'Jacksonville'}
cities['NY'] = 'New York'
cities['OR'] = 'Portland'
def find_city(map, state)
if map.include? state
return map[state]
else
return "Not found."
end
end
# ok pay attention!
cities[:find] = method(:find_city)
while true
print "State? (ENTER to quit) "
state = gets.chomp
break if state.empty?
# this line is the most important ever! study!
puts cities[:find].call(cities, state)
end
For starters if you are a beginner in Ruby just don't bother trying to understand it. This is not the usual way of doing things in Ruby.
But here are some explanations:
:find is a Symbol and it could be :search or something else in this example.
You could actually use a different variable to store the method instead of storing inside the cities Hash. Like so:
# Instead of doing this
hash = {} # => {}
hash[:puts_method] = method(:puts)
hash[:puts_method].call("Foo")
# Foo
# You can just
puts_method = method(:puts)
puts_method.call("Foo")
# Foo
The find_city is the method defined in your code. Passing the symbol :find_city to the method method returns you an object representing that method (very meta uh?) of the class Method.
So like in the example above we can have an object representing the method puts with which we can send the method call to call it.
the_puts = method(:puts)
# => #<Method: Object(Kernel)#puts>
the_puts.call("Hey!")
# Hey!
# => nil
# Which is the same as simply doing
puts("Hey!")
# Hey!
# => nil
Related
In ruby I read some SNMP registers. Response is an array of objects.
Is there a nice way to convert each object to the proper type avoiding the case..when in the following code? It looks strange that it must be converted manually as the type is already known:
require 'snmp'
HOST = '127.0.0.1'.freeze
registers = ['sysContact.0', 'sysUpTime.0',
'upsIdentManufacturer.0', 'upsIdentModel.0', 'upsIdentName.0']
params_array = {}
SNMP::Manager.open(host: HOST) do |manager|
manager.load_module('UPS-MIB')
response = manager.get(registers)
response.each_varbind do |vb|
##################################
# change from here...
value = nil
case vb.value.asn1_type
when 'OCTET STRING' # <==========
value = vb.value
when 'INTEGER' # <==========
value = vb.value.to_i
when 'TimeTicks' # <==========
value = vb.value.to_s
else
puts "Type '#{vb.value.asn1_type}' not recognized!"
exit(1)
end
params_array[vb.name.to_s] = value
# ... to here
##################################
# with something like
# params_array[vb.name.to_s] = vb.value._to_its_proper_type_
end
end
pp params_array
Looking at the code in the gem repo, it doesn't look like like there is a method for this. I suppose you could try to monkey patch it, but not sure if it's worth the trouble.
If you don't like the switch syntax, you could just use a hash lookup like this:
require 'snmp'
HOST = '127.0.0.1'.freeze
TYPE_VALUES = {
'OCTET STRING' => :to_s,
'INTEGER' => :to_i,
'TimeTicks' => :to_s
}.freeze
registers = ['sysContact.0', 'sysUpTime.0',
'upsIdentManufacturer.0', 'upsIdentModel.0', 'upsIdentName.0']
params_array = {}
SNMP::Manager.open(host: HOST) do |manager|
manager.load_module('UPS-MIB')
response = manager.get(registers)
response.each_varbind do |vb|
if method = TYPE_VALUES[vb.value.ans1_type]
params_array[vb.name.to_s] = vb.value.send(method)
else
puts "Type '#{vb.value.asn1_type}' not recognized!"
exit(1)
end
end
end
pp params_array
What is the best way to write a function (or something DSLish) that will allow me to write this code in Ruby. How would I construct the function write_pair?
username = "tyndall"
write_pair username
# where write_pair username outputs
username: tyndall
Is it possible to do? Looking for the most simple way to do this.
Sure it is possible!
My solution tests the var by Object#object_id identity: http://codepad.org/V7TXRxmL
It's crippled in the binding passing style ...
Although it works just for local vars yet, it can be easily be made "universal" adding use of the other scope-variable-listing methods like instance_variables etc.
# the function must be defined in such a place
# ... so as to "catch" the binding of the vars ... cheesy
# otherwise we're kinda stuck with the extra param on the caller
#_binding = binding
def write_pair(p, b = #_binding)
eval("
local_variables.each do |v|
if eval(v.to_s + \".object_id\") == " + p.object_id.to_s + "
puts v.to_s + ': ' + \"" + p.to_s + "\"
end
end
" , b)
end
# if the binding is an issue just do here:
# write_pair = lambda { |p| write_pair(p, binding) }
# just some test vars to make sure it works
username1 = "tyndall"
username = "tyndall"
username3 = "tyndall"
# the result:
write_pair(username)
# username: tyndall
If it's possible for you to use a symbol instead of the variable name, you could do something like this:
def wp (s, &b)
puts "#{s} = #{eval(s.to_s, b.binding)}"
end
In use:
irb(main):001:0> def wp (s, &b)
irb(main):002:1> puts "#{s} = #{eval(s.to_s, b.binding)}"
irb(main):003:1> end
=> nil
irb(main):004:0> var = 3
=> 3
irb(main):005:0> wp(:var) {}
var = 3
Note that you must pass the empty block {} to the method or it cannot get the binding to evaluate the symbol.
You can't actually get a variable's name in Ruby. But you could do something like this:
data = {"username" => "tyndall"}
Or even,
username = "tyndall"
data = {"username", "password", "favorite_color"}
data.each { |param|
value = eval(param)
puts "#{param}: #{value}"
}
I made a vim macro for this:
" Inspect the variable on the current line (in Ruby)
autocmd FileType ruby nmap ,i ^"oy$Iputs "<esc>A: #{(<esc>"opA).inspect}"<esc>
Put the variable you'd like to inspect on a line by itself, then type ,i (comma then i) in normal mode. It turns this:
foo
into this:
puts "foo: #{(foo).inspect}"
This is nice because it doesn't have any external dependencies (e.g. you don't have to have a library loaded up to use it).
Building on previous answers relating to symbols & bindings ... if passing in the variable name as a symbol works for you (who doesn't love cutting out extra keystrokes?!), try this:
def wp(var_name_as_sym)
# gets caller binding, which contains caller's execution environment
parent_binding = RubyVM::DebugInspector.open{|i| i.frame_binding(2) }
# now puts the symbol as string + the symbol executed as a variable in the caller's binding
puts %Q~#{var_name_as_sym.to_s} = #{eval("#{var_name_as_sym.to_s}.inspect", parent_binding)}~
end
aa=1
bb='some bb string'
os = OpenStruct.new(z:26, y:25)
Console output:
> wp :aa
aa = 1
=> nil
> wp :bb
bb = "some bb string"
=> nil
> wp :os
os = #<OpenStruct z=26, y=25>
=> nil
Using ruby 2.2.2p95
(Credit to banister for getting binding of calling context)
This is a simple solution:
def write_pair(variable)
puts variable + eval(variable)
end
This is more readable:
def write_pair(variable)
puts 'A' * 100
puts variable + ': ' + eval(variable).inspect
puts 'Z' * 100
end
Invocation:
write_pair "variable"
def write_pair var, binding
puts "#{ var } = #{ eval(var, binding)}"
end
username = "tyndall"
write_pair "username", binding
This seems weird because binding is never defined, but it works. From Ruby: getting variable name:
The binding() method gives a Binding object which remembers the
context at the point the method was called. You then pass a binding
into eval(), and it evaluates the variable in that context.
Be sure to pass a string, not the variable.
# make use of dynamic scoping via methods and instance vars
#_binding = binding
def eval_debug(expr, binding = #_binding)
"#{expr} => #{eval(expr, binding)}"
end
# sample invocation:
x = 10
puts eval_debug "x"
puts eval_debug "x**x"
I am getting an error when executing my test.
Failure/Error: expect(industry_sic_code).to include page.sic_code
TypeError:
no implicit conversion of Array into String
# ./spec/os/bal/company/company_filter_clean_harbors_industries_stub.rb:62:in `block (2 levels) in <top (required)>'
The Method:
def sic_code
subtables = #b.table(:class => 'industry-codes').tables(:class => 'industry-code-table')
subtables.each do |subtable|
if subtable.tbody.h4.text == "US SIC 1987:"
subtable.tr.next_siblings.each do |tr|
codes = tr.cell
puts codes.text.to_s
end
end
end
end
The Test:
it 'Given I search for a random Clean Harbors Industry' do
#Pick a random clean industry from the file
data = CSV.foreach(file_path, headers: true).map{ |row| row.to_h }
random = data.sample
random_industry = random["Class"]
industry_sic_code = random["SIC Code"]
end
it 'Then the result has the expected SIC code' do
page = DetailPage.new(#b)
page.view
expect(industry_sic_code).to include page.sic_code
end
I have tried to implicitly change each variable to a string but it still complain about the array issue.
When I include some puts statments, I get some really wonky responses. The method itself returns the expected result.
When I used the method in the test I end up with the code gibberish below.
here are the sic codes from the method
5511
Here are the codes from the test
#<Watir::Table:0x00007fa3cb23f020>
#<Watir::Table:0x00007fa3cb23ee40>
#<Watir::Table:0x00007fa3cb23ec88>
#<Watir::Table:0x00007fa3cb23ead0>
#<Watir::Table:0x00007fa3cb23e918>
#<Watir::Table:0x00007fa3cb23e738>
#<Watir::Table:0x00007fa3cb23e580>
Your sic_code method returns subtables array, that's why you have this error. It doesn't matter that the method puts something, every method in ruby implicitly returns result of its last line, in your case it is subtables.each do ... end, so you have an array.
You need to explicitly return needed value. Not sure if I correctly understood what are you doing in your code, but try something like this:
def sic_code
subtables = #b.table(:class => 'industry-codes').tables(:class => 'industry-code-table')
result = [] # you need to collect result somewhere to return it later
subtables.each do |subtable|
if subtable.tbody.h4.text == "US SIC 1987:"
subtable.tr.next_siblings.each do |tr|
codes = tr.cell
result << codes.text.to_s
end
end
end
result.join(', ')
end
What is the correct way to view the output of the puts statements below? My apologies for such a simple question.... Im a little rusty on ruby. github repo
require 'active_support'
require 'active_support/core_ext'
require 'indicators'
my_data = Indicators::Data.new(Securities::Stock.new(:symbol => 'AAPL', :start_date => '2012-08-25', :end_date => '2012-08-30').output)
puts my_data.to_s #expected to see Open,High,Low,Close for AAPL
temp=my_data.calc(:type => :sma, :params => 3)
puts temp.to_s #expected to see an RSI value for each data point from the data above
Maybe check out the awesome_print gem.
It provides the .ai method which can be called on anything.
An example:
my_obj = { a: "b" }
my_obj_as_string = my_obj.ai
puts my_obj_as_string
# ... this will print
# {
# :a => "b"
# }
# except the result is colored.
You can shorten all this into a single step with ap(my_obj).
There's also a way to return objects as HTML. It's the my_obj.ai(html: true) option.
Just use .inspect method instead of .to_s if you want to see internal properties of objects.
I was reading Why's (Poignant) guide to Ruby, and came across a method that didn't quite work out as expected. The method is aimed to return a value (from a hash) for a given string, somewhat like an encoder-decoder. Originally, the method was written inside a class String, but I modified it to change the classname. Here's the code:
class NameReplacer
##syllables = [
{"Paij" => "Personal","Gonk" => "Business", "Blon" => "Slave", "Stro" => "Master", "Wert" => "Father", "Onnn" => "Mother"},
{"ree" => "AM", "plo" => "PM"}
]
# method to determine what a certain name of his means
def name_significance
# split string by -
parts = self.split("-")
# make duplicate of syllables
syllables = ##syllables.dup
signif = parts.collect {|name| syllables.shift[name]}
#join array returned by " " forming a string
signif.join(" ")
end
end
To run this code, the book simply uses "Paij-ree".name_significance. But when I tried doing the same thing, I got a NoMethodError - in <top (required)>: undefined method NameReplacer for "Paij-ree":String (NoMethodError).
I got the same error when I tried: print "Paij-ree".NameReplacer.new.name_significance
I assume this worked in the book because the method was written in a class String, which, I guess, would be equal to having this method in Ruby's String class. Due to that, something like "paij-ree".name_significance" would not throw an error, because the "paij-ree" would be a String object, and String class does have the method name_significance.
However, how do I accomplish this with my current code? Apologies if this question seems stupid.
Three approaches with same result:
# monkey-patching a class
class String
def appendFoo
self + "foo"
end
end
"a".appendFoo
# => "afoo"
# using an external class method
class FooAppender
def self.appendFoo(string)
string + "foo"
end
end
FooAppender.appendFoo("a")
# => "afoo"
# using an external instance method
class StuffAppender
def initialize(what)
#what = what
end
def append_to(string)
string + #what
end
end
new StuffAppender("foo").append_to("a")
# => "afoo"
self means the object the method is defined on. You can't use self in the NameReplacer class to refer to a string, it will be the NameReplacer instance (inside an instance method like yours).
As others have mentioned, that code depends on the String class. An alternative for you would be to extend the String class with your class like so:
class NameReplacer < String
##syllables = [
{
"Paij" => "Personal",
"Gonk" => "Business",
"Blon" => "Slave",
"Stro" => "Master",
"Wert" => "Father",
"Onnn" => "Mother"
},
{
"ree" => "AM",
"plo" => "PM"
}
]
# method to determine what a certain name of his means
def name_significance
# split string by -
parts = self.split("-")
# make duplicate of syllables
syllables = ##syllables.dup
signif = parts.collect {|name| syllables.shift[name]}
#join array returned by " " forming a string
signif.join(" ")
end
end
And then use it like this:
p = NameReplacer.new("Paij-ree")
puts p.name_significance