In my Rails 4 app I am dealing with an API that only handles XML (yes I wish it was JSON too).
I have to make a POST request and the XML string should be stored in a parameter called xmlRequestString.
The XML structure for the POST data is:
<?xml version="1.0" encoding="UTF-8"?>
<GetProperties>
<Auth>
<VendorId>UserID</ VendorId>
<VendorPassword>Password</VendorPassword>
</Auth>
</GetProperties>
As I have never even touched XML before could someone show me how I would actually post this data.
Would something like this be a good way of going about it (borrowed from here: Submitting POST data from the controller in rails to another website)?
require "uri"
require "net/http"
xml = 'xml string can go here'
params = {'xmlRequestString' => xml}
Net::HTTP.post_form(URI.parse('urlendpoint'),params)
You can save this as a template, with instance variables like in a regular html.erb template. Or, you could have it as a method in a model. Either way, you're using something that takes some dynamic data and returns you a text string, that has the xml in it. Then, in your controller, render out the template, or call the method (if you put it in a model) and post it to the api.
#Template method of generating xml
#app/views/properties/get_properties.rxml
xml.instruct! :xml, :version=>"1.0", :encoding => "UTF-8"
xml.GetProperties do
xml.Auth do
xml.VendorId do
<%= #user_id %>
end
xml.VendorPassword do
<%= #password %>
end
end
end
Then, in a controller, call the api:
#user_id = "foo"
#password = "bar"
xml_string = render :template => "properties/get_properties.rxml", :layout => false
http = Net::HTTP.new("www.the-api-website.com")
response = http.post("/path/to/call/the/api", xml_string)
Related
I'm creating a custom ActionMailer which will also send Direct Messages on Twitter, and Text Message through Twilio. I need each to receive a parameter of the body of the message. I need to use the current template as a string variable.
For example:
# calling send_invite's mail method will load the send_invite template
def send_invite(to)
mail(to: to) # body parameter is automatically rendered from template
end
Now if I modify it to send through other services:
def send_invite(to, option)
if option == :email
mail(to: to) # body parameter is automatically rendered from template
elsif option == :twitter
twitter.create_direct_message(to, NEED_TEMPLATE_AS_STRING_HERE)
elsif option == :sms
twilio.sms(to, NEED_TEMPLATE_AS_STRING_HERE)
end
end
How might I grab the template for each of these service calls? Would there be something like:
message(to, render layout: false)
# or
message(to, IO.read template_path_helper("template") )
# or
message(to, template_helper.to_s)
How might I get the template as a string for my message body in these other methods? What's the Rails Way to do it? The templates are erb files and need to be able to render the appropriate variable as they would from a render.
I need to do it this way for translations of templates to be available.
I think you can create the template string from the controller, and send it to your mailer, like this:
#parsed_template = render_to_string(:partial => 'my_partial', :layout => false, :locals => {:my_object => my_value})
SomeMailer.send_invite(to, option, #parsed_template).deliver
I am trying to have one of my pages request and display info from the last.fm API. At this point there is no user input and I just want the root page on my application to display the results of this API call.
My last_fm Controller:
class LastFmController < ApplicationController
include HTTParty
format :json
base_uri 'http://ws.audioscrobbler.com/2.0/'
def self.get_events
return get('http://ws.audioscrobbler.com/2.0/', :query => {
:method => 'geo.getEvents',
:api_key => 'xxxxxyyyyyyzzzzz'})
end
def index
#events = LastFmController.get_events
end
end
My last_fm#index view is empty except this: <%= #events %>.
The error I'm getting when I go to the last_fm#index view:
795: unexpected token at '<?xml version="1.0" encoding="utf-8"?>
<lfm status="ok">
<events xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" location="San Francisco, United States" page="1" perPage="10" totalPages="37" total="364" festivalsonly="0" tag="">
<event xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" >
<id>3782989</id>
<title>Broken Hope</title>
<artists>
<artist>Broken Hope</artist>
<artist>Oceano</artist>
This trace actually includes the entire hash I received from the API call, so it's long and I'm omitting it for brevity.
The title of the error page is Action Controller: Exception caught.
I would be thankful if I could just do some basic manipulation with the hash I received, like being able to navigate the hash and only display certain items. Thanks!
You are trying to parse XML as JSON. XML parser here: Nokogiri
LastFM returns XML by default. You have to pass in format=json as a paramter so it returns JSON. This should work:
class LastFmController < ApplicationController
include HTTParty
format :json
base_uri 'http://ws.audioscrobbler.com/2.0/'
def self.get_events
return get('http://ws.audioscrobbler.com/2.0/', :query => {
:method => 'geo.getEvents',
:format => 'json',
:api_key => 'xxxxyyyyyzzzz'})
end
def index
#events = LastFmController.get_events
end
end
I have a requirement where I need to generate/spit out HTML markup from one of my APIs. I am using grape API but cannot find a way to throw out HTML markup. I can specify content-type as text/html and create a HTML markup but is there a better way to achieve this like rendering a template similar to below:
render template:'my_template' locals: {:data => data}
and 'my_template' (HTML) can take care of how the page looks like ? render is an undefined method in GrapeAPI so not sure what other stuff I can use ?
I think it's quite a bad idea to use an API only framework to render HTML...
Nevertheless, you should be able to use the :txt content-type to simply render out your string like you described.
You could use ERB for that, as it is part of the standard-library and pretty easy to use:
require "erb"
class Template
attr_reader :name, :data
def initialize(name, data)
#name = name
#data = data
end
def build
raw = File.read("templates/#{name}.erb")
ERB.new(raw).result(binding)
end
end
as far as I read, grape automatically uses the to_s method of an entity to render :txt, so you could implement something like this in your model:
def to_s
Template.new(self.class.to_s.downcase, self)
end
it might also be possible to register a html content type and write some kind of formatter that does this kind of stuff.
I am new to rails and looking someone to point me in the right direction in how to accomplish the following:-
I need to communicate with an external api by either passing an XML document directly
into the cgi (https://api.domain.com/v1/method.cgi) and set the content-type to
"text/xml", or pass it as a parameter and set the content-type to "text/plain"
I supposedly get an XML response back instead of the HTML response, so no need to
download the HTML response, store it, then render a local copy for the user; nor will
i need to stick the XML document within a parameter of a locally generated HTML form
to submit it via the browser to avoid downloading the HTML.
Each API method has example xml code for (Sending, The Response, DTD, Schema)
What is the best tools/techniques to accomplish this !??
One of their simpler methods is as follows :-
**SEND**
<?xml version="1.0" encoding="utf-8" ?>
<SoftwareAPI>
<Method>ListUsers</Method>
<APIKey>123</APIKey>
<Account>
<UserName>admin</UserName>
<Password>Password</Password>
</Account>
</SoftwareAPI>
**RESPONSE**
<?xml version="1.0" encoding="utf-8" ?>
<SoftwareAPIResponse>
<TimeNow>2012-01-23T16:44:00Z</TimeNow>
<ResponseId>01-23-1232729040-23456</ResponseId>
<ListUsersResponse>
<User>
<Team>team</Team>
<Office>office</Office>
<UserName>Joe.Bloggs</UserName>
<Password>Password123</Password>
<FullName>Joe Bloggs</FullName>
<Language>Auto-Detect</Language>
<Telephone>+44 207 123 456 789</Telephone>
<ResponseEmail>joebloggs#domain.co.uk</ResponseEmail>
</User>
</ListUsersResponse>
</SoftwareAPIResponse>
This API method needs no interaction from the user or view, should the coding be done
from the controller or should I create a model for all the api methods ?
How do I perform a post to the cgi url with the specified XML above and process the
response XML and display in a view ?
What are the best practices for accomplishing this ?
Many Thanks in Advance
Jonny
You guessed it: the best place for your API client is a model. Using a library such as HTTParty or RestClient, this task is fairly easy. The controller should do no more than request the data needed for the view.
Here's some sample code using HTTParty. Since I don't have the details, you'll have to modify it a bit. This would be a model:
class JonnyService
include HTTParty
base_uri 'http://localhost:3000'
end
Then you can use it like this. Note that it might be better to move some of this logic (e.g. creation of the post params for each service method) into the model as class methods for additional convenience.
options = {
:body => {
:SoftwareAPI => {
:Method => 'ListUsers',
:APIKey => '123',
:Account => {
:UserName => 'admin',
:Password => 'password'
}
}
}
}
response = JonnyService.post('/service.xml', options)
puts response.inspect
#response can be treated as a data structure:
puts response['ResponseId']
In a rails (2.3) app I have been building, I have an XML builder that outputs neat XML. I need to take a snapshot of this XML and store it in the database (or file) upon a certain user action.
How do I get the output of the xml builder view from in the middle of another action?
This code causes a deadlock in a single threaded application...
uri = URI.parse("http://localhost:3000")
response = Net::HTTP.start(uri.host, uri.port) {|http|
http.head('/xmliwanttoarchive.xml')
}
Not sure how to approach this one.. Cheers.
The above is the wrong way to go about invoking the xml builder.
I overrode the to_xml method instead of using an xml.builder view
def to_xml(options={})
if options[:builder]
build_xml(options[:builder])
else
xml = Builder::XmlMarkup.new
xml.instruct!
build_xml(xml)
end
xml
end
def build_xml(xml)
xml.foo("id" => id, "title" => title) do
xml.group("id" => group.id, "name" => group.name)
xml.bars do
bar.each do |session|
xml.bar("id" => bar.id, "session_time" => bar.important_thing)
end
end
end
Now it can be easily used from within the code and from an action