I want to create XML like this:
<categories>
<category id=1>Sound</category>
<category id=2 parentId=1>Speakers</category>
</categories>
I used:
require 'nokogiri'
#builder = Nokogiri::XML::Builder.new do |xml|
xml.root {
xml.categories{
Category.all.each do |c|
xml.category# here i should insert my needs
end
}
}
end
I used an example from "docs about Tag Attribute Short Cuts" but it gave me class and id instead what I want.
How do I do it properly?
This code will add the id attribute to the category tag:
require 'nokogiri'
#builder = Nokogiri::XML::Builder.new do |xml|
xml.root {
xml.categories{
Category.all.each do |c|
xml.category(c.name, "id" => c.id)
end
}
}
end
Output should be like:
<categories>
<category id=1>Sound</category>
<category id=2>Speakers</category>
</categories
Related
Using gem Nokogiri I'm trying to generate XML like:
<?xml version='1.0'?>
<env:Envelope xmln:env = "http://abc.ca">
<env:Header>
<mm7:TransactionID xmlns:mm7="http://def.ca"> Some Text Here </mm7:TransactionID>
</env:Header>
</env:Envelope>
The code I have is:
env_ns = {
"xmlns:env" => "http://abc.ca"
}
mm7_ns = {
"xmlns:mm7" => "http://def.ca"
}
env_header = Nokogiri::XML::Builder.new do |xml|
xml['mm7'].TransactionID(mm7_ns) do
"Some Text Here"
end
end
builder = Nokogiri::XML::Builder.new { |xml|
xml['env'].Envelope(env_ns) do
xml.Header do
env_header
end
end
}
puts env_header.to_xml
puts "----------------------"
puts builder.to_xml
However, the output is not as desired because the value "Some Text Here" did not go inside the mm7:TranactionID tag. The mm7 tag did not go inside the header tag. Also, the header tag did not go inside the envelope tag.
<?xml version="1.0"?>
<mm7:TransactionID xmlns:mm7="http://def.ca"/>
-----------------------------------------------------------
<?xml version="1.0"?>
<env:Envelope xmlns:env="http://abc.ca">
<env:Header/>
</env:Envelope>
Thanks.
You only need 1 builder:
env_ns = {
"xmlns:env" => "http://abc.ca"
}
mm7_ns = {
"xmlns:mm7" => "http://def.ca"
}
builder = Nokogiri::XML::Builder.new do |xml|
xml['env'].Envelope(env_ns) do
xml.Header do
xml['mm7'].TransactionID(mm7_ns, "Some Text Here")
end
end
end
puts builder.to_xml
# will render the following:
# <?xml version="1.0"?>
# <env:Envelope xmlns:env="http://abc.ca">
# <env:Header>
# <mm7:TransactionID xmlns:mm7="http://def.ca">Some Text Here</mm7:TransactionID>
# </env:Header>
# </env:Envelope>
I have to tag a separation by -
Example:
require 'nokogiri'
teste = Nokogiri::XML::DocumentFragment.parse("")
Nokogiri::XML::Builder.with(teste) do |x|
x.root('xmlns:ns3' => 'Example namespace') do
x['ns3'].example "Example Test"
end
end
puts teste.to_xml
Output
<exemplo>teste xml</exemplo>
Required output
<ns3:exemplo-teste>teste</ns3:exemplo-teste>
Try this:
Nokogiri::XML::Builder.with(teste) do |x|
x.root('xmlns:ns3' => 'Example namespace') do
x['ns3'].send('example-test', 'Example Test')
end
end
Output will be:
</root><root xmlns:ns3="Example namespace">
<ns3:example-test>Example Test</ns3:example-test>
</root>
I have created a FormBuilder extension:
form.div_field_with_label(:email)
Which outputs:
<div class="field email">
<label for="user_email">Email</label>
<input class="input" id="user_email" name="user[email]" type="email" value="">
</div>
How do i create or mock the template object in my rspec test?
app/helper/form_helper.rb
module FormHelper
class GroundedFormBuilder < ActionView::Helpers::FormBuilder
def div_field_with_label(key, &block)
if block_given?
content = self.label(key)
content << block.call(key)
end
classes = ['field']
classes << key.to_s
if #object.errors[key].size != 0
classes << 'warning'
msg = #object.errors.full_message(key, #object.errors[key].join(', '))
content << #template.content_tag(:p, msg, :class => "error-message warning alert" )
end
#template.content_tag(:div, content, :class => classes.join(' ').html_safe)
end
end
end
spec/helper/form_helper_spec.rb
require 'spec_helper'
describe FormHelper do
describe FormHelper::GroundedFormBuilder do
describe 'div_field_with_label' do
# how do i create or mock template?
# template = ?
let(:resource) { FactoryGirl.create :user }
let(:helper) { FormHelper::GroundedFormBuilder.new(:user, resource, template, {})}
let(:output) { helper.div_field_with_label :email }
it 'wraps input and label' do
expect(output).to match /<div class="field">/
end
it 'creates a label' do
expect(output).to match /<label for="user[email]">Email/
end
end
end
end
Update for rails 3 or higher.
This how it should looks like now:
require 'spec_helper'
class TestHelper < ActionView::Base; end
describe LabeledFormBuilder do
describe '#upload_tag' do
let(:helper) { TestHelper.new }
let(:resource) { FactoryGirl.build :user }
let(:builder) { LabeledFormBuilder.new :user, resource, helper, {}, nil }
let(:output) do
builder.upload_tag :avatar, title: "Test upload"
end
before { expect(helper).to receive(:render) { "render partial "} }
it { expect(output).to include "Upload file via new solution" }
end
end
I haven't tried this, but looking at how your GroundedFormBuilder uses #template, maybe all you need is an object that extends ActionView::Helpers::TagHelper:
# ...
template = Object.new.extend ActionView::Helpers::TagHelper
# ...
Any of the TagHelper methods such as content_tag should generate the same content as they would when included in a real template.
actually it was as easy as using self since FormBuilder has TagHelper somewhere in the inheritance chain.
# spec/helper/form_helper_spec.rb
require 'spec_helper'
describe FormHelper do
describe FormHelper::GroundedFormBuilder do
describe 'div_field_with_label' do
# how do i create or mock template?
# template = ?
let(:resource) { FactoryGirl.create :user }
let(:helper) { FormHelper::GroundedFormBuilder.new(:user, resource, self, {})}
let(:output) {
helper.div_field_with_label :email do
helper.email_field(:email, :class => 'input')
end
}
it 'wraps input and label' do
expect(output).to include '<div class="field email">'
end
it 'creates a label' do
expect(output).to include '<label'
end
end
end
end
I need to read an XML file and generate a SOAP request body using Nokogiri in Ruby on Rails.
The request body that I need to generate is:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:c2="http://c2_0.customer.webservices.csx.dtv.com/">
<soapenv:Header>
<soapenv:Body>
<c2:getCustomer>
<customerId>10</customerId>
<UserId>adminUser</UserId>
</c2:getCustomer>
</soapenv:Body>
</soapenv:Header>
</soapenv:Envelope>
I am using this code:
require 'nokogiri'
doc = Nokogiri::XML(File.open('p_l_s.xml'))
wsID =doc.xpath('//transaction:WsID' , 'transaction' => 'http://www.nrf-arts.org/IXRetail/namespace/').inner_text
builder = Nokogiri::XML::Builder.new do |xml|
xml.Envelope("xmlns:soapenv" => "http://schemas.xmlsoap.org/soap/envelope/",
"xmlns:c2" => "http://c2_0.customer.webservices.csx.dtv.com/") do
xml.parent.namespace = xml.parent.namespace_definitions.first
xml['soapenv'].Header {
xml.Body {
xml['c2'].getCustomer{
#xml.remove_namespaces!
xml.customerId wsID
xml.UserId "adminUser"
}
}
}
end
end
puts builder.to_xml
And, when executing it from the terminal in Ubuntu, I get:
<?xml version="1.0"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:c2="http://c2_0.customer.webservices.csx.dtv.com/">
<soapenv:Header>
<soapenv:Body>
<c2:getCustomer>
<c2:customerId>10</c2:customerId>
<c2:UserId>adminUser</c2:UserId>
</c2:getCustomer>
</soapenv:Body>
</soapenv:Header>
</soapenv:Envelope>
I get the c2 namespace for the XML elements customerId and UserId which is not required for the method I'm invoking in the WSDL file I will be calling.
I was able to generate the output you needed with the following code:
builder = Nokogiri::XML::Builder.new do |xml|
xml.Envelope("xmlns:soapenv" => "http://schemas.xmlsoap.org/soap/envelope/",
"xmlns:c2" => "http://c2_0.customer.webservices.csx.dtv.com/") do
xml.parent.namespace = xml.parent.namespace_definitions.first
xml.Header {
xml.Body {
xml.getCustomer {
xml.customerId {
xml.parent.content=(wsID)
xml.parent.namespace = xml.parent.namespace_definitions.first
}
xml.UserId{
xml.parent.content=("adminUser")
xml.parent.namespace = xml.parent.namespace_definitions.first
}
xml.parent.namespace = xml.parent.namespace_scopes[1]
}
}
}
end
end
puts builder.to_xml
You don't have to explicitly set namespaces until you get to 'getCustomer'. With a combination of the 'content' and 'namespace_definition' methods, you can then specify the namespace and content of the child nodes - which should output what you were looking for.
The Nogokiri pages for the Builder: http://nokogiri.org/Nokogiri/HTML/Builder.html and Node: http://nokogiri.org/Nokogiri/XML/Node.html are super useful and will hopefully help shed more light on this for you.
I think this code in Mel T's answer:
xml.customerId {
xml.parent.content=(wsID)
xml.parent.namespace = xml.parent.namespace_definitions.first
}
Would output:
<soapenv:customerId>10</soapenv:customerId>
I did this instead:
xml.customerId {
xml.parent.content=(wsID)
xml.parent.namespace = nil
}
I have an element in my XML but I am not sure how to get it generated in Nokogiri::XML::Builder.
<ns0:SearchCondition expressionLanguage='String'expressionType='PartyNumber'>31955854</ns0:SearchCondition>
I tried this:
def test_xml
builder = Nokogiri::XML::Builder.new do |xml|
xml.root {
xml.products {
xml.widget {
xml.id_ "10"
xml.name "Awesome widget"
xml.SearchCondition('expressionLanguage' => 'String', 'expressionType' => 'PartyNumber')
}
}
}
end
puts builder.to_xml
This produces the following
<?xml version="1.0"?>
<root>
<products>
<widget>
<id>10</id>
<name>Awesome widget</name>
<SearchCondition expressionLanguage="String" expressionType="PartyNumber"/>
</widget>
</products>
</root>
But I am not sure how I pass the value to the PartyNumber.
Not sure if this is what you are asking, but you can use the builder text method to create a text element inside another:
xml.SearchCondition('expressionLanguage' => 'String', 'expressionType' => 'PartyNumber') {
xml.text "31955854"
}
You're not using the namespace in your example, but you don't mention that, so I guess that is not an issue.