Grails - Only get URL params - grails

I trying to make a dynamic URL mapping which you can pass any query within the URl. So at this moment in time I want to render the key/values of the query but without the controller,method and action information.
So when I pass person?name=Mark&age=60
it renders ['name':'Mark', 'age':'60', 'controller':'Person', 'method':'GET', 'action':'getPerson']
How am I able to get just the query's in the url and not the other information about the controllers etc.
UrlMappings
"/person"{
controller = "Person"
method = 'GET'
action = "getPerson"
}
PersonController
def getPerson(){
render params
}
I've done the following to remove them from the render by doing this:
HashMap search = params
String action = "action"
String controller = "controller"
String method = "method"
search.remove(action)
search.remove(controller)
search.remove(method)
render search

I think the best you can do is to remove the parameters that you know about and do not want to see:
def getPerson(){
render params.findAll { !(it.key in ['action', 'controller', 'method']) }
}

Related

How to set fake request object for rails controller object

I need to use render_to_string method outside the controller.
So I create controller instance like:
controller = class_name.constantize.new # Rails controller instance
controller.params = params
controller.action_name = 'index'
controller.create_global_current_user user
index_render_options = controller.send('index_render_options')
controller.send('render_to_string', index_render_options)
But it fails because request object inside is nil. Could you help me with it? how to create fake request for controller object?
SOLVED.
Added
controller.request = ActionDispatch::Request.new({})
resp = controller.class.make_response!(controller.request)
controller.set_response!(resp)
so now it looks like
controller = class_name.constantize.new
controller.params = params
controller.action_name = 'index'
controller.request = ActionDispatch::Request.new({})
resp = controller.class.make_response!(controller.request)
controller.set_response!(resp)
controller.create_global_current_user user
index_render_options = controller.send('index_render_options')
controller.send('render_to_string', index_render_options)
Based on this blog post the env parameter looks like
env = {
'REQUEST_METHOD' => 'GET',
'PATH_INFO' => '/hello',
'HTTP_HOST' => 'skylight.io',
# ...
}
Since Rails does not allow an empty map as the env parameter, the minimum code for creating a request object would be
request = ActionDispatch::Request.new({ "REQUEST_METHOD" => "GET" })

Adding a drop down in rails and do particular action

I am new to rails and this might me be a basic question. I checked on the internet but not able to find a simple example(might be my search is bad).
I need to create a form in my view and based the value selected and button clicking action i need to execute a particular action in my controller. I am able to create a drop down in my view using following lines.
= form_tag("/rtes", method: "get") do
= label_tag(:q, "Get Trip Type:")
= select_tag(:q, options_for_select({ "a" => "r4d_001", "b" => "r4d_002" })
<br>
= button_to( "Get Trip", :action => "rtes", :controller =>:q)
where rtes is my controller and i have mapped the value of the drop down values to the corresponding action names that needs to be executed when the button is clicked. This is my controller.
class RtesController < ApplicationController
##client=OptimusRtesServiceModel.new
def index
end
def r4d_001
result = ##client.r4t_001()
#driver_username = result.trip.loads[0].driver_details[0].driver_user_name
#driver_password = result.trip.loads[0].driver_details[0].driver_password
#trip_id = result.trip.trip_id
#carrier_username = result.carrier_details.carrier_user_name
#carrier_password = result.carrier_details.carrier_password
end
def r4d_002
result = ##client.r4t_002()
#driver_username = result.trip.loads[0].driver_details[0].driver_user_name
#driver_password = result.trip.loads[0].driver_details[0].driver_password
#trip_id = result.trip.trip_id
#carrier_username = result.carrier_details.carrier_user_name
#carrier_password = result.carrier_details.carrier_password
end
end
Now, if the first option in the drop down is selected and the button is clicked the "r4d_001" action in the controller needs to be executed. Your help is highly appreciated.
Rails is not like angular that you are trying to use :q variable to pass the value of selection to the button. You can create a drop-down menu instead of using select field to send them to different action for different option. Still if you want to use select field, then you need javascript to handle the front-end task here.
I have slightly modified my code to made it work. The idea is to get use the rails params and execute the corresponding action in my controller using case/when.
My view code:
= form_tag({:controller=>"r4d", :action=>"result"}, method: :get) do
= label_tag(:q, "Trip Type: ")
= select_tag(:q, options_for_select({"a" => "1", "b" => "2"})
= submit_tag("GetDetails")
My Contoller code:
class R4dController < ApplicationController
##client=ServiceModel.new
def r4d_result
result = case params[:q]
when "1"
##client.1
when "2"
##client.2
end
#value = result.value
end
end
By this way, i am able to pass the selected value of the drop down and execute the corresponding thing.

Grails list max results

I have the controller below that returns 100+ results and I want to be able to only pass 10 results with the json call and some sort of method that if more results are desired another request should be made but I'm not sure how to go about doing this.
Here's an except of my controller
def list(){
def results = Domain.list(max: 10)
withFormat {
json (render results as JSON)
}
}
Can someone point me in the write direction where I can read on documentation or see sample codes that might do this.
Thanks!
The default scaffolding templates would be a good place to look as they show how to do pagination in the list action. How about this:
def list(){
// max 10 unless something else was requested
if(!params.max) params.max=10
def results = Domain.list(params)
withFormat {
json (render results as JSON)
}
}
To request the next page of results you'd use .../list?offset=10&max=10, for the next use offset=20, etc.
Refer docs for list() method on how pagination parameters work.
Try this,
def c = Domain.createCriteria()
def results = c.list(max: 10, offset: 10) {
order("some", "desc")
}
withFormat {
json { render results as JSON }
}
Refer

without template, need to render not return json

I'm trying to make code from a Sinatra app work in the Rails context. The Sinatra app uses ajax requests to trigger the Sinatra routes/controller actions. For example, if you trigger the new function on a javascript model
new: function() {
var _this = this;
$.ajax({
url: "/gamestart",
type: "POST",
....
It will trigger the route/controller code in the Sinatra app
post "/new" do
end
When I tried to make this work in Rails, I'm getting a 500 internal server error. In my Rails app, the new_game button triggers an ajax request to a Rails route which triggers a controller action, and that controller action uses the Rails model to get data from the database. For some reason that doesn't seem like the right way to do it in Rails, and I'm wondering if it's the reason I'm getting the server error
GET http://localhost:3000/gamestart 500 (Internal Server Error)
If possible, can you tell me where in the chain of actions outlined below that error is arising and what I might do to fix it.
1 Click on the new game button triggers 'startNewGame' method
'click #new_game': 'startNewGame',
2 The startNewGame method calls method on Game model
startNewGame: function() {
this.model.new();
},
3 The new method in the Game model makes a GET request to the url '/gamestart'. I also tried a post request. I don't know why it would need to be a post request, but neither worked. (In the original Sinatra application, the gamestart url led immediately into the function post '/gamestart' do...)
new: function() {
var _this = this;
$.ajax({
url: "/gamestart",
type: "GET", \\\ also tried POST
success: function(response) {
var json = $.parseJSON(response);
_this.set({lost: false});
_this.set({win: false});
_this.trigger("gameStartedEvent", json);
}
})
},
4 I directed the url to a controller action in Rails router file
match 'gamestart' => 'locations#gamestart', :via => :get
Note, in the original Sinatra application, the route and the controller action were combined
5 The gamestart method of the locations_controller.rb
def gamestart
word = Word.get_random
masquerade_word = Word.masquerade(word)
session[:word] = word
session[:incorrect_guesses] = 0
session[:chars_left] = word.size
session[:revealed_word] = masquerade_word
{:word => masquerade_word}.to_json
end
6 The get_random method on the word model Word.rb, which is called from locations controller
def get_random
words = []
locations = Location.all (this pulls up the names of the locations from the db)
locations.each do |e|
words << e.name
end
words.sample
end
ERROR MESSAGE
GET http://localhost:3000/gamestart 500 (Internal Server Error) jquery.js:8215
XHR finished loading: "http://localhost:3000/gamestart". jquery.js:8215
send jquery.js:8215
jQuery.extend.ajax jquery.js:7767
window.Game.Backbone.Model.extend game.js:27
window.OptionsView.Backbone.View.extend.startNewGame optionsView.js:14
jQuery.event.dispatch jquery.js:3062
elemData.handle.eventHandle
Note, in the original Sinatra application, the route and the controller action were combined in the usual Sinatra way
post "/gamestart" do
word = Word.get_random
masquerade_word = Word.masquerade(word)
session[:word] = word
session[:incorrect_guesses] = 0
session[:chars_left] = word.size
session[:revealed_word] = masquerade_word
{:word => masquerade_word}.to_json
end
UPDATE
The 500 error seemed to be triggered by a missing template. This method in locations controller wasn't rendering anything. It didn't have a view file. I therefore changed the controller to make it respond_to :json and then use respond_with at the end of the action, but that triggered a 406 error.
def gamestart
word = Word.get_random
masquerade_word = Word.masquerade(word)
session[:word] = word
session[:incorrect_guesses] = 0
session[:chars_left] = word.size
session[:revealed_word] = masquerade_word
{:word => masquerade_word}.to_json
end
became now triggers 406 error
respond_to :json
def gamestart
word = Word.get_random
masquerade_word = Word.masquerade(word)
session[:word] = word
session[:incorrect_guesses] = 0
session[:chars_left] = word.size
session[:revealed_word] = masquerade_word
plainvariable = {:word => masquerade_word}.to_json ###changed
respond_with plainvariable ###changed
end
You say that your gamestart controller method is causing a server error due to a missing template. If we look at that controller method:
def gamestart
#...
{:word => masquerade_word}.to_json
end
we see that it returns a JSON string but it neglects to render anything. You don't call any rendering or redirection methods so Rails helpfully (ha ha) assumes that you want to render the gamestart view template; but, you have no such thing so you get an error.
You should render your JSON, not return it; something more like this:
def gamestart
#...
render :json => { :word => masquerade_word }
end

Grails redirect breaks params types

My Grails code has a search function that redirects to another controller action after performing a findAllBy query:
def results = Foo.findAllByBar(baz)
redirect(action: "result", params: [results: results])
findAllByBar returns an ArrayList with models, as expected, but after the redirect the receiving action gets a String array. Worse, when there is only one result it doesn't even get an array, it just gets a String.
Given that I have to iterate through the results in the receiving view, doing it on a string will meticulously print every letter individually. We can all agree that that's probably not the ideal behaviour.
A redirect results in a new GET request with the parameters in the querystring, e.g. /controller/result?foo=bar&baz=123 - you can't put objects there since it's just a string.
You could put the ids of the objects in the params and load them in the result action:
def action1 = {
def results = Foo.findAllByBar(baz)
redirect(action: "result", params: [resultIds: results.id.join(',')])
}
def result = {
def resultIds = params.resultIds.split(',')*.toLong()
def results = Foo.getAll(resultIds)
}
or put them in Flash scope:
def action1 = {
flash.results = Foo.findAllByBar(baz)
redirect(action: "result")
}
def result = {
def results = flash.results
}
It sounds like you want to use the chain method instead of the redirect method. Chain lets you pass a model as a parameter similar to render.
An example would be:
chain(action:'result',model:[results:results])
Heres a link for further information:
http://www.grails.org/doc/latest/ref/Controllers/chain.html

Resources