Using Rails as_json with conditional statement - ruby-on-rails

I have a file (show.json.erb) in which I'm formatting a model as json. In my app, Projects have many Steps and many Images, and Steps have many Images.
I'd like to be able to include only a default image for a project, which I'm trying to do using "conditions." However, it seems to be ignoring the conditions and posting all the images associated with a project. How do I include only the right images?
"projects":
<%= JSON.pretty_generate(#projects.order("updated_at DESC").as_json(only: [:title, :id,
:built, :updated_at], include: {images: {only: [:image_path],
:conditions=>['Step.find(image.step_id).name.eql? "Project Overview"', true] }
})).html_safe %>

I solved this problem by creating a method called image_path in my Projects model:
(I needed the method to return three types of images)
def image_path
images = Hash.new
if first_step.images.present?
images["url"] = first_step.first_image.image_path_url
images["preview"] = first_step.first_image.image_path_url(:preview)
images["thumbnail"] = first_step.first_image.image_path_url(:thumb)
end
return images
end
Then I edited my JSON to look like this:
"projects":
<%= JSON.pretty_generate(#projects.order("updated_at DESC").as_json(only: [:title, :id, :built], :methods=> [:image_path, :updated_at_formatted])).html_safe %>
This gets rendered like this on my website:
"projects":
[
{
"built": true,
"id": 115,
"title": "Video Test",
"image_path": {
"url": "https://s3.amazonaws.com/...",
"preview": "https://s3.amazonaws.com/...",
"thumbnail": "https://s3.amazonaws.com/..."
},
"updated_at_formatted": "07/08/2013 10:31:19"
},
...]
Hope this helps someone else!

Related

How to update multiple users with form in Rails?

I want to use form_for to create a form and update multiple users.
Let's say each user has :id and :name. I would like the POST parameters to be:
{
"users": [
{
"id": 1,
"name": "Bob"
},
{
"id": 2,
"name": "Leo"
}
......
]
}
The form looks like([] means input fields):
Users
1 [ Bob ]
2 [ Leo ]
3 [ ]
How does the form_for code looks like?
form_tag your_url, method: :post do |f|
#users.each do |user|
label_tag user.id
hidden_field_tag "users[][id]", user.id
text_field_tag "users[][first_name]", user.first_name
end
end
I think it will pass parameters as you want, but the hard thing is how to show detail error messages for each user

Hide input parameters partially on activeadmin

I have an activeadmin form, in which one of the inputs fields is populated by json data returned by some method as
f.input :tag, :label => 'Tags', :as => :select, :collection => HelperClass.get_json()
The json looks like this:
{
"group_name": "Group1",
"categories": [
{
"category_name": "cat_1",
"score": "120"
},
{
"category_name": "cat_2",
"score": "120"
}
]
}
While this is shown on the form in the UI, I want only the 'group_name' to appear. However, I would want to use the data in 'categories' later on.
Is there anyway I could do this? For eg, hide the remaining json from the form, or parse the json in some other place using 'group_name', or any other way that I can't think of..
PS: Could you also elaborate while you answer this. I am not a ROR developer, but had to modify the code written by someone else.
First convert to array/hash then use Ruby Array/Hash methods. Assuming you meant the JSON is an array of groups:
data = JSON.parse HelperClass.get_json()
f.input :tag, :label => 'Tags', :as => :select,
:collection => data.map {|grp| grp['group_name']}
I suspect you want a multi-level select, which will require you to write your own JavaScript to populate the second select. Maybe https://github.com/blocknotes/activeadmin_selectize will help with that.

RABL - attributes within child node

If I create a child node in RABL using the node() method, how can I control the attributes that are presented?
The JSON output is this:
[
{
"location": {
"latitude": 33333,
"longitude": 44444,
"address": "xxxxxxx",
"title": "yyyy",
"url": "http://www.google.com",
"rate": {
"created_at": "2012-09-02T11:13:13Z",
"id": 1,
"location_id": 1,
"pair": "zzzzzz",
"updated_at": "2012-09-02T12:55:28Z",
"value": 1.5643
}
}
}
]
I want to get rid of the created_at, updated_at and location_id attributes.
I have this in my view file:
collection #locations
attributes :latitude, :longitude, :address, :title, :url
node (:rate) do
|location| location.rates.where(:pair => #pair).first
end
I tried using a partial and the 'extend' method, but it totally screwed things up. Also, I tried adding attributes to the block but it didn't work (the output was as specified in the attributes but it didn't show the values for each attribute).
Thanks!
Your code: location.rates.where(:pair => #pair).first returns the whole Rate object. If you want specific fields (for example: all, except for create_at, updated_at, etc.) then you have two options:
Manually describe the hash in node():
node (:rate) do |location|
loc = location.rates.where(:pair => #pair).first
{ :pair => loc.pair, :value => loc.value, etc... }
end
Or you can do this:
node (:rate) do |location|
location.rates.where(:pair => #pair).select('pair, value, etc...').first
end
...and as a side note, I should say that placing logic (rates.where) in your view is not a best practice. see if your controller can do that for the view using the Rate model.
You wouldn't be able to use attributes within the node block, since "self" in there is still the root object or collection, so in your case #locations. See also RABL wiki: Tips and tricks (When to use Child and Node)
In the node block you could simply create your custom response by only listing the attributes that your interested in:
node :rate do |location|
rate = location.rates.where(:pair => #pair).first
{:id => rate.id, :location_id => rate.location_id, :value => rate.value}
end
You can also try the approach using a partial:
In app/views/rates/show.json.rabl
object #rate
attributes :id, :location_id, :value
Then in your #locations rabl view:
node :rate do |location|
rate = location.rates.where(:pair => #pair).first
partial("rates/show", :object => rate)
end

How do I convert an array of strings into a comma-separated string?

I have an array:
array = ["10", "20", "50", "99"]
And I want to convert it into a simple comma-separated string list like this:
"10", "20", "50", "99"
array.join(',') will almost do what you want; it will not retain the quotes around the values nor the spaces after.
For retaining quotes and spaces: array.map{|item| %Q{"#{item}"}}.join(', ')
This will print "\"10\", \"20\", \"50\", \"99\"". The escaped quotes are necessary assuming the question does in fact call for a single string.
Documentation on the %Q: string literals.
You could use inspect as suggested in another answer, I'd say that's personal preference. I wouldn't, go look at the source code for that and choose for yourself.
Useful aside: array.to_sentence will give you a "1, 2, 3 and 4" style output, which can be nice!
["10", "20", "50","99"].map(&:inspect).join(', ') # => '"10", "20", "50", "99"'
Here:
array.map {|str| "\"#{str}\""}.join(',')
Several answers have offered solutions using #map, #inspect, #join. All of them fail to get certain details of CSV encoding correct for edge cases involving embedded commas and/or string delimiters in the elements.
It's probably a better idea to use the stdlib class CSV then to roll your own.
irb> require 'csv'
=> true
irb> a = [10,'1,234','J.R. "Bob" Dobbs',3.14159]
=> [10, "1,234", "J.R. \"Bob\" Dobbs", 3.14159]
irb> puts a.to_csv
10,"1,234","J.R. ""Bob"" Dobbs",3.14159
The map.join solutions are sufficient if this encoding doesn't need to care about embedded delimiters, or is intended for some internal representation only, but they will fail if generating data for exchange with other programs that expect Comma Separated Values (CSV) as a generally understood representation.
The simplest solution is to use the built in ".to_sentence" method.
So
["fred", "john", "amy"].to_sentence outputs "fred, john, and amy"
This is a slightly alternative solution, particularly handy if you need to convert an array with double quoted strings to a single quoted list (for say SQL queries):
"'#{["John Oliver", "Sam Tom"].join("','")}'"
to
'John Oliver', 'Sam Tom'
Attribution: https://alok-anand-ror.blogspot.com/2014/04/ruby-join-array-elements-with-single.html
This is how you can send push notifications using FCM for Android devices.
Assuming you want notify followers when ever a user posts something on their status this is how you do it. This is done in Rails 5.2.6 for Rest Apis--- But still you can use the same for web push notifications. This is for sending to many devices with registration_ids to target followers with notifications.
Gem : fcm
in your controller:
require "fcm"
def create_vibe(user)
#vibe = user.vibes.new(content: #content, picture: #picture, video: #video, isvideofile: #isvideofile, video_thumbnail: #video_thumbnail, source: #source, background_color: #background_color)
#followed = user.followers
if #followed.present?
#registration = #followed.map { |s| s.registration_id }
end
if #vibe.save
fcm = FCM.new("") # set your FCM_SERVER_KEY
options = {
data: {
notification_type: 1,
title: "#{#vibe.user.username} " "has posted a new Vibe",
body: "#{#vibe.content}",
user_data: {
vibe_id: #vibe.id,
user_id: #vibe.user.id,
background_color: #background_color,
},
},
}
response = fcm.send(#registration, options)
puts response
render :status => 200,
:json => { :success => true,
:info => "Vibe posted successfully",
:vibe_info => {
:content => #content,
:picture => #picture,
:video => #video,
:video_thumbnail => #video_thumbnail,
:isvideofile => #isvideofile,
:source => #source,
:fcm => options,
} }
else
render :status => 200, :json => { :success => false, :result => "Vibe can't be blank" }
end
end

output formated json with rails 3

I use rails 3.0.3
A javascript auto complete needs data like this
{
query:'Li',
suggestions:['Liberia','Libyan Arab Jamahiriya','Liechtenstein','Lithuania'],
data:['LR','LY','LI','LT']
}
My action is
def autocomplete
#query = params[:query]
#customers = Customer.where('firstname like ?', "%#{#query}%")
render :partial => "customers/autocomplete.json"
end
My view is
{
query:'<%= #query %>',
suggestions: <%= raw #customers.map{|c| "#{c.firstname} #{c.lastname}" } %>,
data: <%= raw #customers.to_json %>
}
it returns
{
query:'e',
suggestions: ["customer 1", "customer 2"],
data: [1, 3]
}
it's not working because the data for suggestions/data should be between simple quote...
I cannot use the to_json method, because it'll returns all the content of my object.
Any suggestion?
cheers
Note: this is way out of date, Jbuilder is by far a better option.
There are two ways you can approach this. If you simply need a subset of the fields in an object, you can use :only or :except to exclude what you don't want.
#customer.to_json(:only => [:id, :name])
in your example it looks like you need to return json in a specific format, so simply serializing an array of results won't work. The easiest way to create a custom json response is with the Hash object:
render :json => {
:query => 'e',
:suggestions => #customers.collect(&:name),
:data => #customers.collect(&:id)
}
I've tried using partials to build json responses, but that doesn't work nearly as well as simply using Hash to do it.
Formatting the first and last names as a single string is something you are likely to do a lot in your views, I would recommend moving that to a function:
class Customer < ActiveRecord::Base
...
def name
"#{first_name} #{last_name}"
end
def name=(n)
first_name, last_name = n.split(' ', 2)
end
end
Just some convenience functions that makes your life a little easier, and your controllers/views cleaner.
If Adam's response won't work for you, this may do it (admittedly not the most elegant solution):
{
query:'<%= #query %>',
suggestions: [<%= raw #customers.map{|c| "'#{c.firstname} #{c.lastname}'" }.join(", ") %>],
data: [<%= raw #customers.map{|c| "'#{c.id}'" }.join(", ") %>]
}
I've seen something like this in a .erb:
<%= raw
{
:query => #query,
:suggestions => #customers.map{|c| "#{c.firstname} #{c.lastname}" },
:data => #customers
}.to_json
%>
If thinking of preparing data to be consumed by other programs as presentation logic, this might make sense to you.
FWIW I like it.

Resources