Generate XML dynamically and post it to a web service in Rails - ruby-on-rails

I am currently developing a Rails app in which I need to dynamically send XML request to an external web service. I've never done this before and I a bit lost.
More precisely I need to send requests to my logistic partner when the status of an order is updated. For instance when an order is confirmed I need to send data such as the customer's address, the pickup address, etc...
I intended to use the XML builder to dynamically generate the request and Net:HTTP or HTTParty to post the request, based on this example.
Is that the right way to do so? How can I generate the XML request outside the controller and then use it in HTTParty or Net:HTTP?
Thanks for your help,
Clem

That method will work just fine.
As for how to get the XML where you need it, just pass it around like any other data. You can use the Builder representation, which will automatically convert to a String as appropriate, or you can pass around a stringified (to_s) version of the Builder object.
If, for example, it makes sense for your model (which we'll call OrderStatus) to generate the XML, and for your controller to post the request:
# Model (order_status.rb)
def to_xml
xml = Builder::XmlMarkup.new
... # Your code here
xml
end
# Controller (order_statuses_controller.rb)
def some_method
#order_status = OrderStatus.find(:some_criteria)
... # Your code here
http = Net::HTTP.new("www.thewebservicedomain.com")
response = http.post("/some/path/here", #order_status.to_xml)
end
You may want to wrap the HTTP calls in a begin/rescue/end block and do something with the response, but otherwise it's all pretty straightforward and simple.
Make XML with Builder, then send it down the wire.
In your case it sounds like you may need to send several different requests as the order evolves; in that case:
Plan out what your possible order states are.
Determine what data needs to be sent for each state.
Decide how to represent that state within your models, so you can send the appropriate request when the state changes.
Where my example uses one method to generate XML, maybe you'll want 5 methods to handle 5 possible order states.

Related

How to make a rails server wide object?

I am using the RedditKit gem and in order to access certain elements, I need to send a request to reddit api to create a "client" object. Below is my current logic:
## application_controller
before_action :redditkit_login
private
def redditkit_login
#client = RedditKit::Client.new ENV["reddit_username"], ENV["reddit_password"]
end
As you can see in my logic here, before EVERY REQUEST, I am subsequently making a new client object and then using that everywhere.
My question is, how do I only make one client object which can be used to serve ALL requests from anywhere?
My motive behind this is speed. For every request to server, I am making a new request to reddit and then responding to the original request. I want to have the client object readily available at all times.
You have a lot of options. A simple one would be to create a config/initializers/reddit_client.rb file and put in there:
RedditClient = RedditKit::Client.new ENV.fetch("reddit_username"), ENV("reddit_password")
(note I switched to ENV.fetch because it will error if the key is not found, which can be helpful).
You could also rename the file as app/models/reddit_client.rb. Although it's not really a model, that folder is also autoloaded so it should work as well.

Generate XML Request Payload with Template in Rails

I'm working on a rails app that integrates with a 3rd party on the backend. To give a quick overview, a user will create an order in our web interface and that will persist a delayed job. So, the job runs outside of a web context and will send a request to this 3rd party. The 3rd party expects a POST with an XML payload in the request body.
I'm currently using Faraday to do the request. I have a class that looks something like this:
class FooRequest
def response
connection.post("/foo", xml)
end
private
def xml
xml = Builder::XmlMarkup.new
xml.root do
xml.foo "something something"
end
xml.target!
end
def connection
#connection ||= Faraday.new(url: "http://example.org")
end
end
The XML is actually bigger and more complex than that and so I don't want to build the xml in this class. I think having some sort of template is best. I'd either like to use Builder in a xml.builder file or just have raw XML in a erb file. Not sure which is best and how best to render a template in this context. I know that rails recently added the ability to render views outside of a controller context (https://medium.com/evil-martians/new-feature-in-rails-5-render-views-outside-of-actions-2fc1181e86a8#.s344qg5ue) but I feel like that feature wasn't built for this use case. I also know that there are other templating languages like Liquid and maybe that's a better fit.
Has anyone done this sort of thing in the past? Is there something I'm missing?
Also, if I do create a template file, should I put it in /app/views? It's not a view that my webapp would render, maybe I should have a /app/templates dir?
PS: Rails's to_xml method is not robust enough for what I want (I need tag attributes and I don't want to represent a deep complex XML tree with a ruby hash).
I've done a similar thing, the easiest way I found was to implement whatever XML classes I needed into a separated gem (you don't really have to do this you can just add the classes somewhere in the models folder, it made sense for my use case).
I used ROXML for this; after that you can simple call root.to_xml.to_s to render your string, see the examples on github, it's really straightforward.

Is it possible to request partial models with ActiveResource?

We have a suite of applications that make heavy use of ActiveResource to provide data across the system. We also override model#as_json a lot to provide additional 'readonly' data for use by the client. This additional data is provided on every resource request and is creating performance issues, especially if we only want basic subset of the model data. Is it possible to request 'variants' of the return data (similar to how HTTP allows for basic info via HEAD requests, and whole response via GET requests)?
# Full response in JSON
def as_json(opts)
super(opts).merge({
"connected_person_name" => self.connected_person.name
})
end
# A Partial response in JSON
def basic_as_json(opts)
super(opts).slice("id", "name", …)
end
Ideally, I'd like to be able to have a controller respond_to { |f| … } style block choosing which JSON method to call based on some parameter of the request. I could include this an attribute, but as it's configuration for the request, and not a model attribute, this seems like the wrong place to put it. I feels like this should be part of the request header, but i haven't seen anything that covers my needs.

Is there any gem to send request to some service in xml format

I want to send request to some third party service in xml and also expecting response in xml. I'm searching for some gem or any idea how to do this.
Thing which is in my mind is to
make some partail _example.xml.builder
onclick from my view to some button send ajax request to controller action and use render_to_string to render that xml doc and then
Save it in some variable
and then call to that service method in same action
But it is not proper thing as I expect there should be some thing more efficient than my suggested thing
RoR doesn't natively use XML so some degree of conversion is required.
Having said that, XML generation is very simple in RoR applications. There are several ways of doing this, my favourite being constructing the required data as a Hash (which is native to Ruby) then the_hash.to_xml.
The XML conversion can also be defined in a model Class if you wish a consistent result:
class Example < ActiveRecord::Base
# ensure that only column1, column2, etc are output as XML
def to_xml(options = {})
super( options.merge( select(:column1, :column2, etc) ) )
end
end
Then in your controller:
poster = Example.find(123)
request = Net::HTTP.new('www.example.com', 80)
request.post('/path', poster.to_xml)
Hopefully the above demonstrates a simple example of posting XML data to a remote host. As you mentioned, a more complicated XML can be constructed using xml.builder
HTH and good luck.

Make Rails return response from other local URL

I want a /plan method to return a json object that's itself returned by another local (but belonging to another web app in java) URL (let's call it /plan2 for the sake of this question).
What I want is not a redirect but really to have /plan return the data as it is returned by /plan2, to which I'm appending a bunch of other keys. Since the request is local, would using Net::HTTP be overkill? What are my options, considering I'd also like to send an HTTP Accept header along.
Shooting in the dark here, but I am assuming that /plan belongs to public Rails app and the /plan2 is the url from another web app (maybe not even Rails) on the same server accessible by Rails app but not publicly available. If this is the case, then yes, you can get the response, but I would suggest rather OpenURI or Mechanize than Net::HTTP. If you put it in respond_to JSON format block then everything should be fine with Accept header also.
are you speaking of re-using functionality of another controller-method?
there is no standard way of doing this in rails. you could either put the common functionality into a module and include it this way, or use inheritance if the functionality is in another controller. if it's the same controller class, you could just call the method.
if it's a "private" url, how are you going to call it via http?!
I would suggest encapsulating whatever functionality /plan2 uses and simply re-use that in /plan1
But if you just want to get it to work...
class PlanController < ApplicationController
def plan1
plan2(extra_parameter_1: true, extra_parameter_2: 'hello')
end
def plan2(extra = {})
params.merge!(extra)
# Whatever your code was before...
end
end

Resources