I'm looking at using Ruby savon for SOAP. For purely masochistic reasons I have to deal with SOAP elements having attributes.
So, no problem, there is an example on the savon docs site which highlights this ability:
{ :person => "Eve", :attributes! => { :person => { :id => 666 } } }.to_soap_xml
"<person id=\"666\">Eve</person>"
My problem is how do I set attributes on child elements, for example, say I add an address child element to person:
{ :person => {:address => ""}, :attributes! => { :person => { :id => 666 } } }.to_soap_xml
Now I want to add an id attribute to the address element:
It's no go if I nest address in the attributes hash:
{ :person => {:address => ""}, :attributes! => { :person => { :id => 666, :address => {:id => 44 }} }}.to_soap_xml
So my question is, how can I get this?
<person id=666><address id=44></address></person>
I ran across the issue of the previous answer no longer working. Eventually I found https://github.com/savonrb/savon/issues/518 which lead me to the correct syntax to add attributes now.
So the previous example would now be done as
{
:person => {
:#id => 666,
:address => {
:#id => 44
}
}
}
Which would generate the following xml
<person id="666">
<address id="44"/>
</person>
You were close - just needed to put the :attributes! key in the same hash that contains the value.
{
:person => {
:address => "",
:attributes! => { :address => { :id => 44 } }
},
:attributes! => { :person => { :id => 666 } }
}.to_soap_xml
# => "<person id=\"666\"><address id=\"44\"></address></person>"
Related
I am trying to upload captions to YouTube using the Data API. However I can't find in the reference or in the forum any example in Ruby. In specific how to send the actual caption file (xml).
body = {
:snippet => {
:videoId => videoId,
:language => "English",
:name => "English"
}
}
captions_insert_response = client.execute(
:api_method => youtube.captions.insert,
:parameters => {
:part => body.keys.join(',')
},
:body_object => body
)
where and how do I add the caption file? I tried doing it like uploading a video, but it didn't seem to work. This line was added after ":body_object"
:media => Google::APIClient::UploadIO.new(captions_file, 'text/xml')
Thanks
I solved the issue changing the language in the snippet to "en".
This is the complete request if someone needs it.
body = {
:snippet => {
:videoId => videoId,
:language => "en",
:name => "English"
}
}
captions_insert_response = client.execute(
:api_method => youtube.captions.insert,
:body_object => body,
:media => Google::APIClient::UploadIO.new(captions_file, 'text/xml'),
:parameters => {
'uploadType' => 'multipart',
:part => body.keys.join(',')
}
)
I am using savon version 2.2 to call some SOAP services. For simple services everything is working OK. However one service has a complex structure like the example below with both repeating groups and attributes on each group:
<sch:RequestControl>
<sch:requestID>6989668868</sch:requestID>
</sch:RequestControl>
<sch:InquiryParam>
<sch:crmParam name="AccountNumber">1234567</sch:crmParam>
<sch:crmParam name="History">1</sch:crmParam>
</sch:InquiryParam>
My current message looks like this:
<RequestControl>
<requestID>6989668868</requestID>
</RequestControl>
<InquiryParam>
<crmParam>1234567</crmParam>
<attributes>
<crmParam>
<name>AccountNumber</name>
</crmParam>
</attributes>
</InquiryParam>
<InquiryParam>
<crmParam>1</crmParam>
<attributes>
<crmParam>
<name>History</name>
</crmParam>
</attributes>
</InquiryParam>
The above is produced using this logic:
message = { :RequestControl =>
{ :requestID => 6989668868 },
:InquiryParam => [
{ :crmParam => { :content! => #account_number } ,
:attributes => { "crmParam" => {"name" => "AccountNumber"} } },
{ :crmParam => { :content! => #history } ,
:attributes => { "crmParam" => {"name" => "History"} } } ]
}
I've tried various combinations using :crmParam => { :content! => #account_number, :attributes => {'name'=>'AccountNumber'} } and similar based on the savon and gyoku documentation but have run onto a brick wall in getting the XML to format like the example. I know I can brute force the message by assigning it to xml but that makes it difficult to see what's going on.
Can anyone suggest a fix to have the attributes inside the crmParam tags?
I'm not sure Savon handles a Ruby array as you'd like, but the following script should give you a better idea what you can do.
require 'savon'
c = Savon.client(endpoint: "http://www.example.com",
namespace: "urn:ns.example.com",
log: true,
log_level: :debug,
pretty_print_xml: true)
r = c.call(:call,
:message => {
:InquiryParam => [
{"crmParam" => 123,
:attributes! => { "crmParam" => { "name" => "AccountNumber" }}},
{"crmParam" => 456,
:attributes! => { "crmParam" => { "name" => "history" }}}
]
}
I can't seem to wrap my head around the AWS Ruby SDK documentation for DynamoDB (or more specifically the concepts of the DynamoDB data model).
Specifically I've been reading: http://docs.aws.amazon.com/AWSRubySDK/latest/frames.html#!AWS/DynamoDB.html
Note: I have read through the Data Model documentation as well and it's still not sinking in; I'm hoping a proper example in Ruby with clear up my confusion
In the following code snippet, I create a table called "my_books" which has a primary_key called "item_id" and it's a Hash key (not a Hash/Range combination)...
dyn = AWS::DynamoDB::Client::V20120810.new
# => #<AWS::DynamoDB::Client::V20120810>
dyn.create_table({
:attribute_definitions => [
{ :attribute_name => "item_id", :attribute_type => "N" }
],
:table_name => "my_books",
:key_schema => [
{ :attribute_name => "item_id", :key_type => "HASH" },
],
:provisioned_throughput => {
:read_capacity_units => 10,
:write_capacity_units => 10
}
})
# => {:table_description=>{:attribute_definitions=>[{:attribute_name=>"item_id", :attribute_type=>"N"}], :table_name=>"my_books", :key_schema=>[{:attribute_name=>"item_id", :key_type=>"HASH"}], :table_status=>"ACTIVE", :creation_date_time=>2014-11-24 16:59:47 +0000, :provisioned_throughput=>{:number_of_decreases_today=>0, :read_capacity_units=>10, :write_capacity_units=>10}, :table_size_bytes=>0, :item_count=>0}}
dyn.list_tables
# => {:table_names=>["my_books"]}
dyn.scan :table_name => "my_books"
# => {:member=>[], :count=>0, :scanned_count=>0}
I then try and populate the table with a new item. My understanding is that I should specify the numerical value for item_id (which is the primary key) and then I could specify other attributes for the new item/record/document I'm adding to the table...
dyn.put_item(
:table_name => "my_books",
:item => {
"item_id" => 1,
"item_title" => "My Book Title",
"item_released" => false
}
)
But that last command returns the following error:
expected hash value for value at key item_id of option item
So although I don't quite understand what the hash will be made of, I try doing that:
dyn.put_item(
:table_name => "my_books",
:item => {
"item_id" => { "N" => 1 },
"item_title" => "My Book Title",
"item_released" => false
}
)
But this now returns the following error...
expected string value for key N of value at key item_id of option item
I've tried different variations, but can't seem to figure out how this works?
EDIT/UPDATE: as suggested by Uri Agassi - I changed the value from 1 to "1". I'm not really sure why this has to be quoted as I've defined the type to be a number and not a string, but OK let's just accept this and move on.
I've finally figured out most of what I needed to understand the data model of DynamoDB and using the Ruby SDK.
Below is my example code, which hopefully will help someone else, and I've got a fully fleshed out example here: https://gist.github.com/Integralist/9f9f2215e001b15ac492#file-3-dynamodb-irb-session-rb
# https://github.com/BBC-News/alephant-harness can automate the below set-up when using Spurious
# API Documentation http://docs.aws.amazon.com/amazondynamodb/latest/APIReference/API_Operations.html
# Ruby SDK API Documentation http://docs.aws.amazon.com/AWSRubySDK/latest/frames.html#!AWS/DynamoDB/Client/V20120810.html
require "aws-sdk"
require "dotenv"
require "spurious/ruby/awssdk/helper"
Spurious::Ruby::Awssdk::Helper.configure
# => <AWS::Core::Configuration>
Dotenv.load(
File.join(
File.dirname(__FILE__), "config", "development", "env.yaml"
)
)
# => {"AWS_REGION"=>"eu-west-1", "AWS_ACCESS_KEY_ID"=>"development_access", "AWS_SECRET_ACCESS_KEY"=>"development_secret", "DYNAMO_LU"=>"development_lookup", "DYNAMO_SQ"=>"development_sequence", "SQS_QUEUE"=>"development_queue", "S3_BUCKET"=>"development_bucket"}
dyn = AWS::DynamoDB::Client.new :api_version => "2012-08-10"
dyn = AWS::DynamoDB::Client::V20120810.new
# => #<AWS::DynamoDB::Client::V20120810>
dyn.create_table({
# This section requires us to define our primary key
# Which will be called "item_id" and it must be a numerical value
:attribute_definitions => [
{ :attribute_name => "item_id", :attribute_type => "N" }
],
:table_name => "my_books",
# The primary key will be a simple Hash key (not a Hash/Range which requires both key types to be provided)
# The attributes defined above must be included in the :key_schema Array
:key_schema => [
{ :attribute_name => "item_id", :key_type => "HASH" }
],
:provisioned_throughput => {
:read_capacity_units => 10,
:write_capacity_units => 10
}
})
# => {:table_description=>{:attribute_definitions=>[{:attribute_name=>"item_id", :attribute_type=>"N"}], :table_name=>"my_books", :key_schema=>[{:attribute_name=>"item_id", :key_type=>"HASH"}], :table_status=>"ACTIVE", :creation_date_time=>2014-11-24 16:59:47 +0000, :provisioned_throughput=>{:number_of_decreases_today=>0, :read_capacity_units=>10, :write_capacity_units=>10}, :table_size_bytes=>0, :item_count=>0}}
dyn.list_tables
# => {:table_names=>["my_books"]}
dyn.scan :table_name => "my_books"
# => {:member=>[], :count=>0, :scanned_count=>0}
dyn.put_item(
:table_name => "my_books",
:item => {
"item_id" => { "N" => "1" }, # oddly this needs to be a String and not a strict Integer?
"item_title" => { "S" => "My Book Title"},
"item_released" => { "B" => "false" }
}
)
# Note: if you use an "item_id" that already exists, then the item will be updated.
# Unless you use the "expected" conditional feature
dyn.put_item(
:table_name => "my_books",
:item => {
"item_id" => { "N" => "1" }, # oddly this needs to be a String and not a strict Integer?
"item_title" => { "S" => "My Book Title"},
"item_released" => { "B" => "false" }
},
# The :expected key specifies the conditions of our "put" operation.
# If "item_id" isn't NULL (i.e. it exists) then our condition has failed.
# This means we only write the value when the key "item_id" hasn't been set.
:expected => {
"item_id" => { :comparison_operator => "NULL" }
}
)
# AWS::DynamoDB::Errors::ConditionalCheckFailedException: The conditional check failed
dyn.scan :table_name => "my_books"
# => {:member=>[{"item_id"=>{:n=>"1"}, "item_title"=>{:s=>"My Book Title"}, "item_released"=>{:b=>"false"}}], :count=>1, :scanned_count=>1}
dyn.query :table_name => "my_books", :consistent_read => true, :key_conditions => {
"item_id" => {
:comparison_operator => "EQ",
:attribute_value_list => [{ "n" => "1" }]
},
"item_title" => {
:comparison_operator => "EQ",
:attribute_value_list => [{ "s" => "My Book Title" }]
}
}
# => {:member=>[{"item_id"=>{:n=>"1"}, "item_title"=>{:s=>"My Book Title"}, "item_released"=>{:b=>"false"}}], :count=>1, :scanned_count=>1}
dyn.query :table_name => "my_books",
:consistent_read => true,
:select => "SPECIFIC_ATTRIBUTES",
:attributes_to_get => ["item_title"],
:key_conditions => {
"item_id" => {
:comparison_operator => "EQ",
:attribute_value_list => [{ "n" => "1" }]
},
"item_title" => {
:comparison_operator => "EQ",
:attribute_value_list => [{ "s" => "My Book Title" }]
}
}
# => {:member=>[{"item_title"=>{:s=>"My Book Title"}}], :count=>1, :scanned_count=>1}
dyn.delete_item(
:table_name => "my_books",
:key => {
"item_id" => { "n" => "1" }
}
)
# => {:member=>[], :count=>0, :scanned_count=>0}
I'll try to keep this simple, my previous wording was maybe a bit too verbose:
Here is the example Omniauth hash: https://github.com/mkdynamic/omniauth-facebook
I can access and save some values from this but not others. The field is writable, so I know its just my syntax (beginner, sorry!)
{
:provider => 'facebook',
:uid => '1234567',
:info => {
:nickname => 'jbloggs',
:email => 'joe#bloggs.com',
:name => 'Joe Bloggs',
:first_name => 'Joe',
:last_name => 'Bloggs',
:image => 'http://graph.facebook.com/1234567/picture?type=square',
:urls => { :Facebook => 'http://www.facebook.com/jbloggs' },
:location => 'Palo Alto, California',
:verified => true
},
:credentials => {
:token => 'ABCDEF...', # OAuth 2.0 access_token, which you may wish to store
:expires_at => 1321747205, # when the access token expires (it always will)
:expires => true # this will always be true
},
:extra => {
:raw_info => {
:id => '1234567',
:name => 'Joe Bloggs',
:first_name => 'Joe',
:last_name => 'Bloggs',
:link => 'http://www.facebook.com/jbloggs',
:username => 'jbloggs',
:location => { :id => '123456789', :name => 'Palo Alto, California' },
:gender => 'male',
:email => 'joe#bloggs.com',
:timezone => -8,
:locale => 'en_US',
:verified => true,
:updated_time => '2011-11-11T06:21:03+0000'
}
}
}
I can do this to get gender and save it.
location:auth.extra.raw_info["gender"]
Obviously though I dont want to save gender to location. I want to get "Palo Alto" and save it. But this doesn't work.
location.auth.extra.raw_info["location"]["name"]
What am I doing wrong? When I try it in console, I'm able to get the value.
try this
location.auth.extra.raw_info.location.name
or this
location.auth.extra.raw_info[:location][:name]
Yeah, what you suggested was what I was trying...and it turns out we were right but FB had changed how that hash was set up so it wasn't working. Lesson learned: subscribe FB's notifications next time :)
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"}