Related
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.
I'm trying to retrieve data in a nested form from the following two tables (in SQLite)
DB = Sequel.sqlite('database.sqlite')
DB.create_table? :artists do
primary_key :id
String :name
end
DB.create_table? :albums do
primary_key :id
String :title
foreign_key :artist_id,
:artists,
:key => :id
end
artists = DB[:artists]
albums = DB[:albums]
id1 = artists.insert(:name => 'Mike')
id2 = artists.insert(:name => 'John')
albums.insert(:title => 'Only You', :artist_id => id1 )
albums.insert(:title => 'Only Us', :artist_id => id1 )
albums.insert(:title => 'Only Me', :artist_id => id2 )
The output I'm trying to get -
[
{
:id => 1,
:name => 'Mike'
:albums => [
{
:id => 1,
:title => 'Only You'
},
{
:id => 2,
:title => 'Only Us'
}
]
},
{
:id => 2,
:name => 'John'
:albums => [
{
:id => 3,
:title => 'Only Me'
}
]
}
]
I've tried 'eager' loading -
class Artist < Sequel::Model(:artists)
one_to_many :db[:albums], :key => :artist_id
end
class Album < Sequel::Model(:albums)
many_to_one :artist, :key => :artist_id
end
Artist.eager(:albums).all{ |a| p a }
But that didn't work.
Can anyone point me in the right direction?
Artist.eager(:albums).all does eagerly load the albums, but {|a| p a} is not going to show the albums (as Sequel::Model#inspect only shows values for the current model, not any associated objects). Use {|a| p [a, a.albums]} to see that the albums are already loaded.
If you want to produce the hash you described:
Artist.eager(:albums).all.map do |a|
a.values.merge(:albums=>a.albums.map{|al| al.values})
end
You can add a method to Artist to output it the way you want it
class Artist < Sequel::Model(:artists)
one_to_many :albums, :key => :artist_id
def my_hash
to_hash.merge!(
{
:albums => albums.map{|a|
a.to_hash.reject!{ |k,v|
k==:artist_id
}
}
}
)
end
end
class Album < Sequel::Model(:albums)
many_to_one :artist, :key => :artist_id
end
records = Artist.all.map{ |a| a.my_hash }
p records
Instead of using reject! it would be cleaner to add a my_hash method the Album to return a hash without the :artist_id, but you get the idea. This outputs:
[
{
:albums=>[
{
:title=>"Only You",
:id=>1
},
{
:title=>"Only Us",
:id=>2
}
],
:name=>"Mike",
:id=>1
},
{
:albums=>[
{
:title=>"Only Me",
:id=>3
}
],
:name=>"John",
:id=>2
}
]
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"}
]
Normally I have:
cookies[:location] = { :value => { :city => 'foo', :country => 'bar' } }
However, sometimes :country is not set. When I do:
cookies[:location][:country].present?
to check if country has been set, it returns an error:
[:country] is not a symbol
How would I check to see if a country is set in cookie[:location] if I cannot do it this way?
You cookies hash is nested differently than you are expecting: :value is nested under :location, :city and :country are nested under :value. Here is some irb output to get you started:
[~]$ irb
>> cookies={}
=> {}
>> cookies[:location] = { :value => { :city => 'foo', :country => 'bar' } }
=> {:value=>{:city=>"foo", :country=>"bar"}}
>> cookies[:location][:value][:country]
=> "bar"
>> cookies.to_s
=> "{:location=>{:value=>{:city=>\"foo\", :country=>\"bar\"}}}"
If you nest the hash like below, you will get your expected behavior:
>> cookies[:location] = { :city => 'foo', :country => 'bar' }
=> {:city=>"foo", :country=>"bar"}
>> cookies[:location][:country]
=> "bar"
PHP
<?php
$dynamicProperties = array("name" => "bob", "phone" => "555-1212");
$myObject = new stdClass();
foreach($dynamicProperties as $key => $value) {
$myObject->$key = $value;
}
echo $myObject->name . "<br />" . $myObject->phone;
?>
How do I do this in ruby?
If you want to make a "dynamic" formal class, use Struct:
>> Person = Struct.new(:name, :phone)
=> Person
>> bob = Person.new("bob", "555-1212")
=> #<struct Person name="bob", phone="555-1212">
>> bob.name
=> "bob"
>> bob.phone
=> "555-1212"
To make an object completely on-the-fly from a hash, use OpenStruct:
>> require 'ostruct'
=> true
>> bob = OpenStruct.new({ :name => "bob", :phone => "555-1212" })
=> #<OpenStruct phone="555-1212", name="bob">
>> bob.name
=> "bob"
>> bob.phone
=> "555-1212"
Use OpenStruct:
require 'ostruct'
data = { :name => "bob", :phone => "555-1212" }
my_object = OpenStruct.new(data)
my_object.name #=> "bob"
my_object.phone #=> "555-1212"
One of many ways to do this is to use class_eval, define_method and so on to construct a class dynamically:
dynamic_properties = {
'name' => 'bob',
'phone' => '555-1212'
}
class_instance = Object.const_set('MyClass', Class.new)
class_instance.class_eval do
define_method(:initialize) do
dynamic_properties.each do |key, value|
instance_variable_set("##{key}", value);
end
end
dynamic_properties.each do |key, value|
attr_accessor key
end
end
You can then consume this class later on as follows:
>> my_object = MyClass.new
>> puts my_object.name
=> 'bob'
>> puts my_object.phone
=> '555-1212'
But it wouldn't be Ruby if there was only one way to do it!