Is it a bug for method hash#to_json in Rails? - ruby-on-rails

I use rails 2.3.8
def index
#posts = Post.all
respond_to do |format|
format.html # index.html.erb
format.json { render :json => ({ :results => #posts.size, :rows => #posts.to_json(:only => [:id, :title, :click_count, :body])}).to_json }
end
end
the generated json data is:
{"rows":"[{\"title\":\"ruby\",\"body\":\"goood\",\"click_count\":1,\"id\":1},{\"title\":\"g\",\"body\":\"h\",\"click_count\":1,\"id\":2}]","results":2}
but in fact is shuld be:
{"rows":[{\"title\":\"ruby\",\"body\":\"goood\",\"click_count\":1,\"id\":1},{\"title\":\"g\",\"body\":\"h\",\"click_count\":1,\"id\":2}],"results":2}
is it a bug in rails?
and now how can to_json generate the expected json data for me?
Thanks!

Sorry,it was my fault.
the action code should be
def index
#posts = Post.all
respond_to do |format|
format.html # index.html.erb
format.json { render :json => ({ :results => #posts.size, :rows => #posts.map{|x| x.attributes}).to_json } }
end
end
That is to say: the value of key :rows must be an array object!
Thanks hoooopo!

Related

Rails: Post JSON to API Using Rails and HTTParty

I'm fairly new to API in Rails, and so I will need some assistance for the issue that I am facing.
All I want is to create a record on the database of the API through a POST request from my application.
That is to create a record on both databases (my database and the on the database of the API through a POST request from my application) whenever I create a book.
So this is what I've done so far:
For the app that will consume the API I am using the HTTParty gem.
I have tried to implement in my create action of the Books Controller using the code below:
#result = HTTParty.post(' https://www.pingme.com/wp-json/wplms/v1/user/register',
:body => { :name => '#{name}',
:author => '#{author}',
:description => '#{description}',
:category_id => '#{category_id}',
:sub_category_id => '#{sub_category_id}'}.to_json,
:headers => { 'Content-Type' => 'application/json', 'Authorization' => '77d22458349303990334xxxxxxxxxx' })
Here is my Books Controller for creating books
require 'httparty'
class BooksController < ApplicationController
include HTTParty
before_action :set_book, only: [:show, :edit, :update, :destroy]
before_action :authenticate_admin!, except: %i[show index]
skip_before_action :verify_authenticity_token
# GET /books
# GET /books.json
def index
#books = Book.search(params[:keywords]).paginate(:page => params[:page], :per_page => 9).order('created_at DESC')
end
# GET /books/1
# GET /books/1.json
def show
end
# GET /books/new
def new
#book = Book.new
end
# GET /books/1/edit
def edit
end
# POST /books
# POST /books.json
def create
#book = Book.new(book_params)
respond_to do |format|
if #book.save
format.html { redirect_to #book, notice: 'Book was successfully created.' }
format.json { render :show, status: :created, location: #book }
else
format.html { render :new }
format.json { render json: #book.errors, status: :unprocessable_entity }
end
end
#result = HTTParty.post(' https://www.pingme.com/wp-json/wplms/v1/user/register',
:body => { :name => '#{name}',
:author => '#{author}',
:description => '#{description}',
:category_id => '#{category_id}',
:sub_category_id => '#{sub_category_id}'}.to_json,
:headers => { 'Content-Type' => 'application/json', 'Authorization' => '77d22458349303990334xxxxxxxxxx' })
end
# PATCH/PUT /books/1
# PATCH/PUT /books/1.json
def update
respond_to do |format|
if #book.update(book_params)
format.html { redirect_to #book, notice: 'Book was successfully updated.' }
format.json { render :show, status: :ok, location: #book }
else
format.html { render :edit }
format.json { render json: #book.errors, status: :unprocessable_entity }
end
end
end
# DELETE /books/1
# DELETE /books/1.json
def destroy
#book.destroy
respond_to do |format|
format.html { redirect_to books_url, notice: 'Book was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_book
#book = Book.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def book_params
params.require(:book).permit(:name, :author, :description, :category_id, :sub_category_id)
end
end
But it still doesn't create these books on the database of the API through the post request when I create books.
Please any form of assistance will be highly appreciated.
Thank you.
Check you logs when you do the request, but I suspect you need to change your body to:
{
:book => {
:name => '#{name}',
:author => '#{author}',
:description => '#{description}',
:category_id => '#{category_id}',
:sub_category_id => '#{sub_category_id}'
}
}.to_json
Note that book key at the top is the difference.
Following contributions from #paulo-fidalgo and #tejasbubane, I found a working solution to the issue.
Here is the corrected HTTParty Post Request
#results = HTTParty.post(' https://www.pingme.com/wp-json/wplms/v1/user/register',
:body => {
:name => "#{#book.name}",
:author => "#{#book.author}",
:description => "#{#book.description}",
:category_id => "#{#book.category_id}",
:sub_category_id => "#{#book.sub_category_id}"}.to_json,
:headers => {
'Content-Type' => 'application/json',
'Authorization' => '77d22458349303990334xxxxxxxxxx'
}
)

Rails - How to render json with extra field in index controller method

I'm using Rails 4.0.2 with paperclip for image upload in my project. Also I need to send a full image path of paperclip. So I can do it with add new field and set image path manually in my show controller method for particular record.
show
def show
respond_to do |format|
format.html
format.json { :json => JSON::parse(#demo.to_json.merge("new_field" => #demo.image_url.url).to_json}
end
end
When I view Json for any of my record, this is will showing good.
{
id: "1",
name: "demo",
new_field: "/demo/1/original/file.jpg"
}
In same scenario, I need to get the full image path of paperclip image for all records when I am requesting to index method on controller
index
def index
#demos = Demo.all
respond_to do |format|
format.html
format.json { :json => Demo.all.to_json}
end
end
I tried some of codes, but I don't know how exactly to write
def index
#demos = Demo.all
#demos.each do |demo|
new_field = {"new_field" => #demo.image_url.url}
# After I stucked with logic, how to uppend with 'demo'.
end
respond_to do |format|
format.html
format.json { :json => Demo.all.to_json}
end
end
How do I iterate my individual Demo model and How to merge full image path into each record.
I found the solution for my question,
def index
#demos = Demo.all
#demos_data = []
#demos.each do |demo|
new_field = {"new_field" => #demo.new_field.url}
demo = JSON::parse(demo.to_json).merge(new_field)
#demos_data << demo
end
respond_to do |format|
format.html
format.json { :json => #demos_data}
end
end
I suggest you to use two approaches, 1)use active model serializer to expose json response. 2) use jbuilder library to expose custom fields. Still you need help please let me know.
Try this:-
def index
#demos = Demo.all
#demos_data = []
#demos.each do |demo|
demo["new_field"] = #demo.image_url.url
#demos_data << demo
end
respond_to do |format|
format.html
format.json { :json => #demos_data}
end
end
maybe you can try:
def index
#demos = Demo.all
#demos.map do |demo|
new_field = {"new_field" => #demo.image_url.url}
demo.attributes.merge(new_field)
end
respond_to do |format|
format.html
format.json { :json => #demos}
end
end
attributes method returns a hash of all the object attributes, just need merge new key-value into the returned hash.
Another way of doing it is in your controller where you are rendering the json
render json: #merchants,
include: {
offers: {
except: [:created_at, :updated_at],
include: {
categories: {
except: [:created_at, :updated_at]
}
}
},
location: {
methods: :country_name,
except: [:created_at, :updated_at]
}
},
except: [:created_at, :updated_at]
Note the methods: :country_name, there you can render methods from your model as json attributes. and through include: ... you can eager load and render related models.

Rails - Add index key for json parsed value

I am using rails 4.0.2. For my mobile api, I need to send JSON values to that. Here I face some problems to sending JSON . I want to customize my JSON with index key
For example, when I request index of my cities controller,
http:localhost:3000/cities.json
I got JSON value like this,
[
{"id":1,"name":"AAAA"},
{"id":2,"name":"BBBB"},
{"id":2,"name":"CCCC"}
]
But I want to surrounded with some named object or array.
{
"cities" :
[
{"id":1,"name":"AAAA"},
{"id":2,"name":"BBBB"},
{"id":2,"name":"CCCC"}
]
}
Now I tried in my controller,
def index
#cities = City.all
respond_to do |format|
format.html
format.json{ render :json => #cities.to_json(:methods => [:image_url]) }
# :methods => [:image_url] this is related to paperclip gem
end
end
Try this first as:-
def index
#cities = City.all
cit ={'cities' => #cities}
respond_to do |format|
format.html
format.json{ render :json => cit}
end
end
After that try as:-
def index
#cities = City.all
cit ={'cities' => #cities}
respond_to do |format|
format.html
format.json{ render :json => cit.to_json(:methods => [:image_url]) }
end
end

Include "distance_of_time_in_words" value in JSON output

I have an index action in my controller that I use to render all posts as JSON:
def index
#user = current_user
#posts = Post.all
end
respond_to do |format|
format.html
format.json {
render json: #posts.to_json(:include => {
:user => { :only => [:first_name, :last_name]},
:category => { :only => [:name, :id]}
}),
:callback => params[:callback]
}
end
end
What I'd like to do, is add an additional attribute to each post's JSON output called posted_on, that has a value of: distance_of_time_in_words(post.created_at, Time.now)}
I can't seem to be able to figure out how to approach this. Any help would be appreciated!
Add the method posted_on to Post model
class Post < ActiveRecord::Base
include ActionView::Helpers::DateHelper
def posted_on
distance_of_time_in_words(created_at, Time.now)
end
end
In your controller, pass the methods option to to_json
def index
#user = current_user
#posts = Post.all
end
respond_to do |format|
format.html
format.json {
render json: #posts.to_json(:include => {
:user => { :only => [:first_name, :last_name]},
:category => { :only => [:name, :id]}
}, :methods => :posted_on),
:callback => params[:callback]
}
end
end

current_user and Comments on Posts - Create another association or loop posts? - Ruby on Rails

I have created a blog application using Ruby on Rails and have just added an authentication piece and it is working nicely. I am now trying to go back through my application to adjust the code such that it only shows information that is associated with a certain user.
Currently, Users has_many :posts and Posts has_many :comments.
When a post is created I am successfully inserting the user_id into the post table. Additionally I am successfully only displaying the posts that belong to a certain user upon their login in the /views/posts/index.html.erb view. My problem is with the comments.
For instance on the home page, when logged in, a user will see only posts that they have written, but comments from all users on all posts. Which is not what I want and need some direction in correcting. I want only to display the comments written on all of the logged in users posts.
Do I need to create associations such that comments also belong to user? Or is there a way to adjust my code to simply loop through post to display this data.
I have put the code for the PostsController, CommentsController, and /posts/index.html.erb below and also my view code but will post more if needed.
class PostsController < ApplicationController
before_filter :authenticate
auto_complete_for :tag, :tag_name
auto_complete_for :ugtag, :ugctag_name
def index
#tag_counts = Tag.count(:group => :tag_name,
:order => 'count_all DESC', :limit => 20)
conditions, joins = {}, :votes
#ugtag_counts = Ugtag.count(:group => :ugctag_name,
:order => 'count_all DESC', :limit => 20)
conditions, joins = {}, :votes
#vote_counts = Vote.count(:group => :post_title,
:order => 'count_all DESC', :limit => 20)
conditions, joins = {}, :votes
unless(params[:tag_name] || "").empty?
conditions = ["tags.tag_name = ? ", params[:tag_name]]
joins = [:tags, :votes]
end
#posts= current_user.posts.paginate(
:select => "posts.*, count(*) as vote_total",
:joins => joins,
:conditions=> conditions,
:group => "votes.post_id, posts.id ",
:order => "created_at DESC",
:page => params[:page], :per_page => 5)
#popular_posts=Post.paginate(
:select => "posts.*, count(*) as vote_total",
:joins => joins,
:conditions=> conditions,
:group => "votes.post_id, posts.id",
:order => "vote_total DESC",
:page => params[:page], :per_page => 3)
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => #posts }
format.json { render :json => #posts }
format.atom
end
end
def show
#post = Post.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => #post }
end
end
def new
#post = Post.new
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #post }
end
end
def edit
#post = Post.find(params[:id])
end
def create
#post = current_user.posts.create(params[:post])
respond_to do |format|
if #post.save
flash[:notice] = 'Post was successfully created.'
format.html { redirect_to(#post) }
format.xml { render :xml => #post, :status => :created, :location => #post }
else
format.html { render :action => "new" }
format.xml { render :xml => #post.errors, :status => :unprocessable_entity }
end
end
end
def update
#post = Post.find(params[:id])
respond_to do |format|
if #post.update_attributes(params[:post])
flash[:notice] = 'Post was successfully updated.'
format.html { redirect_to(#post) }
format.xml { head :ok }
else
format.html { render :action => "edit" }
format.xml { render :xml => #post.errors, :status => :unprocessable_entity }
end
end
end
def destroy
#post = Post.find(params[:id])
#post.destroy
respond_to do |format|
format.html { redirect_to(posts_url) }
format.xml { head :ok }
end
end
end
CommentsController
class CommentsController < ApplicationController
before_filter :authenticate, :except => [:show, :create]
def index
#comments = Comment.find(:all, :include => :post, :order => "created_at DESC").paginate :page => params[:page], :per_page => 5
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => #comments }
format.json { render :json => #comments }
format.atom
end
end
def show
#comment = Comment.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => #comment }
end
end
# GET /posts/new
# GET /posts/new.xml
# GET /posts/1/edit
def edit
#comment = Comment.find(params[:id])
end
def update
#comment = Comment.find(params[:id])
respond_to do |format|
if #comment.update_attributes(params[:comment])
flash[:notice] = 'Comment was successfully updated.'
format.html { redirect_to(#comment) }
format.xml { head :ok }
else
format.html { render :action => "edit" }
format.xml { render :xml => #comment.errors, :status => :unprocessable_entity }
end
end
end
def create
#post = Post.find(params[:post_id])
#comment = #post.comments.build(params[:comment])
respond_to do |format|
if #comment.save
flash[:notice] = "Thanks for adding this comment"
format.html { redirect_to #post }
format.js
else
flash[:notice] = "Make sure you include your name and a valid email address"
format.html { redirect_to #post }
end
end
end
def destroy
#comment = Comment.find(params[:id])
#comment.destroy
respond_to do |format|
format.html { redirect_to Post.find(params[:post_id]) }
format.js
end
end
end
View Code for Comments
<% Comment.find(:all, :order => 'created_at DESC', :limit => 3).each do |comment| -%>
<div id="side-bar-comments">
<p>
<div class="small"><%=h comment.name %> commented on:</div>
<div class="dark-grey"><%= link_to h(comment.post.title), comment.post %><br/></div>
<i><%=h truncate(comment.body, :length => 100) %></i><br/>
<div class="small"><i> <%= time_ago_in_words(comment.created_at) %> ago</i></div>
</p>
</div>
<% end -%>
I believe you can set up another relation on user model
has_many :comments, :through => :posts
and then user #user.comments.
First, don't mess with MVC. Line:
Comment.find(:all, :order => 'created_at DESC', :limit => 3)
should be in controller:
#comments = Comment.find(:all, :order => 'created_at DESC', :limit => 3)
and in view:
<% #comments.each do |comment| %>
Or even better with partials:
<%= render :partial => "comment_item", :collection => #comments %>
It will iterate over all #comments.
Next, if you want to display all comments assigned to a post, then relation post has_many comments is enough. Use it like this:
# controller
#posts = current_user.posts(:include => :comments)
# view
<% #posts.each do |post| %>
<%=h post.title %> - Comments: <br />
<% post.comments.each do |comment| %>
<%=h comment.body %>
<% end %>
<% end %>
If you want to show only comments posted by current_user then all your comments should have user_id field filled. And use it in the same way as you are showing users posts.

Resources