I am trying to convert this .json.rabl view to .json.erb and since im very new to ruby, I am looking for direction in achieving this. What will be the best way to go about this?
object #perform
attributes :id => :team_id
attributes :perform_id
node(:fb_user_id) {|x| x.user.fb_user_id.to_s}
node(:first_name) {|x| x.user.first_name}
node(:last_name) {|x| x.user.last_name}
attributes :level, :jersey, :height, :weight, :positions
node(:stat_source) {|x| Stat::SOURCES.key(x.stat_source)}
if !#nomvps
node({}, :if => lambda{ |m| #event.present? }) do |x|
{
mvp_votes: Vote.player_vote_count(#event.id, x.id)
}
end
end
if !#nostats && (#_options[:event] || #event)
node :stats do |perform|
Hash[*perform.source_stats.where(event_id: (#_options[:event].try(:id) || #event.id)).map{|x|[x.name, x.value]}.flatten]
end
end
Erb is the reverse of a templating language like rabl - you create the json you want, and then insert the values with erb, rather than defining the structure with code as in rabl.
In erb surrounding text with <% %> indicates some ruby code (for assigning, loops etc):
<% x = "output" %>
and <%= %> (NB = sign) indicates ruby code (any code) which will be output to the template by calling to_s:
<%= x.to_json %>
Anything not surrounded by those symbols is output exactly as it is.
So you should be able to just create the json you want with fake values, and then put in the calls to the relevant ruby code to get the real values at runtime. e.g. something like this fragment (not familiar with rabl so the format of json may need adjustment):
[{ "perform" :
{
"id" : <%= #perform.team_id %>,
"user" : <%= #perform.perform_id %>,
...
}
}]
<% if !#nomvps %>
<% node({}, :if => lambda{ |m| #event.present? }) do |x| %>
{
mvp_votes: <%= Vote.player_vote_count(#event.id, x.id) %>
}
<% end %>
<% end %>
For objects which map cleanly to json as a hash for example you could also consider creating to_json methods instead of laying them out in a template like this, but for a complex structure like this without a clean mapping a template might be best.
I'd start with the json you want in a file named xx.json.erb (pure json), then start inserting values using erb.
Maybe it's not an answer to the exact question, but the endorsed way to do JSON views in rails is using jbuilder.
The API of JBuilder is very similar to the RABL one, but it's done by the very same rails core team, so it integrates with all the internals, like cache helpers (in the future also cache dependence), partials etc.
https://github.com/rails/jbuilder
Related
I have an instance variable #tally_property, and if there are photos on that object I would like to cycle through the photos and show them.
So my code snippet looks like this:
<% if #tally_property.photos.present? %>
<% #tally_property.photos.each_with_index do |photo, index| %>
The issue is that based on the above, if #tally_property is nil, then the entire first line throws an error.
So is there a 'nil' check I can do that isn't bulky, i.e. I don't want to do if #tally_property.nil?, on both the primary object and the association, and is elegant and ruby & rails-esque?
I would use the safe navigation operator (&.) and write something like this:
<% #tally_property&.photos&.each_with_index do |photo, index| %>
...
<% end %>
In Ruby 2.3.0+ you can use the safe navigation operator:
#tally_property&.photos
ActiveSupport has a .try method that can be used to the same end in older versions of ruby:
#tally_property.try(:photos)
You can add a simple conditional to be able to safely iterate through the collection:
<% (#tally_property.try(:photos)||[]).each_with_index do |photo, index| %>
<% end %>
Rails 4 adds ActiveRecord::Relation#none and a change in behaviour so that associations always return a ActiveRecord::Relation. So its perfectly acceptable to write:
<% #tally_property.try(:photos).try(:each_with_index) do |photo, index| %>
<% end %>
After upgrading your app. Or you can use a partial and render:
<%= render partial: 'photos', collection: #tally_property.photos if #tally_property %>
Which removes the need for writing the iteration.
Use && (or and, they each have their sweetspot).
Taking it out of Erb for a moment, I would generally write something like this:
if #tally_property and #tally_property.photos.present?
Depending on photos I might use:
if #tally_property and #tally_property.photos
or perhaps:
if #tally_property and not #tally_property.photos.empty?
Sometimes I'll use a temporary variable:
if (photos = #tally_property && #tally_property.photos)
photos.each #…
That kind of thing.
I would recommend this episode of Ruby Tapas, And/Or for a longer (but still quick) look at it.
One more way, just select all photos connected to this tally_property:
example how it might be:
Photo.joins(:tally_property).each_with_index do |photo, index|
In Rails 3.1 it is not possible to access controller instance variables in an asset js.erb or coffee.erb file using syntax such as <%= #foo %>, where #foo is set in the controller. So then the question is what are the best ways for passing controller variables to CoffeeScript or JavaScript assets.
This question has kind of been asked in multiple convoluted forms on the forum, but my point in asking it again is to have a place where all recommendations are gathered together, and the code supplied is simple and readable. Also note that I'm specifically referring to assets and not view response files.
a couple of ways I have done this in the past
put the data in hidden fields, access the data in js/coffee
# single value
<%= hidden_field_tag "foo_name", #foo.name, { :id => "foo-name" } %>
$('#foo-name').val();
# when the 'value' has multiple attributes
<%= hidden_field_tag "foo", #foo.id, { :id => "foo", "data-first-name" => #foo.first_name, "data-last-name" => #foo.last_name } %>
$foo = $('#foo')
console.log $foo.val()
console.log $foo.data("firstName")
console.log $foo.data("lastName")
another option: load data into js data structure in erb, access it from js/coffee
<% content_for(:head) do %>
<script>
window.App = window.App || {};
window.App.Data = window.App.Data || {};
window.App.Data.fooList = [
<% #list.each do |foo| %>
<%= foo.to_json %>,
<% end %>
];
</script>
<% end %>
# coffee
for foo in window.App.Data.fooList
console.log "#{foo.id}, #{foo.first_name} #{foo.last_name}"
I am not a big fan of constructing javascript data from ruby in erb like this, something about it just feels wrong - it can be effective though
and another option: make an ajax call and get the data on-demand from the server
I am also interested in other ideas and approaches
There is a really nice rail cast and quite recent (feb. 2012) about this specific topic:
#324 Passing Data to JavaScript
It shows 3 ways: a script tag, a data attribute, and the Gon gem.
I think house covered all the available techniques. I would only mention that using an AJAX call is best used when you have a large volume of data, dynamic data or combination of both.
Rather than use a hidden field I chose to add a data attribute to the container div which jquery can pick up.
<div class="searchResults" data-query="<%= #q %>"></div>
then the jquery to access it
url: "/search/get_results?search[q]=" + $(".searchResults").data("query") + "&page=" + p
I feel this is the cleanest way to pass data to javascript. After having found no way to pass a variable to a coffee script file with the rails asset pipeline from a controller. This is the method I now use. Can't wait till someone does set up the controller way with rails that will be the best.
In the controller:
#foo_attr = { "data-foo-1" => 1, "data-foo-2" => 2 }
In the view (HAML):
#foo{#foo_attr}
In the CoffeeScript asset:
$("#foo").data("foo-1")
$("#foo").data("foo-2")
In situations where your javascript data gets out of hand, using the gon gem is still the preferred way to go in rails, even in 2015. After setting up gon, you are able to pass data to your javascript files by simply assigning the data to the gon object in rails.
(Gemfile)
gem 'gon'
(controller)
def index
gon.products = Product.all
(layouts)
<%= include_gon %>
(public/javascripts/your_js_can_be_here.js)
alert(gon.products[0]['id');
(html source automatically produced)
<script>
window.gon = {};
gon.products = [{"created_at":"2015", "updated_at":"2015, "id":1, "etc":"etc"}];
You can read more verbose implementation details on Gon or the two other rails-javascript channels from Ryan Bate's screencast.
http://railscasts.com/episodes/324-passing-data-to-javascript
You can edit and add variables to the params array in the controller then access them in the response.js.erb. Here's an example with params[:value]:
def vote
value = params[:type] == "up" ? 1 : -1
params[:value] = value
#public_comment = PublicComment.find(params[:id])
have_voted = #public_comment.evaluators_for(:pub_votes_up) << #public_comment.evaluators_for(:pub_votes_down)
unless have_voted.include?(#current_user) # vote
#public_comment.add_or_update_evaluation(:"pub_votes_#{params[:type]}", value, #current_user)
else # unvote
#public_comment.delete_evaluation(:"pub_votes_#{params[:type]}", #current_user)
params[:value] = 0
end
respond_to do |format|
format.js # vote.js.erb
end
end
And here's an example accompanying response.js.erb
button = $('<%= ".pub#{params[:type]}_#{params[:id]}" %>')
label = button.find('strong')
<% comment = PublicComment.find(params[:id]) %>
label.html('<%= comment.reputation_for(:"pub_votes_#{params[:type]}").to_i %>')
<% if params[:value] == 1 %>
button.addClass('btn-success')
<% elsif params[:value] == -1 %>
button.addClass('btn-danger')
<% else %>
if button.hasClass('btn-success') { button.removeClass('btn-success') }
if button.hasClass('btn-danger') { button.removeClass('btn-danger') }
<% end %>
I'm using Tiny Mce Editor 4.0.5 in Rails 2.
new.html
<% form_for #custom, :url=>"create" do |c| -%>
<%= c.text_area 'description' %>
<%= c.submit %>
<% end %>
Create Action:
CustomReport.create(params[:custom_report][:description])
After submission of form I get
undefined method `stringify_keys!'
Along that I tried
CustomReport.create(:description => params[:custom_report][:description])
But it doesn't store any HTML tags, so how I can store tags along to my DB?
Using xss_terminate plugin
And in my model CustomReport
xss_terminate :except => [:description]
easy and simple.
CustomReport.create(:description => params[:custom_report][:description])
should do the job for you, but Rails would naturally escape html tags, to stop rails from doing that you would need to call:
[html_safe](http://apidock.com/rails/String/html_safe)
or
raw
in your view on the string with the html tags (This is not safe practice, you should be sure the string is sincerely safe before considering this, as it can expose your app to attacks)
I have been trying to figure out a way to customize JSON with special fields, custom formats, etc etc. I have created an as_json and to_xml method in my model to formulate the object how I need. This works well but it is sloppy because some of my helper methods had to move into the model, because I need the formats in the helpers and model. I also think it is sloppy code and makes the model out of control.
I have been able to get a format with json.erb working but don't think it is working 100% correct either and the callback doesn't append either. Anyone get this working
Here is what I got so far.
api calls format.json
template called is items.json.erb
<% #items.each do |item| %>
<%= { :item => { :id => item.id, :name => item.name }.to_json.html_safe %>
<% end %>
This works but seems odd. Anyone have suggestions or have a way to do this?
btw did this for the callback to work
<%= params[:callback]+"(" if params[:callback] %>
<% #items.each do |item| %>
<%= { :item => { :id => item.id, :name => item.name }.to_json.html_safe %>
<% end %>
<%= ")" if params[:callback] %>
I think the best way to do this would be to skip the erb template if you don't absolutely need if for some reason. Then you could do something like this:
items = Item.all
render :json => items.to_json(:only => [:id, :name]), :callback => params[:callback]
You can override the to_json method in your model to add fields or call methods.
Based on your answer to polarblau, you should override the as_json method and use the :methods parameter to include method results in your json
class Item
def date
return "1 year and 8 months" #obviously use logic here
end
def as_json(args={})
super(:methods=>[:date], :only=>[:id=>:name])
end
end
Most likely, you'll want to either:
use custom finder sql to alter column names/perform calculations (it's much faster than Ruby):
MyModel.select('col_name_that_needs_renamed AS new_name').order('some_col DESC')
or a more complicated example:
MyModel.find_by_sql('SELECT col_name_that_needs_renamed AS new_name, foo_col*50 AS math WHERE foo=bar ORDER some_col LIMIT 8')
if there's something you can't do (or can't figure out) in SQL, you may have to revert to Ruby (although not recommended because it's significantly slower)
API Dock for to_json
I want to reformat some helpers in my Rails views. I want a syntax similar to:
<%=
box :option => 'value' do |b|
b.header "Header of box #1"
%>
Content of Box#1
<%
end
%>
The b.header call is optional.
How would I structure my code to allow this? I guess it's something similar to fields_for in Rails.
You could always look at the source for form_for / fields_for?