Basic usage of Jbuilder in a controller - ruby-on-rails

In a Rails (5.2) app, I'm trying to use JBuilder to return some JSON as response.
I have added JBuilder in my Gemfile.
# Gemfile
...
gem 'jbuilder', '~> 2.5'
...
# Gemfile.lock
...
jbuilder (2.8.0)
...
According to JBuilder documentation:
You can also extract attributes from array directly.
#people = People.all
json.array! #people, :id, :name
=> [ { "id": 1, "name": "David" }, { "id": 2, "name": "Jamie" } ]
Now, in my controller, I have added the following:
def index
respond_to do |format|
format.json { render json.array! User.all, :email, :full_name }
end
end
But I get
NameError - undefined local variable or method `json' for
UsersController:0x00007fe2f966f150 16:55:40 rails.1
| => Did you mean? JSON:
Am I missing anything here?

You typically use jbuilder in a view file with the extension .json.jbuilder
in your controller:
def index
#users = User.all
respond_to do |format|
format.json
end
end
in your app/views/users/index.json.jbuilder
json.array! #users, :email, :full_name
EDIT: you can do it from the controller like that as well:
format.json { render json: Jbuilder.new { |json| json.array! User.all, :email, :full_name }.target! }

You can do:
def index
Jbuilder.new do |json|
json.array! User.all, :email, :full_name
end.attributes!
end
(It worked for me)
Source
Side note
You might see people doing:
Jbuilder.encode do |json|
# ...
end
But Jbuilder.encode actually returns a string, as specified by this comment in the source code

Related

Rails: undefined method `any?' for nil:NilClass

I'm getting these params in the console:
category_id" # => ["", "14", "16", "17"]
When I check if the array is empty or not inside a model with this:
#category = category_id.any?
it returns the following error:
undefined method `any?' for nil:NilClass
I have the strong params like this:
:category_id => []
Any ideas on how to check if this array is empty or not?
def new
#account = Account.new
#categories.build
end
def create
#account = Account.new(account_params)
respond_to do |format|
if #account.save
format.html { redirect_to accounts_path, notice: 'Account was successfully added.' }
format.json { render :index, status: :created, location: #account }
else
format.html { render :new }
format.json { render json: #account.errors, status: :unprocessable_entity }
end
end
end
def account_params
params.require(:account).permit(:name, :street, :street_2, :city, :state, :postal_code, :country, category_id: []
end
console results:
"account"=>{"name"=>"", "street"=>"", "street_2"=>"", "city"=>"", "state"=>"", "postal_code"=>"", "country"=>"", "category_id"=>["", "14", "15"]
#variables are not available inside the model. Neither are the params.
It's good practice to not try to do so but here are some ways to do it:
If you want to pass a value that is a database field/column:
# In the controller
account = Account.new(:category => #category)
# In this example, :category is a database field/column of the
# Account model, not sure if that's accurate for you.
If not then you gotta use an attr_accessor like this:
# In the model
class Account < ActiveRecord::Base
attr_accessor :category
#In the controller (this is equivalent to the first example)
account = Account.new
account.category = #category
Using attr_accessor doesn't save anything into the database, but the controller doesn't know the difference (which is good). This allows you to move data around like you wanted.

How to override how my classes property is rendering the JSON for my order model

I am currently doing this in my controller action:
render json: order.to_json(:include => [items: {include: [products: {include: [:inventory]}]}])
Now I am moving this over to a richer object like this:
class OrderResponse
attr_accessor :success, :errors, :order, :users
def initialize(success, errors)
#success = success
#errors = errors
end
end
The problem now is that it doesn't render all the inner data of the order model.
The json now has only the order attributes since I am not calling the :includes now.
response = OrderResponse.new(true, [])
response.order = order
response.user = user
render json: response
How can I override how the order is being converted to json now?
Just like you can create .html templates using the ERB templating language (or others), you can use the jbuilder DSL to render complex json responses.
OrdersController:
#response = OrderResponse.new(true, [])
render :response, formats: [:json]
views/orders/response.json.jbuilder
json.call #response, :id
json.user do
json.extract! #response.user, :id, :created_at # ...
end
json.order do
json.extract! #response.order, :id, :created_at # ...
end

Trouble getting Rails API to display 'show' record

I have a rails 5.2 API. I'm able to display the list of 'blogs' at site.com/blogs.json but when I try to show an individual blog at site.com/blogs/blog-1.json I get a 500 server error.
blogs_controller.rb
def show
#blog.punch(request)
#meta_title = meta_title #blog.title
respond_to do |format|
format.html
format.json
end
end
I'm using jbuilder in the Rails view directory, and it's here I suspect I have something wrong, but can't see what.
_blog.json.jbuilder
json.array! #blogs, :id, :title, :text, :created_at, :updated_at, :url
json.url blog_url(blog, format: :json)
index.json.jbuilder
json.array! #blogs
show.json.jbuilder
json.partial! "blogs/blog", blog: #blog

How do I get my controller to return just 2 attributes of an object in a json response?

So I would like to just get back the :id, :name attributes of my #neighborhood object in my json response.
This is my action in my controller:
def autocomplete_neighborhood_name
#neighborhood = Neighborhood.select("id, name").where("name LIKE ?", "#{params[:name]}%").order(:name).limit(10)
respond_to do |format|
format.json { #neighborhood :only => [:id, :name]}
end
end
I am getting a syntax error on the format.json... line.
How do I do accomplish what I want?
Thanks.
Edit 1
My real goal is to try and refactor this code, to use format.json and use the newer methods of Rails 3.2.x:
def autocomplete_neighborhood_name
respond_with(
Neighborhood.
select("id, name").
where("name LIKE ?", "#{params[:name]}%").
order(:name).
limit(10).
as_json(:only => [:id, :name]))
end
If you have any other suggestions for how I might do this better, I would appreciate the feedback.
Try this:
format.json { render json: #neighborhood , :only => [:id, :name] }

Rails Validation with ActiveModel

I've been going through the documentation for getting ActiveRecord validation working with ActiveModel. For some reason I am not seeing any validation results returned.
I have a set of models which instead of interfacing with ActiveRecord are interfacing through a custom API that will be sitting behind Rails.
The Model:
class ApiObject < ApiConnector
include ActiveModel::Validations
attr_accessor :fieldName
validates :fieldName, :presence => true
def save
#save method implementation
end
end
The Controller:
def create
#apiObject = ApiObject.new(params[:api_object])
respond_to do |format|
if #apiObject.save
format.html { redirect_to(#apiObject, :notice => 'User was successfully created.') }
format.xml { render :xml => #apiObject, :status => :created, :location => #apiObject }
else
format.html { render :action => "new" }
format.xml { render :xml => #apiObject.errors, :status => :unprocessable_entity }
end
end
end
The Form:
<%= form_for :api_object, :url => '/apiobjectcontroller/' do |f| %>
<%= f.label :fieldName, 'Field Name' %>
<%= f.text_field :fieldName %>
<%= f.submit 'Create'%>
<% end %>
I am following the code laid out here: Rails ActiveModel Validation
The method is correctly returning to the form because #apiObject.save is returning as false, but no validation response is coming back. I've checked the markup and the usual rails validation results are not returned. What am I missing?
I have similar code that works, but I have an initialize method in my classes. Perhaps your model should be:
class ApiObject < ApiConnector
include ActiveModel::Validations
validates :fieldName, :presence => true
attr_accessor :fieldName
def initialize(fieldName)
#first_name = fieldName
end
def save
return false unless valid?
# save method implementation to go here
# ...
true # if save successful, otherwise, false
end
end
If the above works, and you end up having a lot of attributes to assign in your initializer, then you could use this old trick:
def initialize(attributes = {})
attributes.each do |name, value|
instance_variable_set "##{name}", value
end
end
EDIT: Added a call to valid? in save implementation, so that errors collection will be filled out.
This should fully answer:
http://asciicasts.com/episodes/211-validations-in-rails-3
In a nutshell: create your form with an instance variable + add the necessary code to display your errors.

Resources