Setup:
Rails 5, using ActiveModel::Serializer and Kaminari
Sample code:
def index
#catalogs = Catalog.page(params[:page])
render json: #catalogs, adapter: :json_api
end
Problem:
When params[:page] is nil, my result is as expected:
{
"data": [
{
"id": "a020ab21-9028-4bfd-8f9c-1b735ed4734b",
"type": "catalogs",
"attributes": {
"name": "First",
"locale": "en"
}
}
],
"links": {
"self": "http://localhost:3000/v1/catalogs?page%5Bnumber%5D=1&page%5Bsize%5D=1",
"next": "http://localhost:3000/v1/catalogs?page%5Bnumber%5D=2&page%5Bsize%5D=1",
"last": "http://localhost:3000/v1/catalogs?page%5Bnumber%5D=3&page%5Bsize%5D=1"
}
}
However, when I make a Postman call to the "next" URL (http://localhost:3000/v1/catalogs?page%5Bnumber%5D=2&page%5Bsize%5D=1),
I get:
Started GET "/v1/catalogs" for 172.18.0.1 at 2017-09-08 15:27:04 +0000
undefined method `to_i' for #<ActionController::Parameters:0x0000c68977f718>
Did you mean? to_s
to_h
Is there something different that has to be done with Rails 5 params to get pagination for ActiveModel::Serializers to work?
It appears params[:page] doesn't hold the page number, but a "hash": { number: 1, size: 1 }. That said, you want to use the page number as the argument to page:
def page_params
params.fetch :page, {}
end
#catalogs = Catalog.page(page_params[:number])
Maybe even call .per(page_params[:size]) too, to let the API change that as well.
Solution:
I encountered nested pagination params problematic in some cases. You could use params page and per_page instead of page[number] and page[size]. Solution for will_paginate is in this comment on GitHub issue. Solution is probably also for kaminari, because its the issue of serialization gem, not pagination gem.
Explanation:
As Leonel explained, link that you are clicking:
localhost:3000/v1/catalogs?page%5Bnumber%5D=2
is the same as:
localhost:3000/v1/catalogs?page[number]=2
so your code should access these params like:
params[:page][:number]
not:
params[:page]
Solution from above, using ActiveModel::Serializers will create links like this:
localhost:3000/v1/catalogs?page=2&per_page=50
So you can access pagination params like you want in your controller:
Catalog.page(params[:page]) # page number
Catalog.per(params[:per_page]) # page size
Related
I am using Rails 5 and Jquery Datatables in an app. I have a filter field on my datatable. I would like for people to be able to click on a tag and get the main table filtered by that tag. The seemingly easy way to do it would be to have a link_to like this:
posts_path(post: {filter: 'science'})
This creates the URL like:
http://localhost:3000/posts?post%5Bfilter%5D=science
I then have this in my controller index action:
#posts = Post.all
#filter = post_params[:filter]
Which is using the standard strong params method like:
def post_params
params.require(:post).permit(:filter ...)
end
Then in the Javascript for the DataTable I did this:
$('#posts_table').dataTable({
saveState: true,
"lengthMenu": [[10, 25, 50, -1], [10, 25, 50, "All"]],
search: {
search: '<%= #filter %>'
},
"columnDefs": [
{
"targets": [-1],
"visible": false,
"searchable": true
}
]
});
It works great!! The page loads with the table filtered on the search term and the term appears in the search text box. But when I call the index page without any params it fails:
http://localhost:3000/posts
ActionController::ParameterMissing at /posts
param is missing or the value is empty: post
which I get because no params are being sent but I'm calling the params method. Is there a simple way to fix this? i.e. could I make the default route have a default param with an empty string as the search term? What would the correct syntax for that route be? I was thinking I could set the params to '' if undefined but not sure how to do that with Strong Params in a way that would not compromise security.
Well I knew this couldn't be too difficult and was probably over-thinking it. I finally just made a route for tags thus:
get 'tags', to: 'posts#by_tag', as: :filter, :constraints => { :filter => /[^\/]+/ }
Then I added a controller action called by_tag like this:
def by_tag
#posts = Post.all
#filter = post_params[:filter]
respond_to do |format|
format.html { render :index }
end
end
then to create my tag links I just do something like:
tags.each do |t|
link_to t.name, filter_path(post: {filter: t.name})
end
which creates links like:
http://localhost:3000/tags?post%5Bfilter%5D=9500XL
And with the JavaScript mentioned in the question I get the table loaded with the filter applied in the search field.
Something about the by_tag action feels a bit un-rails-ish. I feel like I'm missing something simple that would allow me to leverage the index action for this. If anyone has a better answer I'd love to know.
I'm showing a database table using ruby on rails, and I'd like to sort the columns whenever I click on the column title.
My idea was to call an angularjs function that adds a parameter like ?sort_by=id to the url and get it back in my ruby controller to do the ordered request, but it seems like it's not working.
Here is the part of my users.html.haml file which calls the function :
%th
%button{'ng-click': 'sort_by("product")'}
My angularjs function (users.controller.js) :
$scope.sort_by = function(str){
$location.search({sort_by: 'product'});
$window.location.reload();
}
And index function in users_controller.rb
def index
#users = User.all
max_per_page = 50
puts params
paginate #users.size, max_per_page do |limit, offset|
render json: #users.limit(limit).offset(offset)
end
end
When I click on the button, it reloads the page with ?sort_by=product in the url and calls the index function, but puts params still returns {"subdomain"=>"admin", "controller"=>"admin/users", "action"=>"index"}
How can I make this work ?
Thanks in advance.
I used an $http get request with params and it finally worked, in case it helps someone :
$http({
method: 'GET',
url: 'users',
dataType: 'json',
params: {
'sort_by': 'products.name',
},
headers: {
"Content-Type": "application/json"
}
}).success(function(response){
console.log('succes');
}).error(function(error){
console.log('err');
});
and then in index method in users_controller.rb :
puts request.params['sort_by'] if !request.params['sort_by'].nil?
I have a simple hash containing a description:
simple_hash = { "description" => "<p>This is my description.</p>" }
I need write this out to a file. In the file, this hash needs to be represented as JSON:
simple_hash.to_json
But this is what I'm actually getting:
"{\"description\":\"\\u003cp\\u003eThis is my description.\\u003c/p\\u003e\"}"
How do I stop to_json from doing that to my HTML tags?
to_json is doing the safe thing encoding the HTML.
If you are certain that it is safe, you can easily decode with Rails' JSON.parse
>> JSON.parse "{\"desc\":\"\\u003cp\\u003eThis is my description.\\u003c/p\\u003e\"}"
=> {"desc"=>"<p>This is my description.</p>"}
You could try doing this:
def json_template(id)
###################
# Method to create the json templates
###################
File.open("test_dir/temp_file.json", 'w'){|i|
templ = %Q<{
"id": "#{Random.rand(20)}#{Random.rand(20)}",
"version": 1.0.1.1,
"service": "ftp",
"os": "linux"
}>
i.write(templ)
}
end
I have controller method which perform some actions for POST sent json params, but the problem is when I try to send json params without json header.
I would like to handle this exception by converting string to json and here the problems begins.
Let's say that I have simple JSON like this:
{"menu": {
"id": "file",
"value": "File",
"popup": {
"menuitem": [
{"value": "New", "onclick": "CreateNewDoc()"},
{"value": "Open", "onclick": "OpenDoc()"},
{"value": "Close", "onclick": "CloseDoc()"}
]
}
}}
I have placed binding.pry at the begining of method and I try to get params from there. I figured out that request.body will return me object #<StringIO:0x007fbdb5cf9fe8> which I have to convert to JSON.
First thing I have tried was:
[1] pry(#<TestController>)> request.body.to_json
=> "[\"{\\\"menu\\\": {\\n\",\" \\\"id\\\": \\\"file\\\",\\n\",\" \\\"value\\\": \\\"File\\\",\\n\",\" \\\"popup\\\": {\\n\",\" \\\"menuitem\\\": [\\n\",\" {\\\"value\\\": \\\"New\\\", \\\"onclick\\\": \\\"CreateNewDoc()\\\"},\\n\",\" {\\\"value\\\": \\\"Open\\\", \\\"onclick\\\": \\\"OpenDoc()\\\"},\\n\",\" {\\\"value\\\": \\\"Close\\\", \\\"onclick\\\": \\\"CloseDoc()\\\"}\\n\",\" ]\\n\",\" }\\n\",\"}}\"]"
but it is not result I was looking for.
I have tried several more converts, like:
[2] pry(#<TestsController>)> ActiveSupport::JSON.decode(request.body.to_json)
=> []
Which also gives me nothing. I'm out of ideas.
I would like to achive something like this:
[1] pry(#<TestsController>)> params
=> {"menu"=>{"id"=>"file", "value"=>"File", "popup"=>{"menuitem"=>[{"value"=>"New", "onclick"=>"CreateNewDoc()"}, {"value"=>"Open", "onclick"=>"OpenDoc()"}, {"value"=>"Close", "onclick"=>"CloseDoc()"}]}},
"controller"=>"payments",
"action"=>"notification",
"payment"=>{"menu"=>{"id"=>"file", "value"=>"File", "popup"=>{"menuitem"=>[{"value"=>"New", "onclick"=>"CreateNewDoc()"}, {"value"=>"Open", "onclick"=>"OpenDoc()"}, {"value"=>"Close", "onclick"=>"CloseDoc()"}]}}}}
This is scenario for sending params with json header. In that can I can easily iterate through them.
You should use JSON.parse on request.body.read
request.body.rewind
JSON.parse(request.body.read)
Have you tried
request.body replaced with env['rack.input'].read
json = JSON.parse env['rack.input'].read
OR
json = JSON.parse request.body.read
as stated here
Since you are using RoR, if you are using Action Controller Parameters you can use the to_hash method and then use to_json (which is only on RoR, not part of Ruby) to convert your permitted params, like this:
params.permit(:attributes).to_hash.to_json
In my case this worked:
JSON.parse(params[:attributes].to_json)
This seems like it should be pretty simple but I can't seem to figure this out (bit new to Ember). I have a json file that I am using to load data into an ember template with handlebars. I am trying to have it so by visiting /site/id/ it would load data based on the id in the json file. Right now, it loads my json but only takes data from the first json entry. So no matter what /site/anythinghere/, that data is loaded. I am trying to make it dynamic.
I think my problem has to do with not having a model setup properly in ember. I've seen the docs and tutorials talk about the model: property and passing (params) and what not, but I can't seem to get anything to work in my site model or route.
Here's my code...
Router.js.coffee:
App.Router.map ->
#resource 'site', ->
#route 'show', path: '/:site_id'
Routes.rb:
#routes.rb
Rails.application.routes.draw do
root to: 'home#index'
scope constraints: { format: :html } do
get '/site' => 'home#index'
get '/site/:site_id' => 'home#index'
resources 'sites'
end
end
sites_controller.rb:
class SitesController < ApplicationController
def show
render json: File.open('data/sites.json').read
end
end
models/site.js.coffee:
App.Site = DS.Model.extend()
data/sites.json
{
"site": [
{
"id": "1",
"name": "Place One",
"type": "Type One"
}, {
"id": "2",
"name": "Place One",
"type": "Type Two"
}
]
}