Pass parameters from as_json to model - ruby-on-rails

Controller:
user = User.find(params[:id])
respond_with({:posts => #posts.as_json})
Model:
def as_json(options = {})
{
name: self.name,
...
}
end
I want to pass parmeters like params[:id] to the as_json function to change things in the JSON display.
How can I do it?

Well, as_json does take an options hash, so I suppose you could call it using
respond_with({:posts => #posts.as_json(:params => params)})
You'd then be able to reference the params in the definition of as_json:
def as_json(options = {})
params = options[:params] || {}
{
name: self.name,
params_id: params[:id]
...
}
end

Related

Update only params that exist rails

I'm aware I can do something like this:
#object.update_attributes(date: params[:date]) if params[:date].present?
#object.update_attributes(date: params[:name]) if params[:name].present?
#object.update_attributes(date: params[:thing]) if params[:thing].present?
#object.update_attributes(date: params[:item]) if params[:item].present?
But is there a way to do all of this at once?
Something like:
#object.update_attributes(object_params)
where it won't put nulls in my database if they aren't passed in through the url. So I can call
Object.update(date: Date.today, name: "ryan") and it will only update these items.
You can try this
#object.update_attributes(params)
Also, it's a good practice to use strong params, putting this in the end of your controller
private
def object_params
params.require(:object).permit(:date, :name, :item, :thing)
end
end
and use it as
#object.update_attributes(object_params)
Just select params which are not nil in your controller and use #object.update_attributes(object_params) as usual
def object_params
params.require(:object).permit(:date, :name, :thing, :item).select { |k, v| !v.nil? }
end
Try this:
keys = [:date, :name, :item, :thing]
object_params = params.slice(*keys).delete_if { |k,v| v.nil? }
#object.update_attributes(object_params)
You could also do this if the param keys match the model columns:
object_params = params.slice(*Model.column_names).delete_if { |k,v| v.nil? }
#object.update_attributes(object_params)

Update array with strong parameters Rails 4

I'm receiving a JSON object and nested array via a Rails 4 api with params like so:
{
"token" => "123"
"lessons" => [
{
"token_id" => "j12l3n123",
"attr_1" => "hello",
"attr_2" = "is it me you're looking for"
},
{
"token_id" => "j12l",
"attr_1" => "Nope",
"attr_3" = "You're not."
}
]
}
And I have a controller like so:
def update_all
#fetch collection with one db hit
token_ids = params[:lessons].map{|l| l[:token_id]}
#lessons = Lesson.where(token_id: token_ids)
params[:lessons].each do |l|
lesson = #lessons.detect { |lesson| lesson.token_id == l[:token_id] }
# How do I update the record with strong params?
lesson.update_attributes(lesson_params)
end
end
private
def lesson_params
params.permit(
:attr_1,
:attr_2,
:attr_3
)
end
How do i update each record with the right object in the array, and use strong parameters to do so?
def update_all
lesson_params.each do |l|
lesson = Lesson.where(token_id: l[:token_id]).first
lesson.update_attributes(l)
end
end
private
def lesson_params
params.require(:lessons).map do |l|
ActionController::Parameters.new(l.to_hash).permit(
:attr_1,
:attr_2,
:attr_3
)
end
end
def lesson_params
params.permit(:token, lessons: [:token_id, :attr_1, :attr_2, :attr_3 ])
end
in Controller something like following
def update_all
lesson_params[:lessons].each do |lesson_param|
lesson = Lesson.find(lesson_param[:token_id])
lesson.update_attributes(lesson_param)
end
end

How does Rails params parse hash from string

I'm learning Ruby on Rails and got curious how the params method works. I understand what it does, but how?
Is there a built-in method that takes a hash string like so
"cat[name]"
and translates it to
{ :cat => { :name => <assigned_value> } }
?
I have attempted to write the params method myself but am not sure how to write this functionality in ruby.
The GET parameters are set from ActionDispatch::Request#GET, which extends Rack::Request#GET, which uses Rack::QueryParser#parse_nested_query.
The POST parameters are set from ActionDispatch::Request#POST, which extends Rack::Request#POST, which uses Rack::Multipart#parse_multipart. That splays through several more files in lib/rack/multipart.
Here is a reproduction of the functionality of the method (note: this is NOT how the method works). Helper methods of interest: #array_to_hash and #handle_nested_hash_array
require 'uri'
class Params
def initialize(req, route_params = {})
#params = {}
route_params.keys.each do |key|
handle_nested_hash_array([{key => route_params[key]}])
end
parse_www_encoded_form(req.query_string) if req.query_string
parse_www_encoded_form(req.body) if req.body
end
def [](key)
#params[key.to_sym] || #params[key.to_s]
end
def to_s
#params.to_s
end
class AttributeNotFoundError < ArgumentError; end;
private
def parse_www_encoded_form(www_encoded_form)
params_array = URI::decode_www_form(www_encoded_form).map do |k, v|
[parse_key(k), v]
end
params_array.map! do |sub_array|
array_to_hash(sub_array.flatten)
end
handle_nested_hash_array(params_array)
end
def handle_nested_hash_array(params_array)
params_array.each do |working_hash|
params = #params
while true
if params.keys.include?(working_hash.keys[0])
params = params[working_hash.keys[0]]
working_hash = working_hash[working_hash.keys[0]]
else
break
end
break if !working_hash.values[0].is_a?(Hash)
break if !params.values[0].is_a?(Hash)
end
params.merge!(working_hash)
end
end
def array_to_hash(params_array)
return params_array.join if params_array.length == 1
hash = {}
hash[params_array[0]] = array_to_hash(params_array.drop(1))
hash
end
def parse_key(key)
key.split(/\]\[|\[|\]/)
end
end

Define several json formats for model

In my rails app i defined a specific JSON-Format in my model:
def as_json(options={})
{
:id => self.id,
:name => self.name + ", " + self.forname
}
end
And in the controller i simply call:
format.json { render json: #patients}
So now im trying to define another JSON-Format for a different action but i dont know how?
How do i have to define another as_json or how can i pass variables to as_json? Thanks
A very ugly method but you can refactor it for better readability:
def as_json(options={})
if options.empty?
{ :id => self.id, :name => self.name + ", " + self.forname }
else
if options[:include_contact_name].present?
return { id: self.id, contact_name: self.contact.name }
end
end
end
Okay, I should give you a better piece of code, here it is:
def as_json(options = {})
if options.empty?
self.default_json
else
json = self.default_json
json.merge!({ contact: { name: contact.name } }) if options[:include_contact].present?
json.merge!({ admin: self.is_admin? }) if options[:display_if_admin].present?
json.merge!({ name: self.name, forname: self.forname }) if options[:split_name].present?
# etc etc etc.
return json
end
end
def default_json
{ :id => self.id, :name => "#{self.name}, #{self.forname}" }
end
Usage:
format.json { render json: #patients.as_json(include_contact: true) }
By defining hash structure by 'as_json' method, in respective model class i.e User model in (Example 1), it becomes the default hash stucture for active record(i.e., user) in json format. It cannot be overridden by any inline definitions as defined in Example: 2
Example 1:
class User < ActiveRecord::Base
.....
def as_json(options={})
super(only: [:id, :name, :email])
end
end
Example: 2
class UserController < ApplicationController
....
def create
user = User.new(params[:user])
user.save
render json: user.as_json( only: [:id, :name] )
end
end
Therefore, in this example when create action is executed 'user' is returned in ("only: [:id, :name, :email]") format not as ("only: [:id, :name]")
So, options = {} are passed to as_json method to specifiy different format for different methods.
Best Practice, is to define hash structure as constant and call it everwhere it is needed
For Example
Ex: models/user.rb
Here, constant is defined in model class
class User < ActiveRecord::Base
...
...
DEFAULT_USER_FORMAT = { only: [:id, :name, :email] }
CUSTOM_USER_FORMAT = { only: [:id, :name] }
end
Ex: controllers/user.rb
class UserController < ApplicationController
...
def create
...
render json: user.as_json(User::DEFAULT_USER_FORMAT)
end
def edit
...
render json: user.as_json(User::CUSTOM_USER_FORMAT)
end
end
Cool!

How to get attribue in the to_json attribute

I am using Rails 3 and here is my model
class LineItem < ActiveRecord::Base
attr_reader :price
belongs_to :product
def price
self.product.price * self.quantity
end
def as_json(options = {})
super(:include => [:product])
end
end
Above code works. However now I want my json to also have value for price in addition to the other values that I am getting.
How do I accomplish that?
You can use :methods:
def as_json(options = {})
super(options.merge(:include => [:product], :methods => [:price]))
end
You might want to pay proper attention to any incoming :include and :method settings in your options though. So you might want to use the block form of merge:
EXTRAS = { :include => [:product], :methods => [:price] }
def as_json(options = { })
super(options.merge(EXTRAS) { |k,ov,nv| ov.is_a?(Array) ? ov + nv : nv }
end

Resources