GETing different Active Resource models in a single request - ruby-on-rails

Is it possible to receive objects of different Active Resource models in a single request? For example the request "GET /user/joe/articles/1.xml HTTP/1.1" returns an object from User ("joe") and another object from Article (id "1") from the server.
I know it is possible to send these objects inside an array to the client, but ARes can't process them. Is there anyway to "break" the response in two and send the results to different Active Resource models for processing?

I think what you want to do is include the user xml inside the article xml that is returned.
If you are using ActiveRecord to make the xml the you can use the :include parameter of the to xml call to get the user included in the article response. The output is something like
<article>
...
<user>
...
</user>
</article>
You should then be able to call '.user' on the returned article object to get at the user properties.

Related

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.

How to indicate empty array in HTTP request query params?

This is the way Rails accepts arrays in query parameters:
PUT /resource.json?sport_ids[]=133&sport_ids[]=64&sport_ids[]=71 ...
I tried to google this question but didn't find any explicit docs on it:
How to tell Rails that we want sport_ids to become empty (pass empty array of sport_ids via query parameters) ?
HTTP requests can have only variables on the url itself. That's a limitation feature of HTTP, not Rails.
Take a look at How Does Rack Parse Query Params? With Parse_nested_query to figure out how rails collects the variables into an array, it won't run out of the box in case of an empty array.
You can avoiding sending the params["sport_ids"] and patch your controller with:
params["sport_ids"] ||= []
The best practice to use put/post requests, is passing such data in the request body (json/xml) like:
{
"sport_ids": []
}
Or with data as:
//...
{
"sport_ids": [133, 64, 71]
}
//...
For more info about HTTP request steps, check Running a HTTP request with rails.
While #mohameddiaa27's answer has good advice on how to achieve that by passing such data in the request body as JSON I found that I cannot rely on it within my application: I found that it is not easy to combine such passing of JSON into request body within multipart forms where I want to pass user record (with user[sport_ids] in it) and user's avatar image (user[avatar]) field.
So I continued to investigate how to achieve that using default "query parameters in a request body of POST/PUT request" approach and found the reason why I was not able to reset my sport_ids on server-side: it was the lack of permission for that specific sport_ids field. Now I have the following permits (pseudocode):
current_user.update!(user_params)
where user_params is
user_attributes_to_permit = [
...
:sport_ids, # this is what was needed for just `user[sport_ids]` to work.
{ :sport_ids => [] } # this is needed for sport_ids non-empty arrays to work
...
]
params.require(:user).permit(user_attributes_to_permit)
so now I am able to reset the sport_ids array of my user by passing just user[sport_ids] (without '=' and value! i.e. ...&user[sport_ids]&...) within my query parameters.

Pass XML value from one page other page

I want to pass an XML value from one page to another in a better way.
I am getting this XML value from API:
<hotelist>
<hotel>
<hotelId>109</hotelId>
<hotelName>Hotel Sara</hotelName>
<location>UK,london</location>
</hotel>
<hotel>
<hotelId>110</hotelId>
<hotelName>Radha park</hotelName>
<location>UK,london</location>
</hotel>
<hotel>
<hotelId>111</hotelId>
<hotelName>Hotel Green park</hotelName>
<location>chennai,India</location>
</hotel>
<hotel>
<hotelId>112</hotelId>
<hotelName>Hotel Mauria</hotelName>
<location>paris,France</location>
</hotel>
</hotelist>
I want to pass one hotel:
<hotel>
<hotelId>112</hotelId>
<hotelName>Hotel Mauria</hotelName>
<location>paris,France</location>
</hotel>
to next page.
I am using the Nokogiri gem for parsing XML. For the API next call I have to pass the one hotel to the next page. Which is the best method?
Note: This is just a sample. There are a lot of information bound with the hotel including available room, discount and so on.
So as far as I'm getting this, you are searching for some hotels through a third party service, and then displaying a list. After the user clicks on an item you displaying the detail info
for the hotel.
The easiest way would be having another API endpoint, which can provide the detail information for a specific Hotel id. I guess you're dealing with some really bad API and that's not the case.
There are couple of other options (ordered by complexity level):
There is really not much data and it should fit an simple GET request, so you can just encode the respective hotel information into the URL parameter for the detail
page. Assuming you have set up resourcefull routing and have already parsed the XML into #hotels array of some Hotel models/structs or the like:
<% #hotels.each |hotel| do %>
<% # generates <a href=/hotels/112?hotelName=Hotel+Mauria&location=paris%2C+France'>Hotel Mauria</a>
<%= link_to hotel.hotelName hotel_path(hotel.hotelId, hotelName: hotel.hotelName, location: hotel.location) %>
<% end %>
Encode the info into the respective Hotel DOM elements as data-attributes:
<div class="hotel" data-id="112" data-hotel-name="Mauria" ... />
Then render the detail page on the client side without the server entirely by subscribing to a click event, reading the info stored in the respective data attributes and replace the list with the detail div.
If the third party API is public you could even move the search problem entirely to the client.
Introduce caching of search requests on the server. Then just pick a hotel from the cache
by its id. This would be saving you from doing to much third party requests from your Rails app, which is a weak spot of Rails if deployed on a typical multi-process server.
The simplest way of doing this, would be storing the last search result in a user session, but that
would be probably too memory heavy. If you can expect the hotel information not to change frequently, you could cache it by the query parameters. You could also use some smart caching store like redis and index the entire hotel information, than performing the search on the cache and only in case of the cache miss hit the third party API. But always remember, caching is easy, expiring is hard.
"Everyone should be using low level caching in Rails" could be interesting for implementing a cache.
If you don't mind passing all that information in query parameters:
links = doc.xpath('//hotel').map do |hotel|
hash = Hash.from_xml(hotel.to_xml)
url_for({controller: 'x', action: 'y'}.merge(hash))
# or if you have your link as a string
# "#{link_string}?#{hash.to_param}"
end
If you want to create a link for just one hotel, extract the relevant XML (e.g., using the process described in Uri's answer), and then generate the link as above.
Assuming you have the API XML ready before you render the current page, you could render the relevant hotel data into form fields so that you could post to the next page, something like:
<%= fields_for :hotel do |hf| %>
<% hf.hidden_field :hotelId, value: hash['hotel']['hotelId'] %>
# ...
<% end %>
One optimum way to achieve this is as suggested by Mark Thomas.
However if you still need to pass data between pages you can put all the xml information as a string in a session variable and use it on next page.

Generate XML dynamically and post it to a web service in 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.

Backbone.save POST instead of PUT

just a short question:
Having the new instance of a model and issuing a model.save() with URL set to /api/store/category, Backbone issues a POST. According to my knowledge, it should use PUT, like mentioned in this "PUT or POST: The REST of the Story" blog post.
Who is right? BB or this article's author?
According to Backbone documentation, saving a new model will result in a POST request, and saving an existing model (having an id) will emit a PUT request.
save model.save([attributes], [options])
...
If the model isNew, the save will be a "create" (HTTP POST), if the model already
exists on the server, the save will be an "update" (HTTP PUT).
And if you are wondering if Backbone should use a POST for creation, check
PUT vs POST in REST
RESTful web services on Wikipedia
In the light of these articles, I'd say that, in the context of Backbone, the verbs are correctly used:
saving a new model causes a change in the system, a new URL is added, the action is not idempotent, it should be a POST,
saving a known model replaces a resource at a given URL, the action is idempotent, it should be a PUT.

Resources