Wrong number of arguments in as_json [`timeout` method is reserved!] - ruby-on-rails

I get an error in Rails that is related to one of my models, let's call it Unit. The following code produces an error:
format.json { render :json => #units.as_json }
The error is about wrong number of parameters (0 of 1).

I believe what you want is;
def scheme
#object = MonitoringObject.restricted_find params[:id], session[:client]
#units = Unit.where("object_id = ?", #object.id)
respond_to do |format|
format.html
format.json { render :json => #units[0] }
end
end
Or, if you have your relationships set up between the MonitoringObject and Unit models correctly,
def scheme
#object = MonitoringObject.restricted_find params[:id], session[:client]
#units = #object.units
respond_to do |format|
format.html
format.json { render :json => #units[0] }
end
end
You're supplying it with an :except parameter that's empty.
You'll use an :except condition if there are some attributes you don't want included in the JSON response.
According to the rails guide on views and rendering, you don't need to specify .to_json -- it will be called for you automatically.
It looks to me that the problem may lie in your .restricted_find method somewhere. Can you post the entire stack trace )or link to a github gist that contains it?

I solved the problem. From the Rails source:
module Serialization
def serializable_hash(options = nil)
options ||= {}
attribute_names = attributes.keys.sort
if only = options[:only]
attribute_names &= Array.wrap(only).map(&:to_s)
elsif except = options[:except]
attribute_names -= Array.wrap(except).map(&:to_s)
end
hash = {}
attribute_names.each { |n| hash[n] = read_attribute_for_serialization(n) } # exception here
# ...
end
alias :read_attribute_for_serialization :send
# ...
end
# ...
end
So the real error is that one of the methods returned by calling e.g. Unit.first.attributes.keys.sort (["dev_id", "flags", "id", "inote", "ip", "location_id", "model_id", "name", "period", "phone", "port", "snote", "timeout"]) expects me to pass an argument to it. This method is timeout, it is some private method Rails monkey patches Object with. So the correct solution to the problem is to rename this attribute to something else.

Related

NoMethodError: undefined method `get' for RSpec

First of all, this is my first experience with ruby. At this moment, I'm creating tests for the a Controller called Exporter in my application. The method of the Controller I want to test is this:
def export_as_json(equipments)
equipments_json = []
equipments.each {|equipment|
equipment_json = {
:id => equipment.id,
:title => equipment.title,
:description => equipment.description,
:category => equipment.category_id
}
equipments_json << equipment_json
}
respond_to do |format|
format.json { render :json =>equipments_json }
end
end
So, when I try to create a request for this method using this:
RSpec.describe ExporterController, type: :controller do
get '/equipments/all', headers: { 'CONTENT_TYPE' => 'application/json' }, format: :json
expect(response.response).to eq(200)
end
inside the exporter_controller_test.rb file I'm receiving this error:
NoMethodError: undefined method `get' for RSpec::ExampleGroups::ExporterController:Class
This is one of the problems pretty much every one runs into at least once ;)
Step 1: Read the error message very carefully
NoMethodError: undefined method 'get' for RSpec::ExampleGroups::ExporterController:Class
Step 2: Remember the wording NoMethodError: undefined method get for RSpec::ExampleGroups::XXX:Class
Step 3: Solve it by making it an actual example
RSpec.describe ExporterController, "#index", type: :controller do
it "should respond with status: 200" do
get '/equipments/all', headers: { 'CONTENT_TYPE' => 'application/json' }, format: :json
expect(response.response).to eq(200)
end
end
You were simply missing the it block.
I know this is not an answer to your question. But, since you mentioned that you're new to ruby, I thought I would point out that your code could be simplified and prettified a bit.
First, you don't need to do equipments_json = [] and then equipments.each. That's what map is for:
def export_as_json(equipments)
equipments_json = equipments.map{|equipment| {
:id => equipment.id,
:title => equipment.title,
:description => equipment.description,
:category => equipment.category_id
}
}
respond_to do |format|
format.json { render :json =>equipments_json }
end
end
Now, that hash you're putting into equipments_json is just a subset of equipment's attributes. So, use slice there to get the attributes you want:
def export_as_json(equipments)
equipments_json = equipments.map{|equipment| equipment.attributes.slice('id','title','description','category_id')}
respond_to do |format|
format.json { render :json =>equipments_json }
end
end
That map line is still a little long, so, maybe put it into a do block (like you had with each):
def export_as_json(equipments)
equipments_json = equipments.map do |equipment|
equipment.attributes.slice('id','title','description','category_id')
end
respond_to do |format|
format.json { render :json =>equipments_json }
end
end
And personally, I like using symbols instead of strings as my keys, so use with_indifferent_access so that you can use symbols:
def export_as_json(equipments)
equipments_json = equipments.map do |equipment|
equipment.attributes.with_indifferent_access.slice(:id, :title, :description, :category_id)
end
respond_to do |format|
format.json { render :json =>equipments_json }
end
end
That line got a little to long again, so I think I would go ahead and wrap it:
def export_as_json(equipments)
equipments_json = equipments.map do |equipment|
equipment.
attributes.
with_indifferent_access.
slice(:id, :title, :description, :category_id)
end
respond_to do |format|
format.json { render :json =>equipments_json }
end
end
Now, there are some different ways to get those attributes you want (e.g., modifying to_json). But, this will get the job done.
Hope that helps and good luck!
PS: I just noticed in your original hash, you're doing:
:category => equipment.category_id
if that's not a typo and you really want category instead of category_id, then you could do something like:
def export_as_json(equipments)
equipments_json = equipments.map do |equipment|
equipment.
attributes.
with_indifferent_access.
slice(:id, :title, :description).
merge!(category: equipment.category_id)
end
respond_to do |format|
format.json { render :json =>equipments_json }
end
end
Also, the convention for hashes is to do title: equipment.title. :title => equipment.title absolutely works, but is not the current convention. This is a style guide for ruby, in case it helps.

Rails 4: How do I handle a submitted form where nothing was selected?

Sorry if the title is a little confusing. I have a form for an Item with the field name. There's a text field where the user can input a name and submit it. But if the user doesn't type in anything and hits submit, Rails gives me a param not found: item error, and I'm not sure who to get around this.
items_controller.rb
def new
#item = Item.new()
respond_to do |format|
format.html
format.json { render json: #item }
end
end
def create
#item = Item.new(item_params)
respond_to do |format|
if #item.save
format.html { redirect_to items_path }
format.json { render json: #item, status: :created, location: #item }
else
format.html { render action: 'new', :notice => "Input a name." }
format.json { render json: #item.errors, status: :unprocessable_entity }
end
end
end
private
def item_params
params.require(:item).permit(:name)
end
app/views/items/new.html.haml
= form_for #item do |f|
= f.label :name
= f.text_field :name
= f.submit "Submit"
The params.require(:item) part is what is causing the error. What the convention for handling the error when params[:item] isn't present?
It's late for an answer but i'll still write it for someone else. As stated in rails guides you need to use fetch instead of require in strong parameters, by using fetch you can provide a default value if nothing is passed as input. Something like:
params.fetch(:resource, {})
Update:
Scaffolded rails4 app:
https://github.com/szines/item_17751377
It works if a user keep name field empty when create a new item...
Looks, it works without problem...
Development.log shows that parameters would be the following if user keep a field empty:
"item"=>{"name"=>""}
There is always something in the hash...
As Mike Li already mentioned in a comment, something wrong... because shouldn't be empty this params[:item]...
You can check if something nil, with .nil? , in this case params[:item].nil? will be true if it is nil. Or you can use .present? as sytycs already wrote.
Previous answer:
If you have situation when :item is empty, you should just use params[:item] without require.
def item_params
params[:item].permit(:name)
end
More information about require in strong_parameters.rb source code:
# Ensures that a parameter is present. If it's present, returns
# the parameter at the given +key+, otherwise raises an
# <tt>ActionController::ParameterMissing</tt> error.
#
# ActionController::Parameters.new(person: { name: 'Francesco' }).require(:person)
# # => {"name"=>"Francesco"}
#
# ActionController::Parameters.new(person: nil).require(:person)
# # => ActionController::ParameterMissing: param not found: person
#
# ActionController::Parameters.new(person: {}).require(:person)
# # => ActionController::ParameterMissing: param not found: person
def require(key)
self[key].presence || raise(ParameterMissing.new(key))
end
I personally have not switched to strong parameters so I'm not sure how one should handle something like:
params.require(:item).permit(:name)
but you can always check for item presence with something like:
if params[:item].present?
…
end

Overriding as_json method with params

First of all, I'm using Rails 3.0.6 and Ruby 1.9.2
I have a controller with two different actions, both should return a json object, but with different formats. Therefore I'm overriding the as_json method to write the JSON object in my own format. Problem is that I don't know how to pass params to as_json method since it's being automatically called by Rails.
My code looks like this:
class MyController < ApplicationController
def action1
# my code
respond_to do |format|
# Render with :json option automatically calls to_json and this calls as_json
format.js { render :json => #myobjects }
end
end
def action2
# a different code
respond_to do |format|
# This action should return a JSON object but using a different format
format.js { render :json => #myobjects }
end
end
end
class MyModel < ActiveRecord::Base
def as_json(options = {})
# I would like to add a conditional statement here
# to write a different array depending on one param from the controller
{
:id => self.id,
:title => self.description,
:description => self.description || "",
:start => start_date1.rfc822,
:end => (start_date1 && start_date1.rfc822) || "",
:allDay => true,
:recurring => false
}
end
end
Note that #myobjects are a collection of objects which class is MyModel.
Any help would be appreciated. Thank you!
Call it explicitly in controller and pass params. as_json will return string and calling as_json on string returns itself. It is quite common practice.
respond_to do |format|
# Render with :json option automatically calls to_json and this calls as_json
format.js { render :json => #myobjects.as_json(params) }
end

How to DRY up Rails 3 controllers by overriding methods like respond_with?

I'm trying to create a JSONP API for my Rails 3 application. Right now in my controllers, I have a lot of actions which follow this pattern:
# This is from my users_controller.rb, as an example
def index
#users = User.all
respond_with(#users, :callback => params[:callback])
end
While this works as is, I would like to DRY it up by not having to repeat the :callback => params[:callback] in every action's call to respond_with. How can I do this?
Update: One thing I've realized that is ugly about my above code is that the :callback => params[:callback] option will be passed for any response format, not just JSON. The following code is probably more correct:
def index
#users = User.all
respond_with(#users) do |format|
format.json { render :json => #users, :callback => params[:callback]}
end
end
There are a couple ways I've considered to address this problem, but I can't figure out how to make them work:
Override render (perhaps in the application controller) so that it accepts a :jsonp option that automatically includes the :callback => params[:callback] parameter. This way I could change the above code to the following, which is somewhat shorter:
def index
#users = User.all
respond_with(#users) do |format|
format.json { render :jsonp => #users}
end
end
Create a responder that overrides to_json in order to solve my problem. That way I could leave out the block and just call respond_with(#users, :responder => 'MyResponder') to solve the issue. Or perhaps I could include this code in an application responder using plataformatec's responders gem so that respond_with(#users) by itself would be sufficient.
Note that technically, it is incorrect to render JSON with a callback parameter, since you get a JavaScript response (a function call to the JSON-P callback) rather than a JSON result.
So if you have
render :json => my_object, :callback => params[:callback]
and a request for /users?callback=func comes in, Rails would answer
func({…})
with content type application/json, which is incorrect, since the above response is clearly not JSON but JavaScript.
The solution I use is
def respond_with_json(item)
respond_with do |format|
format.json { render :json => item }
format.js { render :json => item, :callback => params[:callback] }
end
end
which responds correctly with or without callback. Applying this to the aforementioned solution, we get:
def custom_respond_with(*resources, &block)
options = resources.extract_options!
if params[:callback]
old_block = block
block = lambda do |format|
old_block.call(format) if block_given?
format.js { render :json => resources[0], :callback => params[:callback] }
end
end
respond_with(*(resources << options), &block)
end
Also note the correction to resources[0], otherwise you end up wrapping resources in an extra array as a result of the splat operator.
THere's a gem that can do this to: rack-jsonp-middleware.
The setup instructions are pretty scant on the site, but I did create a little Rails project that uses it - which you can take a look at the commits and see what I did to get the middleware up and running.
https://github.com/rwilcox/rack_jsonp_example
This is bit 'low-tech' compared to the reponder solution, but what about just creating a private method in your appliation_controller.rb to handle this. The params variable will be available to it and you could pass the #users object to it.
#application_controller.rb
private
def jsonp(my_object)
render :json => my_object, :callback => params[:callback]
end
#controller
def index
#users = User.all
respond_with(#users) do |format|
format.json { jsonp(#users)}
end
end
Thanks to samuelkadolph for helping me in the #rubyonrails IRC channel today. He provided a solution in this gist, copied below for convenience:
def custom_respond_with(*resources, &block)
options = resources.extract_options!
if options[:callback]
old_block = block
block = lambda do |format|
old_block.call(format) if block_given?
format.json { render :json => [] }
end
end
respond_with(*(resources << options), &block)
end
I haven't tried this in my application yet, but I can see that it should work. He also confirmed that I could similarly override the respond_with method itself simply by changing the name of this method and changing the last line of the definition to super(*(resources << options), &block).
I think this will work for me. However, I'm still interested in knowing how to write a custom responder to do the job. (It would be a more elegant solution, IMHO.)
Update: I tried this in my application and it works with some minor changes. Here is the version I'm using now in the private section of my ApplicationController, designed to automatically provide the :callback => params[:callback] option to JSON requests:
def custom_respond_with(*resources, &block)
options = resources.extract_options!
if params[:callback]
old_block = block
block = lambda do |format|
old_block.call(format) if block_given?
format.json { render :json => resources, :callback => params[:callback] }
end
end
respond_with(*(resources << options), &block)
end
Note that I had to change if options[:callback] to if params[:callback] in order to get it working.
You can also check out this answer. basically you can create a "default" respond_to for your controller so you can just make your all your actions default to responding to json.
was that what you were asking?

How do I deal with an authorization hiccup because of bad controller naming?

I seem to have an authorization hiccup in my Ruby on Rails app. I have been using the following method in my application controller and it has been working beautifully.
def require_owner
obj = instance_variable_get("##{controller_name.singularize.camelize.underscore}") # LineItem becomes #line_item
return true if current_user_is_owner?(obj)
render_error_message("You must be the #{controller_name.singularize.camelize} owner to access this page", root_url)
return false
end
I then filter in the specific controllers by:
before_filter :require_owner, :only => [:destroy, :update, :edit]
I recently created a new controller which has a bit of a different naming convention that seems to be causing a problem. Normally my controllers read messages_controller or posts_controller. In this specific case I named the resource box_wod which generated box_wods_controller.
This is the only controller that seems to be having a problem with this filter so I bet I can tell it is in the naming of it and therefore the application_controller method is not recognizing the owner of the record.
I am not getting an error message but the application is not letting me edit, update or destroy a record because I am not the BoxWod owner. My routes are correct as are my associations and the correct information is getting passed to the box_wod table.
Is there a way to rewrite the application_controller method to recognize the additional underscore in the box_wod resource? Or is this even my problem?
UPDATE:
Here are the three methods in the BoxWodsController:
def edit
#workout_count = Workout.count
#box_wod = BoxWod.find(params[:id])
end
def update
#box_wod = BoxWod.find(params[:id])
respond_to do |format|
if #box_wod.update_attributes(params[:box_wod])
flash[:notice] = 'BoxWod was successfully updated.'
format.html { redirect_to(#box_wod) }
format.xml { head :ok }
else
format.html { render :action => "edit" }
format.xml { render :xml => #box_wod.errors, :status => :unprocessable_entity }
end
end
end
def destroy
#box_wod = BoxWod.find(params[:id])
#box_wod.destroy
respond_to do |format|
format.html { redirect_to(box_wods_url) }
format.js
end
end
In situations like this, I like to create a controller method that I can override when necessary. For example:
# application_controller.rb
class ApplicationController
def require_owner
obj = instance_variable_get("##{resource_instance_variable_name}")
# Do your authorization stuff
end
private
def resource_instance_variable_name
controller_name.singularize.camelize.underscore
end
end
# box_wods_controller.rb
class BoxWodsController
private
def resource_instance_variable_name
'box_wod' # Or whatever your instance variable is called
end
end
Lastly, please post your BoxWodsController code so we can better diagnose the problem.
It would seem that the #box_wod instance variable is not created until the require_owner method is invoked so current_user_is_owner? is checking a nil value, resulting in it always returning false. Perhaps you need another before_filter to populate the instance variable before require_owner is invoked.

Resources