RABL fails for empty query - ruby-on-rails

I have a data tables table which is receiving data via a query that is then formatted using RABL.
It all works great until my search finds no rows.
Then I just get the ....processing message and no update.
I am really confused by RABL. Is there a way to see what it is doing?
This is my data.rabl file
object false
node(:iTotalRecords){#iTotalRecords}
node(:iTotalDisplayRecords){#iTotalDisplayRecords}
child(#aaData, :object_root => false) do
attributes :school_name => "1",
:student_name => "3",
:paid => "4",
:short_name => "5",
:emailed => "6",
:reconciled => "9"
node("6") do |p|
if p.emailed == 0
'false'
else
'true'
end
end
node("0") do |p|
p.created_at.to_date
end
node("2") do |p|
schedule = p.enrollment.schedule
link_to(p.klass_name, schedule_path(schedule))
end
node("3") do |p|
link_to(p.enrollment.student.name, student_path(p.enrollment.student_id))
end
node("7") do |p|
p.enrollment.rate
end
node("8") do |p|
p.enrollment.paid
end
node("10") do |p|
link_to("Edit", edit_payment_path(p), :class => 'btn btn-info btn-mini')
end
end

What I needed to do was to make sure that the json included a field called :payments.
If there was no data the child loop was not being executed and so there was no reason for RABL to generate a :payments field.
I found that you can force this to happen by creating an alias.
So I added the code:
child :payments => :payments
right before the line
child(#aaData, :object_root => false) do
This works great now.

Related

How do you render a partial to a hash value in JBuilder?

I have a tree-like object graph that resembles the following:
{
:name => "Grandparent",
:children => {
:child_a => {
:name => "Parent A",
:children => {
:grandchild_a_a => {
:name => "Child A-A",
:children => {}
}
:grandchild_a_b => {
:name => "Child A-B"
:children => {}
}
}
}
:child_b => {
:name => "Parent B",
:children => {}
}
}
}
I want to generate JSON that mirrors this structure. I don't know how deep the child nesting goes, and the attributes are the same for each level. The keys in the children hash are significant and must be preserved.
I want to use a JBuilder partial to represent a level, and then call it recursively. Here's my template thus far:
# _level_partial.json.jbuilder
# passing the above object graph as :level
json.name level[:name]
json.children do
level[:children].each do |key, child|
# How do I map the following to the given key?
json.partial! "level_partial", :level => child
end
end
I can generate the JSON for each child through the partial call easily enough, but that inserts it directly into the JSON output. How do I map the results of the partial to a particular hash/object key?
I've found an answer. Although it appears to be largely undocumented, JBuilder.set! can accept a block instead of an explicit value. That block can call the partial, which is then assigned to the hash.
json.name level[:name]
json.children do
level[:children].each do |key, child|
json.set! key do
json.partial! "level_partial", :level => child
end
end
end

Activerecord-reputation-system - how to check value of current_user's vote

I'm checking if a user has voted on a given post and then changing the up and down arrows' styling. The activerecord-reputation-system gem hasn't been updated in a while and every example I find either uses outdated code or requires tracking "up" votes and "down" votes separately. I prefer to just use :post_votes and check the value of that one parameter.
posts_controller.rb
#voted_items = Post.evaluated_by(:post_votes, current_user)
index.html.erb
<% if current_user && #voted_items.include?(post) %>
# display active up or down arrows
<% end %>
How do I get just the value of the relevant record within the #voted_items object?
Edit: I notice there is no built-in scope for :value. Maybe adding one manually would let me query a particular value?
For now I am came up with this method def on the model:
def evaluation_value(user, comment)
if #up_voted = ReputationSystem::Evaluation.where(:reputation_name => "comment_votes",
:value => "1.0", :source_id => user.id, :source_type => user.class.name,
:target_id => comment.id, :target_type => comment.class.name).exists?
"upvoted"
elsif #down_voted = ReputationSystem::Evaluation.where(:reputation_name => "comment_votes",
:value => "-1.0", :source_id => user.id, :source_type => user.class.name,
:target_id => comment.id, :target_type => comment.class.name).exists?
"downvoted"
else
nil
end
end
It's not ideal because it requires hardcoding the values and other details, but it works for now until the gem is updated to allow checking individual values.
This way I can run comment.evaluation_for(current_user, comment) == "upvoted" or downvoted.

Iterating through columns on active_admin model

New to Ruby and Rails, so perhaps I'm not searching/asking this the right way. I'm using the ActiveAdmin gem and want to turn this:
column :purchase_price, :sortable => :purchase_price do |piece|
div :class => "price" do
number_to_currency piece.purchase_price
end
end
column :appraised_value, :sortable => :appraised_value do |piece|
div :class => "price" do
number_to_currency piece.appraised_value
end
end
column :sale_price, :sortable => :sale_price do |piece|
div :class => "price" do
number_to_currency piece.sale_price
end
end
into this:
price_array = [:purchase_price, :appraised_value, :sale_price]
price_array.each do |p|
column p, :sortable => p do |piece|
div :class => "price" do
number_to_currency piece.p
end
end
end
...in the interest of DRY.
The longer solution works, but the shorter one gives a "NoMethodError in Admin::Pieces#index" and I'm kind of at a loss as to what's wrong. Any suggestions?
As house9 said, my problem was that I tried calling piece.p when really it should be piece.send(p). Still not exactly sure why you can't just replace all instances of p with the iterator loop like I did originally but maybe someone else can explain.
In any case, thank you!

multiparameter error with datetime_select

I have the following code in my form.
<%= f.datetime_select(:date_time, :prompt => {:day => 'Day', :month => 'Month', :year => 'Year'}, :start_year => Date.today.year, :end_year => Date.today.year + 2, :minute_step => 15, :include_blank => false) %> if either one is blank.
When one of the fields is left blank, I get:
1 error(s) on assignment of multiparameter attributes
The params that are being passed are:
{"utf8"=>"✓",
"authenticity_token"=>"kQpfsj5RxnDtxkvBdwPEFnX1fY6euKnMQeDRAkvJvIE=",
"event"=>{"description"=>"",
"venue"=>"",
"street"=>"",
"city"=>"",
"country_id"=>"",
"date_time(1i)"=>"",
"date_time(2i)"=>"",
"date_time(3i)"=>"",
"date_time(4i)"=>"00",
"date_time(5i)"=>"00",
"ticket_url"=>""},
"x"=>"94",
"y"=>"12"}
Anyone know why this is occurring?
There seems to be a "dirty" fix for this at this link, but perhaps there is a better solution in Rails 3?
Christian. This is a bug in Rails that checks the database to infer the type needed for the multiparameter attributes. My guess is that your "date_time" attribute is not associated with a time column in your database.
I recently tackled this problem where I wanted a non-database attribute to accepted multiparameter attributes, this was the best solution I could come up with:
I found myself wanting to set an attr_accessor to handle passing a date to my model in a form_for tag with the f.datetime_select helper. So this is what I had:
Model:
attr_accessor :my_time
View:
<%= f.datetime_select :my_time %>
Unfortunately when I submit my form I get this:
1 error(s) on assignment of multiparameter attributes
Well it turns out that this is actually a Rails bug a ticket for which has been submitted. In the meantime how do we make this work? The only solution I could find that was remotely attractive was to make use of composed_of as a replacement for attr_accessor. so...
Model:
composed_of :my_time,
:class_name => 'Time',
:mapping => %w(Time to_s),
:constructor => Proc.new{ |item| item },
:converter => Proc.new{ |item| item }
I know almost nothing about the composed_of method so you should probably do your own reading on it, but what I do know is that it creates both a reader and writer for the given instance variable, and more importantly, the setter accepts multiparameter attributes. How I chose the options:
class_name: the name of our expected class. In this case, Time
mapping: the first argument is the class and the second argument seems to work with any method that an instance of the class responds to. I chose to_s
constructor: Not really sure how this is supposed to work. Seems to be called when #my_time is nil.
converter: Not really sure how this is supposed to work. Seems to be called when from my_time=, but doesn't seem to be applied with mass assignment.
One problem I ran into with this solution was that times were getting set in UTC instead of the environment's time zone. So unfortunately we cannot use my_time directly, but instead need to convert it to the proper time zone:
Time.zone.parse(my_time.to_s(:number))
What Does ActiveRecord::MultiparameterAssignmentErrors Mean?
def initialize(attributes={})
date_hack(attributes, "deliver_date")
super(attributes)
end
def date_hack(attributes, property)
keys, values = [], []
attributes.each_key {|k| keys << k if k =~ /#{property}/ }.sort
keys.each { |k| values << attributes[k]; attributes.delete(k); }
attributes[property] = values.join("-")
end
I had the same problem using a date dropdown that wasn't backed by a database attribute. I wrote a little Rack middleware to cope with the problem:
class DateParamsParser
def initialize(app)
#app = app
end
def call(env)
if %w{POST PUT}.include? env['REQUEST_METHOD']
params = Rack::Utils.parse_query(env["rack.input"].read, "&")
# selects only relevant params like 'date1(1i)'
filtered_params = params.select{ |key, value| key =~ /\(\di\)/ }
# delete date params
filtered_params.each { |key, value| params.delete(key) }
# returns something like {'date1' => [2012, 5, 14], 'date2' => [2002, 3, 28]}
date_array_params = filtered_params.sort.reduce({}) do |array_params, keyvalue|
date_key = keyvalue.first.match(/(.+)\(/)[1] + ']'
array_params[date_key] ||= []
array_params[date_key] << keyvalue.last
array_params
end
# Creates params with date strings like {'date1' => '2012-5-14', 'date2' => '2002-3-28'}
date_params = Hash[date_array_params.map{ |key, date_array| [key, date_array.join('-')] }]
params.merge! date_params
env["rack.input"] = StringIO.new(Rack::Utils.build_query(params))
env["rack.input"].rewind
end
#app.call(env)
end
end
And in application.rb I put
config.middleware.insert_before ActionDispatch::ParamsParser, "DateParamsParser"
Note that I only build a date string here. So if you also require time you'll need to build the date_params differently.
I faced the same problem with the model below
class Reservation < ActiveRecord::Base
attr_accessor :sid, :check_in, :credit_card_number, :expiration_date
attr_accessible :expiration_date
end
The corresponding form with the field for the expiration date:
<div class="field">
<%= f.label :expiration_date %>
<%= f.date_select(:expiration_date, start_year: Time.now.year + 3, :end_year => Time.now.year - 3, discard_day: true) %>
</div>
as mentioned by #gabeodess the problem is checking the database to infer the type accordingly the solution I did for it was adding the following code to the model to put the type of the needed attribute in this case :expiration_date so the model is modified to be the following
class Reservation < ActiveRecord::Base
attr_accessor :sid, :check_in, :credit_card_number, :expiration_date
attr_accessible :expiration_date
columns_hash["expiration_date"] = ActiveRecord::ConnectionAdapters::Column.new("expiration_date", nil, "date")
end
Hope this is useful
Remove :include_blank => false from your code.
<%= f.datetime_select(:date_time, :prompt => {:day => 'Day', :month => 'Month', :year => 'Year'}, :start_year => Date.today.year, :end_year => Date.today.year + 2, :minute_step => 15 %>
Thanks....
I was facing the same problem.
I just added attr_accessible for that attribute and it works fine.
Hope it helps.

Modifying attributes on the join model with accepts_nested_attributes_for

Simply, a Contact can have various associated Time Windows, which may or may not be Active as a Schedule. To wit:
Models
class Contact < ActiveRecord::Base
has_many :schedules
has_many :time_windows, :through => :schedules
accepts_nested_attributes_for :schedules, :allow_destroy => true
end
class TimeWindow < ActiveRecord::Base
has_many :schedules
has_many :contacts, :through => :schedules
end
class Schedule < ActiveRecord::Base
belongs_to :contact
belongs_to :time_window
end
View
<% TimeWindow.all.each do |tw| %>
<% schedule = Schedule.find_by_contact_id_and_time_window_id(#contact.id, tw.id)
schedule ||= Schedule.new %>
<p>
<%= f.label tw.description %>
<%= hidden_field_tag "contact[schedules_attributes][][id]", schedule.id %>
<%= check_box_tag "contact[schedules_attributes][][time_window_id]",
tw.id, #contact.time_windows.include?(tw) %>
<%= check_box_tag "contact[schedules_attributes][][active]", nil,
schedule.active %>
</p>
<% end %>
This submits something like this:
Parameters: { "commit" => "Update", "contact" => {
"group_ids" => ["2"], "enabled" => "1",
"schedules_attributes" => [ { "time_window_id"=>"1", "id"=>"46"},
{ "time_window_id" => "2", "id" => "42", "active" => "on" },
{ "time_window_id" => "3", "id" => "43"},
{ "time_window_id" => "4", "id" => "44", "active" => "on"}],
"last_name" => ...
The update action in the controller is basically stock, except to handle another instance of another related model which I coded using the "Handling Multiple Models" example from the Advanced Rails Recipes book.
According to this API doc, I think the above ought to work. However, nothing about the Schedules is getting updated. This shows up in the server log:
[4;35;1mSchedule Update (0.2ms)[0m [0mUPDATE `schedules` SET `updated_at` = '2010-09-30 20:39:49', `active` = 0 WHERE `id` = 42[0m
[4;36;1mSchedule Update (0.1ms)[0m [0;1mUPDATE `schedules` SET `updated_at` = '2010-09-30 20:39:49', `active` = 0 WHERE `id` = 44[0m
(NetBeans is giving me those stupid "[0m"'s in the output. I don't know what's wrong there.)
The SQL shows that the "active" boolean field is getting set to 0 where checked. How do I get this to correctly set the active bit?
As a followup, how would I organize this to get rid of the Schedule "connection" at all? I'm thinking I need to submit a :_delete with the Schedule from the form, but how would I do that conditionally when a checkbox is involved?
Thanks for any help you can provide. Rails is turning out to be a vast subject for me, and I want to do it "right." I'm really close here, but there's got to be a way to make this -- not just correct -- but elegant. The view code just feels way too cumbersome to be proper Rails. ;-)
I've kept trying different approaches to this problem, and I've come up with this, which works. Mostly. The only problem is that it doesn't handle NOT having a "Schedule" for each "Time Window". The form will render, and I'll get a disabled check_box (to prevent me from trying to delete something that isn't there), but I don't have a way to add it back, and submitting without it throws off the params hash (and causes Rails to give me an "Expected Hash (got Array)" error)
<% TimeWindow.all.each do |tw| %>
<% schedule = Schedule.find_by_contact_id_and_time_window_id(#contact.id, tw.id)
schedule ||= Schedule.new %>
<% f.fields_for "schedules_attributes[]", schedule do |sf| %>
<p>
<%= sf.label tw.description %>
<%= sf.hidden_field :id %>
<%= sf.check_box :_destroy, :disabled => schedule.new_record? %>
<%= sf.check_box :active %>
</p>
<% end %>
<% end %>
Note that the "schedules_attributes[]" array will automatically give you an existing ID within the braces in your HTML (which is nice), but the _attributes hash is expecting an "id" alongside the other attributes in order to make sense of the sub-hashes.
One of the big lessons I've learned here is that the "check_box_tag" method doesn't (seem to) give me a paired-up hidden field for Rails to parse in the unchecked case. I would have expected this. Adding one in by hand made a mess, which led me to finally giving into the "fields_for" method, and trying many incarnations before finding the appropriate syntax to get what I wanted out of it.
I've realized that my model isn't quite appropriate in this setup, so I'm going to change it, but I was so close to this answer, I wanted to at least get to the point of being able to see the end before I moved on.

Resources