writing raw html to a response object in rails (using ActionDispatcher::Response) - ruby-on-rails

How to write the response from the controller using the ActionDispatch::Response object. There seems to be no api that does http://api.rubyonrails.org/classes/ActionDispatch/Response.html.
The below code works which does not use any view. Is the same can be achived using a response object. The reason being having a necessity to write some binary data to html(which is required for the the current rails app being written)
class HelloController < ApplicationController
def index
render :text => "hello" # want to use ActionDispatch::Response object instead of this
end
end

Have you taken a look at send_data? It may be what you're looking for.

Related

How to fetch local JSON data from Rails e.g bookings.json

Hi all very noob question.
I'm trying to store data in a react calendar but it needs to store it using JSON.
I've noticed that when you scaffold, rails automatically also gives you a JSON version.
In my case - http://localhost:3000/users/1/bookings.json
Which returns [{"first_name":"Fake Name","booking_time":"2019-04-22T02:03:00.000Z","pick_up_time":"2019-04-22T02:03:00.000Z"}] in JSON.
I know how to fetch JSON data from a external URL and parse it through however all these external URL's are public whereas in my case the bookings are private.
Is there a way for me to fetch from bookings.json and store it in a variable and also by making it private where I wouldn't need to publicise it?
class HomeController < ApplicationController
def dashboard
#lookup_booking = ???("/users/1/bookings.json")???
end
end
React dashboard
<%= react_component("Booking", { booking: #lookup_booking})%>
You could make a local request to the Bookings JSON endpoint the same way you'd make any external request - using something like HTTParty or Faraday might work:
#lookup_booking = HTTParty.get(user_bookings_url(1))
But this won't be authenticated, so it'll need the same authentication as any other request.
It's a little weird to do it this way unless whatever is generating the bookings is a separate service, or if you want it to be one. If you're going to be using one codebase, you might want to do something similar to what arieljuod suggested in the comments, and simply share the code.
You could break the BookingsController code into an ActiveSupport::Concern or a module, or a Service Object (or, more simply, a method on the User class) and that would then allow you to cleanly share the code between the BookingsController and HomeController. It might look something like this:
# app/services/lookup_user_bookings.rb
class LookupUserBookings
def self.bookings_as_json(user_id)
# complicated logic to find user bookings goes here...
bookings.as_json
end
end
# bookings_controller.rb
class BookingsController
def index
#bookings = LookupUserBookings.bookings_as_json(current_user)
render json: #bookings
end
end
# home_controller
class HomeController
def dashboard
#bookings = LookupUserBookings.bookings_as_json(current_user)
end
end
# dashboard.html.erb
<%= react_component("Booking", { booking: #bookings.to_json })%>

Rails ajax return model no-db field

My app is built on Rails 4.
I have a model mem which contains a custom field realname:
class Mem < ActiveRecord::Base
def realname
'hello'
end
end
Now,I post a ajax request form client to get the mems list,include the field realname:
def
render json: {Mem.all}
end
But the returned data didn't include realname,why? and how I can realize this? thanks!
When you pass a single model or a collection through, it uses to_json to format it. By default, this will grab its database attributes only. You can tell it to grab other data by doing this:
render json: Mem.all.to_json(methods: :realname)
Can read more up on how to use this method here: http://apidock.com/rails/ActiveRecord/Serialization/to_json. The linked method is deprecated, but the options are the same.

undefined method {....}_url when calling create in api controller

I'm working on a rails app that serves some json and I'm having hard time understanding what is going on in the code below (simplified for the purpose of understanding the issue).
module Api
class ProjectController < ApplicationController
respond_to :json
def show
x = {"id"=>17, "name"=>"abc", "version"=>1}
respond_with x.to_json, status: 200
end
def create
x = {"id"=>17, "name"=>"abc", "version"=>1}
respond_with x.to_json, status: 200
end
end
end
The show action works fine but when I call the create action I get
NoMethodError (undefined method '{"id":17,"name":"abc","version":1}_url' for
Api::ProjectsController:0x007fbb2294cd18)
Why do I get this error while show works just fine? is it because create makes a post instead of a get?
How can I solve it?
Thanks for your help and have a nice day.
The issue is that your controller is in a module (Api). That affects the namespace, and thus the routing; you must include that namespace as part of what you pass to respond_with.
Normally, I'd direct you to this stackoverflow answer (credit goes there). But since you are using a hash instead of a model, this answer might be more applicable.
Note how Rails is trying to call a ..._url method. The ... is your hash, in JSON format. You need to help Rails here on how to render.

Fetch Controller Action Response From Tasks and Models

I would love to call a controller action from within a model. Yes, MVC. Thanks.
So, why would i like to call a controller action and fetch the response?
Because my controller knows how to render the file I would like to cache. Why should I duplicate code to collect all data needed by my view?
Setup:
InvoicesController responds_to :html, :pdf
Invoice uses state_machine (:new -> :open -> paid)
What I "need" within the state transition from :new to :open
generate /invoices/:id.pdf
cache the PDF as Invoice#file for later use in delayed_job or simliar
What interface i would love to use elsewhere?
#invoice.build_pdf
Any suggestions?
Update:
I would like to cache the PDF as model attachement for later use (delayed_job mailing, etc)
It may be better to take the code you're using in your controller to render the PDF data, and create a module or class in your lib directory let's say lib/pdf.rb then use that code in both your controller and your model.
Update: You can cache the PDF itself on the filesystem as a file with a timestamp as it's name or something like that that is uniquely identifiable. Then add a file or invoice attribute to the model to store the location in the database for later use.
Update: You can use the block form of respond_to like so:
respond_to do |format|
format.html
format.pdf { InvoiceBuilder.create(#invoice) }
end
Update: It seems that ActionDispatch::Integration::Session is a class for creating and interface for integration testing. But I think you could exploit it for your purposes.
You should be able to use it like this:
# app/models/invoice.rb
class Invoice < ActiveRecord::Base
def get_body
app = ActionDispatch::Integration::Session.new(Rails.application)
app.get("invoices/#{self.id}.pdf")
app.response.body # this returns the rendered content that the browser will see.
end
end
Another way would be to grab the content by using curl i.e. curl http://localhost:3000/invoices/1.pdf and store the output.

Loading a page into memory in Rails

My rails app produces XML when I load /reports/generate_report.
On a separate page, I want to read this XML into a variable and save it to the database.
How can I do this? Can I somehow stream the response from the /reports/generate_report.xml URI into a variable? Or is there a better way to do it since the XML is produced by the same web app?
Here is my generate_report action:
class ReportsController < ApplicationController
def generate_report
respond_to do |format|
#products = Product.all
format.xml { render :layout => false }
end
end
end
Here is the action I am trying to write:
class AnotherController < ApplicationController
def archive_current
#output = # get XML output produced by /reports/generate_report
# save #output to the database
respond_to do |format|
format.html # inform the user of success or failure
end
end
end
Solved: My solution (thanks to Mladen Jablanović):
#output = render_to_string(:file => 'reports/generate_report.xml.builder')
I used the following code in a model class to accomplish the same task since render_to_string is (idiotically) a protected method of ActionController::Base:
av = ActionView::Base.new(Rails::Configuration.new.view_path)
#output = av.render(:file => "reports/generate_report.xml.builder")
Perhaps you could extract your XML rendering logic to a separate method within the same controller (probably a private one), which would render the XML to a string using render_to_string, and call it both from generate_report and archive_current actions.
What I typically do in this type of situation is to create a separate module/class/model to generate the report (it could even potentially be right in the Product model). This separate component could be in app/models or it could be in lib. In any case, once you have it extracted you can use it anywhere you need it. The controller can call it directly. You can generate it from the console. You can have a cron job generate it. This is not only more flexible, but it also can help smooth out your request response times if the report becomes slow to generate.
Since you are using a template it's understandable that the controller route is convenient, but even if you have to include some kind of ruby templating system in your auxiliary lib, it's still probably going to be less hassle and more flexible then trying to go through the controller.
#output = Product.all.to_xml
I'm sorry, is you question about Xml or about sessions? I mean is the fact that your action generates Xml material to the question? Or do you just want to save the output of the action for latter use?
You said on a "separate" page - you mean on another request? (like after user approved it?)
Why do you want to save the output? Because it should be saved exactly as rendered? (for example user can get frustrated if he clicked to save one report and you saved another)
Or is this thing expensive to generate?
Or may be, I got it wrong and it's about refactoring?

Resources