Pass XML value from one page other page - ruby-on-rails

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.

Related

Where do I save a variable that should not be overwritten when I refresh the page in rails

I am pretty new to Rails and Ruby and still wrapping my head around the hole concept of Rails.
What I want to do:
I'm creating a shift-planner with a view of one week and want to create a button that will show the next/last week.
What I did:
I have 3 tables that are relevant. shift, person and test (contains types of shifts)
Where both Test and Person have one to many relations to Shift.
In my controller I did the following:
def index
#todos = Todo.all
#shifts = Shift.all
#people = Person.all
#start_of_week = Date.new(2015,8,7)
#end_of_week = Date.new(2015,8,11)
view:
<% person.shifts.where(:date_of_shift => #start_of_week..#end_of_week).order(:date_of_shift).each do |shift| %>
<td>
<%="#{shift.test.name} #{shift.date_of_shift}"%>
</td>
<%end%>
My Idea was I would make a link where I would increment both Dates and refresh my Page
<a href="/todos/new">
Next Week
<% #start_of_week += 7 %>
<% #end_of_week += 7 %>
</a>
Unfortuately that doesn't work. Cause everytime I call the index function in my controller it sets the date on the default value.
And I'm pretty clueless how to fix this problem in a rails way.
My only would be to somehow pass the dates as parameter to the index function or something like that.
The general structure is:
I scaffolded a Todo view/controller/db just for the sake of having a view / controller and my 3 tables.
Thx for the help.
PS: I'm using the current version of ruby and rails on lubuntu15 (shouldn't be really releveant^^)
The controller method will be invoked every time someone loads the page. If you want to have temporarily persistent storage you should look into cookies (generally sessions are easier to use but in your case it might be relevant to let the client change the data too, click on back/forward buttons and you don't want to have a HTTP request for every little interaction, right?). You can find the relevant documentation here.
Using the variable could look like this:
# in the controller
cookies[:start_of_week] ||= Date.new(2015,8,7)
# in the view
cookies[:start_of_week]
Maybe you want to work with an integer instead of a date object here if you plan on using it on the client side, but then you would have to take that into account in your views.
Edit: On re-reading your question I realized that my answer didn't address several things.
Why is the code you tried not working?
<a href="/todos/new">
Next Week
<% #start_of_week += 7 %>
<% #end_of_week += 7 %>
</a>
When writing your views you have to be aware that it will be evaluated on the server side and then be sent to the client side (web browser in this case). You increment the instance values #start_of_week and #end_of_week when the page is rendered but not later.
So either you have the link go to a special action in your controller that will handle the new value set the cookie/session variable (later one is an option only in this case) and render a new page and send it back to the user or you use Javascript and do this on the client side and change the cookie so no unescessary additonal requests have to be made (but then thinking about it you will most probably need to load data from the server anyway so it might not make any difference which one you take).

Rails: Is it dangerous to use hidden field tags for money related params?

I am creating a checkout for an online store. When the customer has filled out his shipping and billing info, it takes them to an order summary page. Here's my action controller for that:
def order_summary
#cart = current_or_guest_user.cart
#purchase = current_or_guest_user.incomplete_purchases.last
if #purchase.shipping_method.uses_ups?
products = #cart.cart_items.map(&:product)
ups_shipping = UpsShipping.new(products: products, purchase: #purchase)
#shipping_rate = ups_shipping.request_shipping_rate
else
#shipping_rate = UnitedStatesPostalServiceShipping.calculate_shipping_rate(sub_total: #cart.total)
end
end
UpsShipping and UnitedStatesPostalServiceShipping are non ActiveRecord models that I wrote to hit external APIs to request a shipping rate. They don't point to any tables in my database.
This they don't have any records in the database, I need a way to persist the value of the #shipping_rate response in the request that follows order_summary and store it as #purchase.shipping_cost. I'm thinking of either just storing it in the session or putting a hidden field in the view.
Would it be safe for me to have a <%= hidden_field_tag "purchase[shipping_cost]" #shipping_rate %> tag in the view? Or would putting that in the session be safer?
Or is hitting the UPS/USPS API another time in the following request the best choice? I am thinking that hitting the APIs again would make my app a little more fragile, which is why I only want to hit it once and store the value. Thoughts on this?

Fragment Caching and User avatars/images

We use Gravatar on our website, but we want to let users upload their profile images directly in an effort to improve user experience similarly to what Stackexchange has been doing.
On our website users can follow each other, comment, 'like' and interact in ways that cause content to be generated directly and indirectly.
This means that cache-fragments with user avatars are scattered all over the place, and we can't find a reasonable way to invalidate it without negatively affecting render performance.
Here are the possible solutions we've looked at:
Option 1) Take the Gravatar approach and set very short Expires/Cache-Control max-age headers and recycle the same image filename.
Option 2) Use a placeholder image for all users with a data attribute containing the user ids that are read by JavaScript and used to make a second Ajax request asking the server for up-to-data avatars.
Is there any better way to solve this problem? Are we overlooking something?
I think I understand your question, but Im not sure I understand option 2 as a solution, which may indicate that its not a great solution. If it was me I would just cache the html surrounding the user gravatar, which is being reused, in a top level cache key, keyed by the users id. I.E.
<% cache("user_#{user.id}_profile_image") do %>
<img src="blahblahblah or gravatar helper code or whatev">
<% end %>
If you're concerned about a user uploading a gravatar, and subsequently having a cached default gravatar image, I would say your best options are:
1) In the users profile area where you send them to upload a gravatar, have a refresh link which points to a refresh action which actually invalidates that users cache fragment. i.e.
expire_fragment "user_#{user.id}_profile_image"
(http://guides.rubyonrails.org/caching_with_rails.html)
2) you could instead of using the default gravatar redirect to upload an image, you could intercept the click, send it to your own controller, schedule a background task to be run in 15 minutes or so, and then render a javascript response which redirects them to the actual gravatar page to upload their pic. The background worker would then clear the fragment at a later time when it ran. This assumes they actually upload their image and is alltogether I would say a terrible idea.
Honestly though, Im not sure why you are concerned about caching the gravatar to begin with. It's hitting gravatars servers so it causes no load on your server, storing it yourself seems a bit self defeating to the point of using gravatar. Hopefully your question was simpler (and was just: how to clear default cached gravatar image when user uploads their own image), which can be solved by #1), which will allow you to expire the cached gravatar image and recache it using your own image, after the user uploads their image. (which, next time the page was rendered would recache the image because youd have some logic like:
<% cache("user_#{user.id}_profile_image") do %>
<% if user.has_uploaded_image? %>
<%= display_profile_image %>
<% else %>
<%= display_gravatar_image %>
<% end %>
<% end %>

show content items based on user date params

We have a website with holiday rentals/activities/events in Umbria/Marche(Italy). We have nice content of the area, such as events (model), activities(model) etc. Each model has the attributes start_date and end_date.
I want to make a form with date-fields and ask the visitor what the period is that they want to come to umbria/marche. When the visitor submits the data I want the entries of events and activities that are showed of the 'user' period. I can make the form/calendar. But how can I show the correct 'user input based' content in the entire website. Must this be session based?
Besides using a session you could also overwrite the url_for helper. You would then add the user's data directly to the url and read it on any subsequent request.
Check this documentation and especially the code at the end of the page: http://apidock.com/rails/ActionController/Base/url_for
I would put that new url_for function in your ApplicationHelper.

Rails best practice for having same form on multiple pages

I am developing an Rails 2.3.1 Web site. Throughout the Web site, I need to have a form for creating Posts on various pages (Home page, Create Posts page, Post listing page, Comment listing page, etc. -- suffice to say this form needs to be on many pages served by a variety of controllers). Each of these pages displays a wide variety of other information that is retrieved in the corresponding controller/action. Ex, the home page lists latest 10 posts, content pulled from the DB, etc.
So, I've moved the Post creation form into its own partial, and included this partial on all the necessary pages. Note that the form in the Partial POSTs to /questions (which routes to PostsController::create -- this is default rails behavior).
The problem I am running into is when the the Posts form is not completed correctly, by default the PostsController::create method render's questions/new.html.erb, even if the form was submitted from the home page (/home/index.html.erb).
I tried changing the form in the partial to submit the "submitting_controller" and "submitting_action", and in PostsController::create, when #post.save? == false, I render action => "../submitting_controller/submitting_action" (Which is slightly hacky, but lets you render actions from non-PostsController's).
This seemed to work OK on the surface. The incomplete form was rendered in the view that submitted it with all the correct #post.errors message, etc. The problem was ALL the other data on the pages didnt show up, because the actual submitting_controller/submitting_action methods weren't called, just the associated view. (Remeber, I did a render which preserves instance objects, rather than a redirect_to which does not preserve the #post instance object which has all the error messages and submitted values.)
As far as I can see I have two options:
1) I can store the #post object in the session when #post.save? fails in PostsController::create, redirect_to submitting_controller/submitting_action, at which point i pull the #post object out of the session and use it to re-populate the form/error messages. (As far as I understand, storing objects in the session is BAD practice in rails)
2) I can move all the logic used to pull non-post creation form data from the various submitting_controller/submitting_action, put it in the ApplicationController, create a giant switch statement in PostsController::create for submitting_controller/submitting_action and call the methods in the ApplicationController to grab all the extra data needed for each submitting page's render.
Thoughts on the best way to do this within Rails?
By any chance is your Post model in a belongs_to relationship with each model who's controller you'll be using to render your form? Are you doing any special processing in the Post controller beyond Post.create(params[:post])?
If you answered yes to the first question and no to the second, you could get by with minimal mangling by adding accepts_nested_attributes_for to each of the controllers on which a user can create a post.
Regardless, Robertpostill is correct in that this is probably when to start looking at AJAX, to just replace the section of the page. The only problem is what to do if a user has javascript disabled. Personally I like to do design for the non-javascript case and add convenience methods.
As for thoughts on what you consider your two options,
1) I've used this method to store a shallow copy of an object in the flash hash. Preserving it across redirects. However this might not work for you given the variable nature of posts. As you can only send about 4K worth of data, and that includes other information in addition to your shallow copy.
2) See robertpostill's response
This is about the point that you move from full page updates to updating sections of a page through the use of AJAX. There are a bunch of things you should consider but the most rails-esque approach would be to split the response between an AJAX response and a plain HTML response. Check out this ONLamp article, this register article or the awesome agile web development with rails book. Essentially your controller renders a new div replacing the old div containing the result of submitting the partial.
In your question you mention two approaches and so I'll try and give you some pointers on why and why not here:
Option 1) Ths option is not so bad with a couple of tweaks. The main tweak is is to store the object in a serialized form in the DB. Then simply pass around the ID of the serialized object. Your upsides are that the session data gets persisted so recovering a a session is neater and your session stays light. The downside of this is that having a bucket of session cruft in your DB will pollute your app and you'l need to do some thinking as to how you expire unused session cruft from the DB. I've never seen this end well...
Option2) Eeek not inside the application_controller! :) Seriously, keep that as your weapon of last resort. You can pop things insde the helpers though and get access to those methods inside your controllers and views. However the testing of that stuff is not so easy so be careful before choosing that route. Switch statements can be replaced in OO apps with a little thinking, certainly in his case you can use option hashes to get a railsy way of having some smarts about the state of the app at the time the request is made.

Resources