I need to have a button to save the current web site (just like clicking on "Save as"), I created a method in the controller which works great for any external site (like http://www.google.com) but doesn't work for the sites inside my application, I get a timeout error!. This has no explanation to me :(
Any clue what is the issue?
#CONTROLLER FILE
def save_current_page
# => Using MECHANIZE
agent = Mechanize.new
page = agent.get request.referer
send_data(page.content, :filename => "filename.txt")
end
I tried also Open URI, same problem!
#CONTROLLER FILE
def save_current_page
# => USANDO OPEN URI
send_data(open(request.referer).read, :filename => "filename.txt")
end
I'm using rails 3.2 and ruby 1.9, any help is appreciated, I already spent like 10 hours trying to make it work!!
Rails can only handle one request at a time. It's a never-ending standoff between the two requests - the first request is waiting for the second request, but the second request is waiting for the first request, and therefore you get a Timeout error. Even if you're running multiple instances of the app with Passenger or something, it's a bad idea.
The only way I can think to get around it would be to use conditional statements like so:
referer = URI.parse(request.referer)
if Rails.application.config.default_url_options[:host] == referer.host
content = "via yoursite.com"
else
agent = Mechanize.new
page = agent.get request.referer
content = page.content
end
send_data content, filename: "filename.txt"
A little dirty but it should get around the Timeout problem. As far a getting the actual content of a page from your own site - that's up to you. You could either render the template, grab something from cache, or just ignore it.
A much better solution would be to enqueue this code into something like Resque or Delayed Job. Then the queue could make the request and wait in line to request the page like normal. It would also mean that the user wouldn't have to wait while your application make a remote request, which is dangerous because who knows how long the page will take to respond.
After several hours and lots of other posts I got to a final solution:
Bricker is right in that it is not possible for rails to render more than once in a call, as taken from http://guides.rubyonrails.org/layouts_and_rendering.html "Can only render or redirect once per action"
The site also states "The rule is that if you do not explicitly render something at the end of a controller action, Rails will automatically look for the action_name.html.erb template in the controller’s view path and render it."
Then, the solution that worked great for me was to tell the controller to render to a string if a download flag (download=true) was set in :params (I also use request.url to have it working from any view in my application)
View:
= link_to 'Download', request.url+"&downloadexcel=true", :class => 'btn btn-primary btn-block'
Controller:
def acontrolleraction
#some controller code here
if params[:downloadexcel]
save_page_xls
else
# render normally
end
end
def save_page_xls
#TRESCLOUD - we create a proper name for the file
path = URI(request.referer).path.gsub(/[^0-9a-z]/i, '')
query = URI(request.referer).query.gsub(/[^0-9a-z]/i, '')
filename = #project_data['NOMBRE']+"_"+path+"_"+query+".xls"
#TRESCLOUD - we render the page into a variable and process it
page = render_to_string
#TRESCLOUD - we send the file for download!
send_data(page, :filename => filename, :type => "application/xls")
end
Thanks for your tips!
Related
I am trying to send an sms to a number entered in an input field using Nexmo gem
This is what I have so far and it doesn't seem to work
pages/test.html.erb
<%= form_tag "/pages/send_sms" do -%>
<%= text_field_tag "number" %>
<%= submit_tag "Send" %>
<% end -%>
routes.rb
Rails.application.routes.draw do
get 'pages/home'
post '/pages/send_sms', as: 'send_sms'
get 'test', to: 'pages#test'
root 'pages#home'
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end
pages_controller.rb
def send_sms
#number = params[:number]
nexmo = Nexmo::Client.new(
key: ENV['NEXMO_API_KEY'],
secret: ENV['NEXMO_API_SECRET']
)
notification = "Download the app through this link"
response = nexmo.send_message(
from: "GLAM360",
to: params['number'],
text: notification
)
if response['messages'].first['status'] == '0'
redirect_to root_path
end
end
This is what I see in the terminal
Started POST "/pages/send_sms" for 127.0.0.1 at 2017-10-08 00:35:45 +0400
Processing by PagesController#send_sms as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"zPj5PcZrD+uNYxvvfDio8B5uNWitg0vMw+3Vm8KbvQumbNWzsgN4sBJDKsi2srx0rSatiOISegWHQFE860
JxcA==", "number"=>"+971585959698", "commit"=>"Send"}
No template found for PagesController#send_sms, rendering head :no_content
Completed 204 No Content in 803ms
Any help will be highly appreciated here
The error itself tells you what to do. You have few options.
First for your case, just add another redirect if the last check
condition fails. In your case, its failing and so asking for default template. If you specify an else clause, in which you
describe where should it go to (say to the form again with an alert message), rails will itself take care of that.
if response['messages'].first['status'] == '0'
redirect_to root_path
else
redirect_to test_path
end
I used your script and added a send_sms.html.erb with a status
variable being passed from the controller according to the response
of the nexmo.send_message function. Like "Success" or failure and
everything works fine, on the webpage it informs me about the status and I received a text message as well. This is what I will prefer for a better UX.
.
If I have to do it, there would be a lot of changes I would do to the script. A suggestion would be to never rely on an external API, always wrap the interactions you do to an external API in an interface. I will wrap the Nextio scripts, take it out of the controller and place them in an interface (Have a class in either lib or a wrapper function in helper and use interface to interact with it) and will interact with it through my controller. Try not to put logic in your controller. I would also use begin rescue block for interactions with external API and will read about all the errors and think about how to handle them. Lastly for the flow, I would give users more information and not keep them hanging. I would redirect them to another page like send_sms and give them status or use alert messages to tell them whats the status.
Lastly, I would use background tasks for these jobs (which can take sometime depending upon external servers). Use something like sidekiq.
I have files in server for whom i want to keep the url confidential. For this, i created a controller that fetch the data and ultimately render it to the web broswer.
In the view
<%= link_to "Click to view the file", file_proxy( user.pdf_file_url ) %>
In users_heper.rb
def file_proxy(url)
file_proxy_path(url: url)
end
In the routes.rb
get "file_proxy" => "file_proxy#fetch"
In the controller
def FileProxy < ApplicationController
def fetch
response = HTTParty.get params[:url]
render response
end
end
I'm getting an <HTTParty::Response:0x10cd6e6a8 parsed_response="%PDF-1.3......" is not an ActiveModel-compatible object. It must implement :to_partial_path.
Do you know how to tweak this code so that it can display the PDF file correctly ?
Thanks!
You can't call render that way. It's expecting very specific options. In this case it probably looks like:
pdf_content = HTTParty.get(params[:url])
send_data(pdf_content, disposition: 'inline', type: 'application/pdf')
As a note, you probably want to limit what sorts of things that tool fetches or someone will eventually abuse it.
I am very new to rails and following a tutorial for RESTful API so let me excuse if it is of not very good quality as I am equally a starter for these kind of terminologies as well.
I created a controller kitten with a command rails g controller kitten index
and in the index method I posted this code -
class KittenController < ApplicationController
def index
require 'open-uri'
kittens = open('http://placekitten.com/')
response_status = kittens.status
response_body = kittens.read[559, 441]
puts response_status
puts response_body
end
end
and un commented match ':controller(/:action(/:id))(.:format)' in routes.rb
When i navigate through this - http://localhost:3000/kitten
this is what i am getting in my browser -
Kitten#index
Find me in app/views/kitten/index.html.erb
and this in my command line -->
Now my question why it so although i am expecting it in my browser but the cat is shown in command prompt instead of browser ..i am new to rest resource so please excuse if it is a bad one :(
I don't know what tutorial you're following, but doing this seems like a very odd thing to do for Rails in general and learning RESTful APIs in particular.
Anyway, the puts in your controller outputs text to Ruby's standard out, which is going to be the terminal where the server started. That's why this is appearing in the console rather than in your browser: puts is putting it there.
If you want this to appear in a web page, you'll need to make a view for that controller action. Perhaps following further along your tutorial will get you there: if not, you might want to find a better one.
You should read the Model-View-Controller rails guide.
Controllers provide the “glue” between models and views. In Rails, controllers are responsible for processing the incoming requests from the web browser, interrogating the models for data, and passing that data on to the views for presentation.
Define your variable in the controller and display it in the view:
class KittenController < ApplicationController
def index
#variable = 'Hello World'
end
end
In your view (app/views/kitten/index.html.erb):
<%= #variable %>
Rails controllers setup responses with a render call.
When the call is not performed it instantiates the appropriate view and renders that view. In your case that is index.html.erb
Try this:
render :text => kittens.read[559, 441], :status => kittens.status
I am trying to create a site in RoR and have enabled caching for some pages and actions. The related DB may not accessible every time and hence using the cache is very much required. Hence I cant wait for someone to actually visit the page, render it and then cache it. Instead I want whatever is cache-able to be cached manually, programatically. Is it actually possible or is it that caching is completely automatic in RoR?
The lazy* solution would be to visit the page as part of your deployment process with lynx, or even curl. That would trigger the cache event from the outside, but at a time of your choosing.
(*) lazy in a good way, I hope.
Check out this page_cache plugin. Seems like this is what you need.
I am doing manual caching triggering now, and looks like you can use built-in API of actionpack-page_caching plugin to manually trigger the creating of pages cache. You need to use cache_page(content, path, extension = nil, gzip = Zlib::BEST_COMPRESSION) function with attributes (look line 80 at https://github.com/rails/actionpack-page_caching/blob/master/lib/action_controller/caching/pages.rb). Here I made sample action, which is iterating over some collection and making cache of "show" method of each item of this collection.
def precompile
#pages = Page.all
#pages.each do |page|
#page = page
cache_page(render_to_string(template: 'pages/show'), url_for(action: :show, id: #page, only_path: true))
end
redirect_to '/'
end
The url_for(action: :show, id: #page, only_path: true) part of my code is not very clean, but it is the first version of code which is working as I needed, any refactor are welcome.
Also, this code will overwrite the cache file every time it is fired, without checking for any changes or expirations.
Ref :- Link
class ProductsController < ActionController
caches_page :index
def index
end
end
set perform caching to true in your enviorment config/environments/development.rb
config.action_controller.perform_caching = true
I am having problems with Rhomobile rhodes, plaese can someone tell me how to make http post, get, put, and delete using Rho::AsyncHttp?
I've been trying it to no success for hours.
Here's some sample code to place in your controller.rb file
Here's the initial call
def index
Rho::AsyncHttp.get(
:url => 'http://the.page.you.want.to.get',
:callback => (url_for :action => :httpget_callback),
:callback_param => "" )
render :action => :wait
end
the code above will initiate the httpget_callback method (below)
while that goes off and loads the url it'll change the screen and load the wait.erb file
def httpget_callback
if #params['status'] != 'ok'
##error_params = #params
WebView.navigate(url_for :action => :show_error )
else
#html = #params['body']
end
WebView.navigate ( url_for :action => :show_result )
end
Without getting too far into it - the body of the returned page is placed into #html variable
Hope that helps, if you need more help, let me know.
I have a sample of get an post
res = Rho::AsyncHttp.post(:url => 'http://192.168.1.64/WebServiceTest/Service.asmx/Sumar')
#msg= "Sync http call: #{res}"
http://wiki.rhomobile.com/index.php/RhodesConnectToWebServices
I'm often struggling with the nuances of AsyncHttp in Rhodes as well, so I can't claim mastery yet, but I really felt the need to chime in with a suggestion:
I find using the Firebug plugin of Firefox to be VERY helpful when debugging my Rhodes app. You can hook it up very easily! You can load your app with any browser by configuring the web server to run on a specific port. This setting is in rhoconfig.txt and it is called local_server_port.
This is specifically helpful because you can easily survey the HTML and raw data of requests/responses and use the console to run javascript commands and play with the DOM and web page in realtime.