How do I add a parameter to a URL? - ruby

I'm trying to open multiple HTML documents. The URL for each site looks like this:
http://www.website.com/info/state=AL
AL is Alabama, but it changes by the state. I can create an array with all the two letter combinations state=('aa'..'zz').to_a, but how can I input this into the parameter were AL is above?
I want it to pull up the HTML document for all two letter combinations, and from there I can use a conditional to weed out the ones I don't want. But how should I go about inserting the two letter combinations?

Ruby's URI class is useful. It's not the most full-featured package for handling URLs out there -- check out Addressable::URI if you need more, but it's good:
require 'uri'
uri = URI.parse('http://www.website.com/info')
{
'Alabama' => 'AL',
'Alaska' => 'AK',
'Arizona' => 'AZ',
'Arkansas' => 'AR',
'California' => 'CA',
}.each_pair do |k, v|
uri.query = URI.encode_www_form( {'state' => v} )
puts uri.to_s
end
Which outputs:
http://www.website.com/info?state=AL
http://www.website.com/info?state=AK
http://www.website.com/info?state=AZ
http://www.website.com/info?state=AR
http://www.website.com/info?state=CA
Or:
%w[AL AK AZ AR CA].each do |s|
uri.query = URI.encode_www_form( {'state' => s} )
puts uri.to_s
end
Which outputs the same thing.

Related

I can't find a way to create a simple multidimensional array or hash in Ruby

You Ruby pros will laugh but I'm having such a hard time with this. I've searched and searched and tried a lot of different things but nothing seems right. I guess I'm just used to dealing with arrays in js and php. Here is what I want to do; consider this pseudo code:
i = 0
foreach (items as item) {
myarray[i]['title'] = item['title']
myarray[i]['desc'] = item['desc']
i++
}
Right, so then I can loop through myarray or access 'title' and 'desc' by the index (i). Simplest thing in the world. I've found a few ways to make it work in Ruby but they've all been really messy or confusing. I want to know the right way to do it, and the cleanest.
Unless you are actually updating my_array (which implies that there is probably a better way to do this), you probably want map instead:
items = [
{'title' => 't1', 'desc' => 'd1', 'other' => 'o1'},
{'title' => 't2', 'desc' => 'd2', 'other' => 'o2'},
{'title' => 't3', 'desc' => 'd3', 'other' => 'o3'},
]
my_array = items.map do |item|
{'title' => item['title'], 'desc' => item['desc'] }
end
items # => [{"title"=>"t1", "desc"=>"d1", "other"=>"o1"}, {"title"=>"t2", "desc"=>"d2", "other"=>"o2"}, {"title"=>"t3", "desc"=>"d3", "other"=>"o3"}]
my_array # => [{"title"=>"t1", "desc"=>"d1"}, {"title"=>"t2", "desc"=>"d2"}, {"title"=>"t3", "desc"=>"d3"}]
I'm not quite sure why you are trying to do this, as it seems like items is already an array with hashes inside it, and in my code below, myarray is exactly the same as items.
Try using each_with_index instead of a foreach loop:
items.each_with_index do |item, index|
myarray[index] = item
end
If you have extra attributes in each item, such as a id or something, then you would want to remove those extra attributes before you add the item to myarray.
titles = ["t1", "t2", "t3"]
descs = ["d1", "d2", "d3"]
h= Hash.new
titles.each.with_index{ |v,i| h[i] = {title: "#{v}" } }
puts h[0][:title] #=> t1
puts h #=> {0=>{:title=>"t1"}, 1=>{:title=>"t2"}...}
descs.each.with_index{ |v,i| h[i] = h[i].merge( {desc: "#{v}" } ) }
puts h[0][:desc] #=> d1
puts h #=> {0=>{:title=>"t1", :desc=>"d1"}, 1=>...

How to do named capture in ruby

I want to name the capture of string that I get from scan. How to do it?
"555-333-7777".scan(/(\d{3})-(\d{3})-(\d{4})/).flatten #=> ["555", "333", "7777"]
Is it possible to turn it into like this
{:area => "555", :city => "333", :local => "7777" }
or
[["555","area"], [...]]
I tried
"555-333-7777".scan(/((?<area>)\d{3})-(\d{3})-(\d{4})/).flatten
but it returns
[]
You should use match with named captures, not scan
m = "555-333-7777".match(/(?<area>\d{3})-(?<city>\d{3})-(?<number>\d{4})/)
m # => #<MatchData "555-333-7777" area:"555" city:"333" number:"7777">
m[:area] # => "555"
m[:city] # => "333"
If you want an actual hash, you can use something like this:
m.names.zip(m.captures).to_h # => {"area"=>"555", "city"=>"333", "number"=>"7777"}
Or this (ruby 2.4 or later)
m.named_captures # => {"area"=>"555", "city"=>"333", "number"=>"7777"}
Something like this?
"555-333-7777" =~ /^(?<area>\d+)\-(?<city>\d+)\-(?<local>\d+)$/
Hash[$~.names.collect{|x| [x.to_sym, $~[x]]}]
=> {:area=>"555", :city=>"333", :local=>"7777"}
Bonus version:
Hash[[:area, :city, :local].zip("555-333-7777".split("-"))]
=> {:area=>"555", :city=>"333", :local=>"7777"}
In case you don't really need the hash, but just local variables:
if /(?<area>\d{3})-(?<city>\d{3})-(?<number>\d{4})/ =~ "555-333-7777"
puts area
puts city
puts number
end
How does it work?
You need to use =~ regex operator.
The regex (sadly) needs to be on the left. It doesn't work if you use string =~ regex.
Otherwise it is the same syntax ?<var> as with named_captures.
It is supported in Ruby 1.9.3!
Official documentation:
When named capture groups are used with a literal regexp on the
left-hand side of an expression and the =~ operator, the captured text
is also assigned to local variables with corresponding names.
A way to turn capture group names and their values into a hash is to use a regex with named captures using (?<capture_name> and then access the %~ global "last match" variable.
regex_with_named_capture_groups = %r'(?<area>\d{3})-(?<city>\d{3})-(?<local>\d{4})'
"555-333-7777"[regex_with_named_capture_groups]
match_hash = $~.names.inject({}){|mem, capture| mem[capture] = $~[capture]; mem}
# => {"area"=>"555", "city"=>"333", "local"=>"7777"}
# If ActiveSupport is available
match_hash.symbolize_keys!
# => {area: "555", city: "333", local: "7777"}
This alternative also works:
regex = /^(?<area>\d+)\-(?<city>\d+)\-(?<local>\d+)$/
m = "555-333-7777".match regex
m.named_captures
=> {"area"=>"555", "city"=>"333", "local"=>"7777"}
There are a LOT of ways to create named captures, many of which have been mentioned already. For the record though, we could have even used the originally posted code along with Multiple Assignment like so:
a, b, c = "555-333-7777".scan(/(\d{3})-(\d{3})-(\d{4})/).flatten
hash = {area: a, city: b, local: c}
#=> {:area=>"555", :city=>"333", :local=>"7777"}
OR
hash = {}
hash[:area], hash[:city], hash[:local] = "555-333-7777".scan(/(\d{3})-(\d{3})-(\d{4})/).flatten
hash
#=> {:area=>"555", :city=>"333", :local=>"7777"}
OR along with zip and optionally to_h:
[:area, :city, :local].zip "555-333-7777".scan(/(\d{3})-(\d{3})-(\d{4})/).flatten
#=> [[:area, "555"], [:city, "333"], [:local, "7777"]]
([:area, :city, :local].zip "555-333-7777".scan(/(\d{3})-(\d{3})-(\d{4})/).flatten).to_h
#=> {:area=>"555", :city=>"333", :local=>"7777"}

Databasedotcom materialize a custom object in Ruby

I would like to return the results of a SOQL query as JSON, but the data seems to be returned as a string.
client = SFDC_Adapter.login
data = client.query("SELECT MarkupAmount__c,
MarkupPercent__c,
Product_Type_Id__c,
Product_Type__c
FROM Product_Type__c
WHERE Product_Type_Id__c = #{product_type_id}")
p data
=> [#<Product_Type__c:0x00000001c356f8 #Id=nil, #OwnerId=nil, #IsDeleted=nil, #Name=nil, #CreatedDate=nil, #CreatedById=nil, #LastModifiedDate=nil, #LastModifiedById=nil, #SystemModstamp=nil, #MarkupPercent__c=5.0, #Subscription__c=nil, #Product_Type__c="Research Trip", #MarkupAmount__c=nil, #Product_Type_Id__c=36.0>]
puts data
=> #<Product_Type__c:0x00000001c356f8>
puts data.to_json
=> ["#<Product_Type__c:0x00000001c356f8>"]
How do I materialize these results into a JSON object for use in a Restful service?
I don't know that gem, but from looking at your output, and glancing at your results, it looks like you got a Product_Type object back.
When you use p or puts, inspect is being used, which is turning the instance into something viewable in a web-page, by using an HTML encoding on it. That's why you see < and > in the output.
Instead, you need to access the values in the object. According to the docs, you can use standard getters or using a hash[key] form to do that:
contact = Contact.find("contact_id") #=> #
contact = Contact.find_by_Name("John Smith") #=> dynamic finders!
contacts = Contact.all #=> a Databasedotcom::Collection of Contact instances
contacts = Contact.find_all_by_Company("IBM") #=> a Databasedotcom::Collection of matching Contacts
contact.Name #=> the contact's Name attribute
contact["Name"] #=> same thing
contact.Name = "new name" #=> change the contact's Name attribute, in memory
contact["Name"] = "new name" #=> same thing
contact.save #=> save the changes to the database
contact.update_attributes "Name" => "newer name",
"Phone" => "4156543210" #=> change several attributes at once and save them
contact.delete #=> delete the contact from the database
Try data['Product_Type_Id'] and you should get 36.0. An alternate way of doing the same thing is data.Product_Type_Id.
Once you have your accessors figured out you can generate JSON using a simple hash or array of hashes. This would generate a hash:
require 'json'
hash = {
'Id' => data.Id,
'OwnerId' => data.OwnerId,
'IsDeleted' => data.IsDeleted,
'Name' => data.Name,
'CreatedDate' => data.CreatedDate,
'CreatedById' => data.CreatedById,
'LastModifiedDate' => data.LastModifiedDate,
'LastModifiedById' => data.LastModifiedById,
'SystemModstamp' => data.SystemModstamp,
'MarkupPercent' => data.MarkupPercent,
'Subscription' => data.Subscription,
'Product_Type' => data.Product_Type,
'MarkupAmount' => data.MarkupAmount,
'Product_Type_Id' => data.Product_Type_Id,
}
puts hash.to_json
I didn't see a to_h or to_hash method which would be a shortcut.

Refactor ruby on rails model

Given the following code,
How would you refactor this so that the method search_word has access to issueid?
I would say that changing the function search_word so it accepts 3 arguments or making issueid an instance variable (#issueid) could be considered as an example of bad practices, but honestly I cannot find any other solution. If there's no solution aside from this, would you mind explaining the reason why there's no other solution?
Please bear in mind that it is a Ruby on Rails model.
def search_type_of_relation_in_text(issueid, type_of_causality)
relation_ocurrences = Array.new
keywords_list = {
:C => ['cause', 'causes'],
:I => ['prevent', 'inhibitors'],
:P => ['type','supersets'],
:E => ['effect', 'effects'],
:R => ['reduce', 'inhibited'],
:S => ['example', 'subsets']
}[type_of_causality.to_sym]
for keyword in keywords_list
relation_ocurrences + search_word(keyword, relation_type)
end
return relation_ocurrences
end
def search_word(keyword, relation_type)
relation_ocurrences = Array.new
#buffer.search('//p[text()*= "'+keyword+'"]/a').each { |relation|
relation_suggestion_url = 'http://en.wikipedia.org'+relation.attributes['href']
relation_suggestion_title = URI.unescape(relation.attributes['href'].gsub("_" , " ").gsub(/[\w\W]*\/wiki\//, ""))
if not #current_suggested[relation_type].include?(relation_suggestion_url)
if #accepted[relation_type].include?(relation_suggestion_url)
relation_ocurrences << {:title => relation_suggestion_title, :wiki_url => relation_suggestion_url, :causality => type_of_causality, :status => "A", :issue_id => issueid}
else
relation_ocurrences << {:title => relation_suggestion_title, :wiki_url => relation_suggestion_url, :causality => type_of_causality, :status => "N", :issue_id => issueid}
end
end
}
end
If you need additional context, pass it through as an additional argument. That's how it's supposed to work.
Setting #-type instance variables to pass context is bad form as you've identified.
There's a number of Ruby conventions you seem to be unaware of:
Instead of Array.new just use [ ], and instead of Hash.new use { }.
Use a case statement or a constant instead of defining a Hash and then retrieving only one of the elements, discarding the remainder.
Avoid using return unless strictly necessary, as the last operation is always returned by default.
Use array.each do |item| instead of for item in array
Use do ... end instead of { ... } for multi-line blocks, where the curly brace version is generally reserved for one-liners. Avoids confusion with hash declarations.
Try and avoid duplicating large chunks of code when the differences are minor. For instance, declare a temporary variable, conditionally manipulate it, then store it instead of defining multiple independent variables.
With that in mind, here's a reworking of it:
KEYWORDS = {
:C => ['cause', 'causes'],
:I => ['prevent', 'inhibitors'],
:P => ['type','supersets'],
:E => ['effect', 'effects'],
:R => ['reduce', 'inhibited'],
:S => ['example', 'subsets']
}
def search_type_of_relation_in_text(issue_id, type_of_causality)
KEYWORDS[type_of_causality.to_sym].collect do |keyword|
search_word(keyword, relation_type, issue_id)
end
end
def search_word(keyword, relation_type, issue_id)
relation_occurrences = [ ]
#buffer.search(%Q{//p[text()*= "#{keyword}'"]/a}).each do |relation|
relation_suggestion_url = "http://en.wikipedia.org#{relation.attributes['href']}"
relation_suggestion_title = URI.unescape(relation.attributes['href'].gsub("_" , " ").gsub(/[\w\W]*\/wiki\//, ""))
if (!#current_suggested[relation_type].include?(relation_suggestion_url))
occurrence = {
:title => relation_suggestion_title,
:wiki_url => relation_suggestion_url,
:causality => type_of_causality,
:issue_id => issue_id
}
occurrence[:status] =
if (#accepted[relation_type].include?(relation_suggestion_url))
'A'
else
'N'
end
relation_ocurrences << occurrence
end
end
relation_occurrences
end

Easy way to access hashes from within arrays with ruby

I'm using ruby to get get some address information from the Google geocode API. I get JSON returned and parse this into a variable which is an array containing hashes.
After I've parsed the json it looks like this
{"status"=>"OK",
"results"=>
[{"address_components"=>
[{"long_name"=>"XXX", "types"=>["street_number"], "short_name"=>"XXX"},
{"long_name"=>"St Georges Terrace",
"types"=>["route"],
"short_name"=>"St Georges Terrace"},
{"long_name"=>"Perth",
"types"=>["locality", "political"],
"short_name"=>"Perth"},
{"long_name"=>"Western Australia",
"types"=>["administrative_area_level_1", "political"],
"short_name"=>"WA"},
{"long_name"=>"Australia",
"types"=>["country", "political"],
"short_name"=>"AU"},
{"long_name"=>"6000", "types"=>["postal_code"], "short_name"=>"6000"}],
"types"=>["street_address"],
"geometry"=>
{"location_type"=>"ROOFTOP",
"viewport"=>
{"northeast"=>{"lng"=>115.86768092068, "lat"=>-31.9540383793198},
"southwest"=>{"lng"=>115.86138567932, "lat"=>-31.9603336206802}},
"location"=>{"lng"=>115.8645333, "lat"=>-31.957186}},
"formatted_address"=>"XXX St Georges Terrace, Perth WA 6000, Australia"}]}
My ruby looks like this
require 'rubygems'
require 'json'
require 'open-uri'
require 'pp'
#url = "http://maps.googleapis.com/maps/api/geocode/json?address=PERTH+XXX+St+Georges+Terrace,+Western+Australia&sensor=false"
uri = URI.parse(#url)
json = uri.open.read
parsed_json = JSON.parse(json)
pp parsed_json
The mixture of hashjes and arrays is confusing me in Ruby. I wish to extract the information into one hash to look something like this
result = { "address_line_one" => "XXX St Georges Terrace", "address_line_two" => "Perth", "state" => "Western Austalia", "postal_code" => "6000" }
Thanks
j = parsed_json
a = j['results'][0]['address_components'].map { |e| e['long_name'] }
p(
{ 'address_line_one' => a[0..1].join(' '),
'address_line_two' => a[2],
'state' => a[3],
'postal_code' => a[5] }
)

Resources