How does one populate an array in Ruby? - ruby

Here is the code I'm working with:
class Trader
def initialize(ticker ="GLD")
#ticker = ticker
end
def yahoo_data(days=12)
require 'yahoofinance'
YahooFinance::get_historical_quotes_days( #ticker, days ) do |row|
puts "#{row.join(',')}" # this is where a solution is required
end
end
end
The yahoo_data method gets data from Yahoo Finance and puts the price history on the console. But instead of a simple puts that evaporates into the ether, how would you use the preceding code to populate an array that can be later manipulated as object.
Something along the lines of :
do |row| populate_an_array_method(row.join(',') end

If you don't give a block to get_historical_quotes_days, you'll get an array back. You can then use map on that to get an array of the results of join.
In general since ruby 1.8.7 most iterator methods will return an enumerable when they're called without a block. So if foo.bar {|x| puts x} would print the values 1,2,3 then enum = foo.bar will return an enumerable containing the values 1,2,3. And if you do arr = foo.bar.to_a, you'll get the array [1,2,3].
If have an iterator method, which does not do this (from some library perhaps, which does not adhere to this convention), you can use foo.enum_for(:bar) to get an enumerable which contains all the values yielded by bar.
So hypothetically, if get_historical_quotes_days did not already return an array, you could use YahooFinance.enum_for(:get_historical_quotes_days).map {|row| row.join(",") } to get what you want.

Related

How best to get all the format sequence keys from a string in ruby?

When given a string that is intended to be formatted with a hash of values to write into the string, is there a clean way to get all the keys that string is expecting values for?
I'm putting together text in a situation where there is a lot of room for customization, and several options for dynamic values to insert into the text. Some of the values are more expensive to get than others, so I'd like to be able to prepare my hash to send in to % to only include the values that are needed in the string.
Ideally I'd be able to query the system that performs the formatting on the string, but I'm not seeing any documentation of such an interface. What I'd like is something like:
"Your request for %{item} is at position %<pos>d".formatting_keys
>>> [:item, :pos]
When passing a hash to String#%, it will call the hash's default proc if a key is missing. You could utilize this behavior and make the proc sneakily collect the passed keys:
def format_keys(format_string)
keys = []
format_string % Hash.new { |_, k| keys << k ; 0 }
keys
end
format_keys("Your request for %{item} is at position %<pos>d")
#=> [:item, :pos]
Note that the proc's return value has to be a valid object for the various field types. I'm using 0 here which seems to work fine.
I'd like to be able to prepare my hash to send in to % to only include the values that are needed in the string.
Instead of a Hash, use an object that does the calculation on demand. That will be useful everywhere.
Use string interpolation to call the methods instead of format sequences.
class Whatever
def item
#item ||= calculate_item
end
def pos
#pos ||= calculate_pos
end
private
def calculate_item
# do something expensive
end
def calculate_pos
# do something expensive
end
end
obj = Whatever.new
puts "Your request for #{obj.item} is at position #{obj.pos.to_i}"
Using Ruby's own sequence parsing as per https://stackoverflow.com/a/74728162 is ideal, but you can also do your own:
class String
def format_keys
scan(
/
(?<!%) # don't match escaped sequence starts, e.g. "%%{"
(?:
(?<=%\{) [^\}]+ (?=\}) # contents of %{...}
| # OR
(?<=%\<) [^\>]+ (?=\>) # contents of %<...>
)
/x
)
end
end

How to override Enumerables sort method

I'm trying to create a method that uses the functionality of Enumerables's sort method.
Imagine I have this data
data = [{project: 'proj', version: '1.1'}, {project: 'proj2', version: '1.11'}, {project: 'proj3', version: '1.2'}]
I want to be able to call the method like this:
data.natural_sort{|a,b| b[:version] <=> a[:version] }
The actual call that happens would achieve something like this:
data.sort{|a,b| MyModule.naturalize_str(b[:version]) <=> MyModule.naturalize_str(a[:version]) }
Heres my current broken code:
Enumerable.module_eval do
def natural_sort(&block)
if !block_given?
block = Proc.new{|a,b| Rearmed.naturalize_str(a[:version]) <=> Rearmed.naturalize_str(b[:version])}
end
sort do |a,b|
a = Rearmed.naturalize_str(a)
b = Rearmed.naturalize_str(b)
block.call(a,b)
end
end
end
It throws an error because a and b are the hashes instead of the versions I wanted.
You're working at odds with yourself here. In your natural_sort block you're expecting hash objects, yet within the implementation you've explicitly cast a and b to be strings.
In Ruby there's two ways to sort, the sort method with a,b pairs, and the sort_by method which uses an intermediate sort form to do the comparisons. The sort_by approach is usually significantly faster since it applies the transform to each object once, while the sort method does it each time a comparison is done.
Here's a rewrite:
def natural_sort_by(&block)
if (block_given?)
sort_by do |o|
Rearmed.naturalize_str(yield(o))
end
else
sort_by do |o|
Rearmed.naturalize_str(o)
end
end
end
Then you can call it this way:
data.natural_sort_by { |o| o[:version] }

Iterating over all elements of a page-object

I have a search filter. I need to be able to check that all fields on the page are there and contain values (if they are select boxes).
I've used the MethodFinder gem to successfully do this, but I was wondering if there is any way with just the PageObject gem.
require 'methodfinder'
class BasicSearchFilter
include PageObject
text_field(:facility_name, :id => "facility-name")
text_field(:queue, :id => "queue")
select_list(:from, :id => "from")
button(:continue, :id => "continue")
def get_search_filter_elements
methods = MethodFinder.find_in_class_or_module('BasicSearchFilter', '.*_element$')
elements = []
methods.each do |method|
elements << send(method)
end
end
end
I've successfully used the above, but now I'm unable to use the page object methods which I would like to do. I would like to be able to somehow hand a list of valid "elements" which is just the PageObject version of the elements.
Edit: So it turns out that something extremely fishy is going on.
I have a RSpec test grabbing the fields from the class above. It looks like this:
it "the basic filter dropdowns should not contain duplicate values"
on_page(BasicSearchFilter).get_search_filter_elements.each do |element|
if element.tag_name == "select"
p element
puts "a select tag #{element}"
end
end
end
Now according to documentation the your_element_element command should return the watir element. Which is happening once. The second puts is somehow changing back to a PageObject object. I now have literally no clue what is happening. Here is some of the output from the above.
#<Watir::Select:0x4c6bdfa4 located=true selector={:id=>"facility-name", :tag_name=>"select"}>
a select tag #<PageObject::Elements::SelectList:0x3101538>
Getting All Elements
While the page-object-gem does not have a method to get all elements, you could do it using plain Ruby. The following would give you the same results as your current method (assuming MethodFinder works as expected).
def get_search_filter_elements
element_methods = self.class.instance_methods(false).find_all{ |m| m =~ /_element$/ }
elements = element_methods.collect{ |m| self.send(m) }
end
Iterating Over Elements
Iterating over the elements returned by get_search_filter_elements is done as you did. However, there are a couple of things regarding your observations.
The your_element_element method returns the PageObject element (not the Watir element). This can be seen by outputting the class of the elements returned:
page.get_search_filter_elements.each{ |e| puts e.class }
#=> PageObject::Elements::TextField
#=> PageObject::Elements::TextField
#=> PageObject::Elements::SelectList
#=> PageObject::Elements::Button
The output of having the Watir element and then the PageObject element is not because the element has changed types. It is always the PageObject element. The output changes because you do p vs puts. When you p an object the .inspect method is outputted, where as when you puts an object the .to_s method is outputted. Given the way that the PageObject elements are written, .to_s gives information about the PageObject element, while the .inspect gives information about the native element (ie Watir or Selenium).
page.queue_element.to_s
#=> <PageObject::Elements::TextField:0x3851ca0>
page.queue_element.inspect
#=> #<Watir::TextField:0x1d58cb4 located=false selector={:id=>\"queue\", :tag_name=>\"input or textarea\", :type=>\"(any text type)\"}>
Converting to Native Element
If you actually want to iterate over the Watir elements (ie you want to call a method not supported by the PageObject), you use the element method. For example, the following iterates over the elements as Watir elements:
page.get_search_filter_elements.each{ |e| puts e.element.class }
#=> Watir::TextField
#=> Watir::TextField
#=> Watir::Select
#=> Watir::Button

Ruby call constructor with random parameters count

I have some classes like
class Demo1 < Struct.new(:text, :text2)
end
class Demo2 < Struct.new(:text, :text2, :text3)
end
How can I call constructor of each class if I only have name and hash of parameters
I need to write method like this,
but this is wrong becasue after send(:new,args) Struct will contain :text which equal to args
def call_demo_object(demo_name, args={})
demo_name.to_s.constantize.send(:new,args)
end
The mian problem is calling constructor with random parameters from hash
variant one:
def call_demo_object(demo_name, args={})
z = [':new']
args.keys.each do |key|
z.push "args[:"+key.to_s+"]"
end
eval('demo_name.to_s.constantize.send(' + z.join(', ') +')' )
end
variant two:
def call_demo_object(demo_name, args={})
a = demo_name.to_s.constantize.send(:new)
args.each do |key, value|
a[key] = value if a.members.include?(key)
end
a
end
One possible variant:
def call_demo_object(demo_name, args={})
obj = demo_name.new
obj.members.each do |member|
obj[member] = args[member]
end
obj
end
It's pros:
args can be in any order
only availible structure members will be assigned
I see a couple of things wrong:
Not sure if your classes really look like that, but you'll need end at the end of them, otherwise you'll get syntax errors.
Also, constantize is not a method on strings in Ruby, it's something Rails defines. So you'll need to use
Kernel.const_get(demo_name.to_s)
to get the same functionality.
As pointed out in the comments I neglected to mention how to expand the parameters.
To do that you'll need to use what's called the "splat operator"
Kernel.const_get(demo_name.to_s).send(:new,*args) #notice the * in front of args
That will expand args out.
However, when args is a hash, say {:text=>"hello", :text2=>"hello2"}, it will expand it out to an array with 2 elements where each element is an array with they key in the first position and key in the second position.
Instead, if you pass an array in as args with the objects in order, you will get what you're looking for.
I think if you're going for what amounts to named parameters, you might have to try another route, but I don't know that for sure.
To go with optional or named parameters, you might look at how Rails does it: use a hash for the parameter, then pass in a hash with the keys. You can then keep a valid list of keys and check the passed-in hash and either reject them or raise an error.

Can I reject objects which do not meet my criteria as they are entered into an array?

I know there are a number of ways to create new elements in an existing ruby array.
e.g.
myArray = []
myArray + other_array
myArray << obj
myArray[index] = obj
I'm also pretty sure I could use .collect, .map, .concat, .fill, .replace, .insert, .join, .pack and .push as well to add to or otherwise modify the contents of myArray.
However, I want to ensure that myArray only ever includes valid HTTP/HTTPS URLs.
Can anyone explain how I can enforce that kind of behaviour?
I would create a module that allows you to specify an acceptance block for an array, and then override all the methods you mention (and more, like concat) to pre-filter the argument before calling super. For example:
module LimitedAcceptance
def only_allow(&block)
#only_allow = block
end
def <<( other )
super if #only_allow[ other ]
end
def +( other_array )
super( other_array.select(&#only_allow) )
end
end
require 'uri'
my_array = []
my_array.extend LimitedAcceptance
my_array.only_allow do |item|
uri = item.is_a?(String) && URI.parse(item) rescue nil
uri.class <= URI::HTTP
end
my_array << "http://phrogz.net/"
my_array << "ftp://no.way"
my_array += %w[ ssh://bar http://ruby-lang.org http:// ]
puts my_array
#=> http://phrogz.net/
#=> http://ruby-lang.org
Create a class to encapsulate behavior you want. Then you can create your << method doing the verifications you want.
Put all logic that handle this data in methods in this domain class. Probably you will discover code floating around the use of this data to move to the new class.
My 2 cents.
Use this to insert. (untested).
def insert_to_array(first_array, second_array)
second_array.each do |i| {
if URI.parse(i).class == URI::HTTP
first_array.insert(i)
end
}
first_array
end

Resources