Rails Controller Hash into Array - ruby-on-rails

trying to change the structure of a JSON response using a serializer. I'm having problems though as I need to group the data alphabetically (|one_record| one_record.name[0].to_s) which is returning a hash at the moment. How could I go about converting this to an array that I can pass into the serializer?
#WIP - order should return an array of contacts ordered alphabetically according to their name.
def order
# returns the first letter of the name
#ordered_contacts = #user.all_contacts.group_by { |one_record| one_record.name[0].to_s }
render json: #ordered_contacts, serializer: ContactshipsSerializer, :status => :ok
end
Serializer:
class ContactshipsSerializer < ActiveModel::Serializer
# method override
def serializable_object(options={})
#ordered_contacts.map do | *title*, ordered_contacts |
[ *title* , serialized_ordered_contacts(ordered_contacts) ]
end.to_h
end
private
def serialized_contactships contactships
ordered_contact.map{ |contactship| ContactshipsSerializer.new(ordered_contact, root: false) }
end
end

Related

Too many checks for empty params. How to optimize queries to ActiveRecord in Rails5?

I'm doing checks for empty parameters before do the query.
There is only 1 check for params[:car_model_id]. I can imagine if I will add more checks for other params, then there will be a mess of if-else statements. It doesn't look nice and I think it can be optimized. But how? Here is the code of controller:
class CarsController < ApplicationController
def search
if params[:car_model_id].empty?
#cars = Car.where(
used: ActiveRecord::Type::Boolean.new.cast(params[:used]),
year: params[:year_from]..params[:year_to],
price: params[:price_from]..params[:price_to],
condition: params[:condition]
)
else
#cars = Car.where(
used: ActiveRecord::Type::Boolean.new.cast(params[:used]),
car_model_id: params[:car_model_id],
year: params[:year_from]..params[:year_to],
price: params[:price_from]..params[:price_to],
condition: params[:condition]
)
end
if #cars
render json: #cars
else
render json: #cars.errors, status: :unprocessable_entity
end
end
end
The trick would be to remove the blank values, do a little bit of pre-processing (and possibly validation) of the data, and then pass the params to the where clause.
To help with the processing of the date ranges, you can create a method that checks both dates are provided and are converted to a range:
def convert_to_range(start_date, end_date)
if start_date && end_date
price_from = Date.parse(price_from)
price_to = Date.parse(price_to)
price_from..price_to
end
rescue ArgumentError => e
# If you're code reaches here then the user has invalid date and you
# need to work out how to handle this.
end
Then your controller action could look something like this:
# select only the params that are need
car_params = params.slice(:car_model_id, :used, :year_from, :year_to, :price_from, :price_to, :condition)
# do some processing of the data
year_from = car_params.delete(:year_from).presence
year_to = car_params.delete(:year_to).presence
car_params[:price] = convert_to_range(year_from, year_to)
price_from = car_params.delete(:price_from).presence
price_to = car_params.delete(:price_to).presence
car_params[:price] = convert_to_range(price_from, price_to)
# select only params that are present
car_params = car_params.select {|k, v| v.present? }
# search for the cars
#cars = Car.where(car_params)
Also, I'm pretty sure that the used value will automatically get cast to boolean for you when its provided to the where.
Also, #cars is an ActiveRecord::Relation which does not have an errors method. Perhaps you mean to give different results based on whether there are any cars returned?
E.g: #cars.any? (or #cars.load.any? if you don't want to execute two queries to fetch the cars and check if cars exist)
Edit:
As mentioned by mu is too short you can also clean up your code by chaining where conditions and scopes. Scopes help to move functionality out of the controller and into the model which increases re-usability of functionality.
E.g.
class Car > ActiveRecord::Base
scope :year_between, ->(from, to) { where(year: from..to) }
scope :price_between, ->(from, to) { where(price: from..to) }
scope :used, ->(value = true) { where(used: used) }
end
Then in your controller:
# initial condition is all cars
cars = Cars.all
# refine results with params provided by user
cars = cars.where(car_model_id: params[:car_model_id]) if params[:car_model_id].present?
cars = cars.year_between(params[:year_from], params[:year_to])
cars = cars.price_between(params[:price_from], params[:price_to])
cars = cars.used(params[:used])
cars = cars.where(condition: params[:condition]) if params[:condition].present?
#cars = cars

Filter data in JSON string

I have a JSON string as pulled from some API
[{"_id"=>"56aefb3b653762000b400000",
"checkout_started_at"=>"2016-02-01T07:32:09.120+01:00",
"created_at"=>"2016-02-01T07:29:15.695+01:00", ...}]
I want to filter data in this string based on created_at, e.g. letting the user chose a specific date-range and then only show the data from this range.
E.g.
#output = my_json.where(created_at: start_date..end_date)
My first thought was to somehow transfer the JSON string to Hashie, to interact with JSON as the data were objects:
my_json = (my_json).map { |hash| Hashie::Mash.new(hash) }
but that didn't work out
undefined method `where' for Array:0x007fd0bdeb84e0
How can I filter out data from a JSON string based on specific criteria or even SQL queries?
This simplest possible way would be to use Enumberable#select directly on the array of hashes:
require 'time'
myjson.select { |o| Time.iso8601(o["created_at"]).between?(start_date, end_date) }
If you want a fancy interface surrounding the raw data:
require 'time' # not needed in rails
class Order < Hashie::Mash
include Hashie::Extensions::Coercion
coerce_key :created_at, ->(v) do
return Time.iso8601(v)
end
end
class OrderCollection
def initialize(json)
#storage = json.map { |j| Order.new(j) }
end
def between(min, max)
#storage.select { |t| t.created_at.between?(min, max) }
end
end
orders = OrderCollection.new(myjson)
orders.between(Time.yesterday, Time.now)

Rails Render JSON Object instead of entire array

In my controller I have the present code that results in a JSON array, even though there is only one result:
def getSharedSpecial
#result = Campaign.find_by_sql("SELECT
id
,name
,image
,ad_caption
,ad_details
FROM campaigns
WHERE id = " + params[:shared_campagin_id].to_s + "
LIMIT 1;")
respond_to do |format|
format.html
format.json { render json: { special_shared: #result }}
end
end
returns:
"special_shared":[
{
"id":41,
"name":"tester the way",
"image":{
"url":"/uploads/campaign/image/41/Gilded_pic.jpg"
},
"ad_caption":"yfftitu6",
"ad_details":"jku"
}
]
}
As can be seen given the [], this is a JSON array.
How can I create just an object and not an entire array?
The problem is that find_by_sql always returns an array even though you are only looking for a single record. There is no need to use find_by_sql and you've opened yourself to SQL injection attacks by doing so, so just write the finder the traditional way:
#result = Campaign.select(:id, :name, :image, :ad_caption, :ad_details).find(params[:shared_campagin_id])

Why Rails deserializes snake case data structures but serializes camel case data structures?

It is one of my first Ruby on Rails project and it is weird for me to send JSON with properties written in snake case on my requests and receive JSON with properties written in camel case on my responses.
Here is an example of request payload:
{
"end_point":"test"
}
And here is an example of response payload:
{
"endPoint":"test"
}
Here is the code that consumes and returns the alike data structures above:
def create
def create
#api = interactor.create(params[:organization_id], api_params)
respond_to do |format|
format.json { render :show, status: :created, location: api_url(#api) }
end
end
Use String#camelize method from ActiveSupport(which comes with Rails) like so:
attributes_hash = { "test_data" => "test" }
json_hash = Hash[attributes_hash.map {|k, v| [k.camelize(:lower), v] }]
#=> {"testData"=>"test"}
Now, you can convert it to JSON string:
p json_hash.to_json #=> "{\"testData\":\"test\"}"
UPDATE: You can override serializable_hash method in your model class(since you're not clear with what exactly interceptor is in your code, I assume you'd need to put this in the class whose object you're instantiating to send the data as JSON) like so -
def serializable_hash(options = nil)
options ||= {}
if options[:camelize]
Hash[attributes.map {|k, v| [k.camelize(:lower), v] }]
else
super
end
end
Now, you can do: #api.to_json(:camelize => true) and it'll convert attributes to camecase except the first character, i.e.: endPoint.

id nil when select model without no relation

I have a model with no relation.
class GridConfig < ActiveRecord::Base
end
my question is why result of query are appended id:nil columns?
GridConfig.select(:fontSize)
result is
#<GridConfig id: nil, fontSize: "12px">
is there any options for this?
thank you.
I want find some records and pick certain columns. and send to client.
user_key = params[:user_key]
grid_id = params[:grid_id]
#config = GridConfig.where(['user_key = ? and grid_id = ?', user_key, grid_id])
.select(:model_id, :fontSize, :displayCount, :columnModel)
# i checked #config variables at this point and found nil:id...
#config = #config.index_by(&:model_id)
# and i want to this makes indexed by model_id like [{"model":{...}},{"model2" : {...}}, {}]
respond_to do |format|
format.json { render json: #config }
end
You can use the pluck method to select only certain columns into an array, then index by the first column.
#config = GridConfig.where(user_key: params[:user_key], grid_id: params[:grid_id])
.pluck(:model_id, :fontSize, :displayCount, :columnModel)
#config = #config.index_by{ |x| x[0] }

Resources