Ruby hash with duplicate keys to create URL parameters - ruby

I am using a hash for parameters to be used in generating a URL. I have something like this:
params = {
:center => Geocoder.coordinates(currentlocation).join(","),
:zoom => 10,
:size => "460x280",
:markers => Geocoder.coordinates(markerlocation).join(","),
:sensor => true,
:key => ENV["GOOGLE_API_KEY"]
}
query_string = params.map{|k,v| "#{k}=#{v}"}.join("&")
image_tag "https://maps.googleapis.com/maps/api/staticmap?#{query_string}", :alt => location
However I need to have multiple "markers" parameters in the URL. For each URL I generate I won't know how many "markers" parameters I need. For example if I have an array markerlocations I will need to create a :markers key-value pair for each member of the array for use in the URL. What is the best way to accomplish this?

require 'net/http'
params = {
:center => Geocoder.coordinates(currentlocation).join(","),
:zoom => 10,
:size => "460x280",
:markers => [Geocoder.coordinates(markerlocation).join(",")],
:sensor => true,
:key => ENV["GOOGLE_API_KEY"]
}
query_string = URI.encode_www_form(params)
image_tag ...

Related

Watir: Passing dynamic selector

Is there a way to pass a dynamic set of options to a textarea element in browser?
options = {
:type => 'textarea',
:selector => ':id',
:field => 'id_of_textarea_field',
:value => 'Joe Salesperson'
}
browser.textarea(options[:selector] => options[:field]).set ''
Error received:
invalid attribute: ":id"
A similar thread is listed here (selecting elements using variables in ruby /watir) but no answer.
options = {
:type => 'textarea',
:selector => :id,
:field => 'id_of_textarea_field',
:value => 'Joe Salesperson'
}
You can just pass the identifier, no need for quotes.
2017-01-03, Updating with a use case. Here is the declaration of the method I'm using and the call. The actual code is much more verbose and contains different validations than what I've pasted here. In the example, you'll notice a simple switch block which sets the appropriate information based on the element type passed (specifically text areas, text fields, and select elements).
def validateInput(options = {})
success = true
begin
case options[:type]
when 'textarea', 'text_field'
Watir::Wait.until{$browser.textarea(options[:selector] => options[:field]).present?}
$browser.textarea(options[:selector] => options[:field]).set options[:value]
when 'select'
$browser.select_list(options[:selector] => options[:field]).select_value options[:value]
else
puts "in else"
end
end
rescue => e
$message.push("Failed to validate '#{options[:field]}' field. #{e}")
success = false
end
return success
end
validateInput({
:type => 'textarea',
:selector => :id,
:field => 'order_approval_comment_name',
:value => 'Joe Salesperson'
})

Adapter pattern for Gem that uses multiple composed classes?

I'm using the Axlsx gem (https://github.com/randym/axlsx) for generating excel files. I want to refactor my code so that I have a wrapper around the gem just in case I want to switch gems.
Going through this reminds me of the Adapter design pattern. However, there are just so many objects nested underneath the main Package object that I am getting confused as to how to actual create an adapter for it. Ex:
You create a Package object
You access the Workbook within this Package objet
You access the Sheet from the Workbook object, and vice versa
Here are some of my classes:
class ReportGenerator::Base
...
def create_workbook
...
#package = Axlsx::Package.new <---------------------------
#workbook = #package.workbook <---------------------------
#workbook.use_shared_strings = true
end
class Sheet::Base
def initialize(workbook, question, options = {})
...
#sheet = workbook.add_worksheet(:name => sheet_name) <---------------------------
end
def styles
#styles ||= {
"title" => #sheet.workbook.styles.add_style(:sz => 20, :b => true, :alignment => { :wrap_text => true }),
"bold" => #sheet.workbook.styles.add_style(:b => true),
"header" => #sheet.workbook.styles.add_style(:fg_color => "FFFFFF", :bg_color => "ff3333", :sz => 12, :b => true, :alignment => {:horizontal => :center}, :border => {:style => :thin, :color => "FFFFFF"}),
"subheader" => #sheet.workbook.styles.add_style(:fg_color => "FFFFFF", :bg_color => "ff3333", :sz => 12, :b => true, :alignment => {:horizontal => :center}),
"subheader_border_left" => #sheet.workbook.styles.add_style(:fg_color => "FFFFFF", :bg_color => "ff3333", :sz => 12, :b => true, :alignment => {:horizontal => :center}, :border => {:style => :thin, :color => "FFFFFF", :edges => [:left]}),
"blue_link" => #sheet.workbook.styles.add_style(:fg_color => '0000FF'),
"wrap_text" => #sheet.workbook.styles.add_style(:alignment => { :wrap_text => true, :horizontal => :left }),
"percentage" => #sheet.workbook.styles.add_style(:format_code => "0.00%")
}
end
Here's my first stab:
class ExcelWriter
def initialize
#package = Axlsx::Package.new
end
def workbook
#package.workbook
end
# starting to feel like it's not doable within one class..?
end
There are so many classes involved that it feels like I can't wrap everything into one adapter? Or maybe I'm doing it wrong? Any tips would be welcomed.
Focus on what you're actually using and not on existing infrastracture of Axlsx gem. This way you may combine work of several Axlsx objects into 1 method call.
I don't know what you're actually using, so it's hard for me to say which objects do you need.

How do I correctly call Magento SOAP API with Ruby Savon for Category Product Links?

I am trying to call the catalog_product_link.list API method using Savon. However, I keep receiving the error Error cannot find parameter.
Here is what I am using, though I have tried several variations of the call and still cannot get it to go through correctly:
client = Savon.client(wsdl: 'http://localhost/index.php/api/soap/?wsdl')
response = client.call(:login){message(username: 'user', apiKey: 'key')}
session = response.body[:login_response][:login_return]
#These all do not work
client.call(:call){message(:session => session, :method => 'catalog_product_link.list', :type => 'up_sell', :productId => '166')}
client.call(:call){message(:session => session, :method => 'catalog_product_link.list', :type => 'up_sell', :product => '166')}
client.call(:call){message(:sessionId => session, :resourcePath => 'catalog_product_link.list', :args => {:type => 'up_sell', :product => '166'})}
client.call(:call){message(:sessionId => session, :resourcePath => 'catalog_product_link.list', :args => {:type => 'up_sell', :productId => '166'})}
client.call(:call){message(:sessionId => session, :resourcePath => 'catalog_product_link.list', :arguments => {:type => 'up_sell', :product => '166'})}
Is there a different way to format to get this to work?
UPDATE: If I try removing the type parameter, it gives the error Given invalid link type, so it appears it does not like something about multiple parameters.
response = client.call(:call){message(:session => session, :method => 'catalog_product_link.list', :product => '166')}
I was able to get this to work using Builder:
class ServiceRequest
def initialize(session, type, product)
#session = session
#type = type
#product = product
end
def to_s
builder = Builder::XmlMarkup.new()
builder.instruct!(:xml, encoding: "UTF-8")
builder.tag!(
"env:Envelope",
"xmlns:env" => "http://schemas.xmlsoap.org/soap/envelope/",
"xmlns:ns1" => "urn:Magento",
"xmlns:ns2" => "http://xml.apache.org/xml-soap",
"xmlns:xsd" => "http://www.w3.org/2001/XMLSchema",
"xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance"
) do |envelope|
envelope.tag!("env:Body") do |body|
body.tag!("ns1:call") do |call|
builder.sessionId(#session, "xsi:type" => "xsd:string")
builder.resourcePath("catalog_product_link.list", "xsi:type" => "xsd:string")
builder.args("xsi:type" => "ns2:Map") do |args|
args.item do |item|
item.key("type", "xsi:type" => "xsd:string")
item.value(#type, "xsi:type" => "xsd:string")
end
args.item do |item|
item.key("product", "xsi:type" => "xsd:string")
item.value(#product, "xsi:type" => "xsd:string")
end
end
end
end
end
builder.target!
end
end
client.call(:call, xml: ServiceRequest.new(session, 'up_sell', '166').to_s)
Thanks to #rubiii for the direction.

Ruby - multidimensional hash

How do I make this multidimensional? Each search result entry has multiple attributes :attributes => ['sAMAccountName','givenName','SN','mail']. and there can be many entries in the result. This code is good for creating only one entry with multiple attributes.
def self.Find(attribute, loginID)
conn = Net::LDAP.new :host => SERVER,
:port => PORT,
:base => BASE,
:auth => {
:username => 'admin',
:password => 'admin',
:method => :simple
}
if conn.bind
result = HashWithIndifferentAccess.new
conn.search( :base => LDAPBASE,
:filter => Net::LDAP::Filter.eq( attribute, loginID+"*" ),
:attributes => ['sAMAccountName','givenName','SN','mail'],
:return_result => true
).each do |entries|
entries.each do |attribute, values|
values.each do |value|
result[attribute] = value
end
end
end
result
end
end
The actual response I get from ldap is like this -
puts result.to_s
{
"dn"=>"CN=somename\\, somefirstname,OU=Users,DC=site,DC=com",
"sn"=>"somename",
"givenname"=>"somefirstname",
"samaccountname"=>"someuserid",
"mail"=>"someone#somthing.com"
}
Since the search is has * wildcard to find all matching entries. Ldap will return multiple entries in the format above. similar to this -
{
"dn"=>"CN=somename\\, somefirstname1,OU=Users,DC=site,DC=com",
"sn"=>"somename1",
"givenname"=>"somefirstname1",
"samaccountname"=>"someuserid1",
"mail"=>"someone1#somthing.com"
},
{
"dn"=>"CN=somename\\, somefirstname2,OU=Users,DC=site,DC=com",
"sn"=>"somename2",
"givenname"=>"somefirstname2",
"samaccountname"=>"someuserid2",
"mail"=>"someone2#somthing.com"
},
{
"dn"=>"CN=somename\\, somefirstname3,OU=Users,DC=site,DC=com",
"sn"=>"somename3",
"givenname"=>"somefirstname3",
"samaccountname"=>"someuserid3",
"mail"=>"someone3#somthing.com"
},
A[a=>1,b=>11,c=>111]
B[a=>2,b=>22,c=>222]
C[a=>3,b=>33,c=>333]
D[a=>4,b=>44,c=>444]
I'm not sure that I understood the question.
def self.Find(attribute, loginID)
conn = Net::LDAP.new :host => SERVER,
:port => PORT,
:base => BASE,
:auth => {
:username => 'admin',
:password => 'admin',
:method => :simple
}
if conn.bind
conn.search( :base => LDAPBASE,
:filter => Net::LDAP::Filter.eq( attribute, loginID+"*" ),
:attributes => ['sAMAccountName','givenName','SN','mail'],
:return_result => true
).reduce(Array.new) do |acc, el|
#
# Any modification of the entry must be here
#
acc + [el]
end
end
end
end
I think Array.map is good choice too
My assumption:
conn.search return Array of Hash
the form of result you want:
[
{"uid":"123","displayName":"User 123","mail":"123#example.com"},
{"uid":"456","displayName":"User 456","mail":"456#example.com"},
{"uid":"789","displayName":"User 789","mail":"789#example.com"}
]

How do I fetch multiple hash keys with a nested hash?

I'm playing around with nested hashes and I'm trying to figure out how to fetch multiple keys when my hash is a nested one:
imahash = { :id => { :name => "Alma", :email => "alma#mail.com" },
:stats => { :gender => "Female" },
:location => { :city => "Freeport", :state => "Maine" }
}
I know how to retrieve the nested value, and typing in the hash name will dump all the keys and values. But what I want to do is to fetch specific keys, such as :name and :gender only. Or :name and :city only.
Is this possible? Because from what I've found, it seems that you can only retrieve hash values for one key at a time or for all the keys at once.
My desired output would be something like:
=> { :id => { :name => "Alma" }, :location => { :city => "Freeport" } }
I presume you want to grab the values out in a tuple? You can make an array that contains whatever collection of values you want.
Try the following for name and city:
[imahash[:id][:name], imahash[:location][:city]]
=> ["Alma", "Freeport"]
Not exactly sure what you're asking here, but it seems like you're wanting to create a new hash from the bigger one.
To fetch specific keys like :name and :gender only
name_and_gender_hash = {
:name => imahash[:id][:name],
:gender => imahash[:stats][:gender]
}
would result in
{:name => "Alma", :gender => "female"}

Resources