I have a rest api that that is getting a string sent to it. The problem is that I need to use this string (which will always be a number) for an id, which needs to be an integer. I am trying to accomplish this by turning the string into an integer when it is received. This is what I have been trying to do, but I get the error message listed bellow when I try:
def create
respond_with #classroom = Classroom.create(classroom_params)
if #classroom.save
#do stuff
else
#do other stuff
end
end
def classroom_params
params.require(:classroom).permit(:period.to_i, :teacher.to_i, :subject.to_i)
end
Here is the error:
NoMethodError (undefined method `to_i' for :period:Symbol):
app/controllers/api/v1/classroom_controller.rb:42:in `classroom_params'
app/controllers/api/v1/classroom_controller.rb:24:in `create'
Try this. permit method requires a list of symbols to allow. You can only do classroom_params.map(&:to_i) since you want all the parameters as integer. If there is anything in classroom_params that you want as string, you will have to convert the ones that you want as integers explicitly.
def create
attributes = classroom_params
#classroom = Classroom.create(:teacher_id => attributes[:teacher].to_i,
:student_id => attributes[:student].to_i,
:subject_id => attributes[:subject].to_i)
respond_with
if #classroom.save
#do stuff
else
#do other stuff
end
end
def classroom_params
params.require(:classroom).permit(:period, :teacher, :subject)
end
Related
Rails 5.2
In my inventories_controller.rb, I have the following:
before_action :fetch_product, only: [:show]
def show
........
end
def fetch_product
if params.has_key?(:sku)
#product = Product.get_product(params)
end
end
This works fine, when I do: http://0.0.0.0:3000/sku/12345678
I am trying to implement search functionality, so I modified nventories_controller.rb as follows:
def fetch_product
if params.has_key?(:search) && !params[:search].blank?
product = Product.find_by_sku(params[:search])
if !product
params = params.except[:search]
redirect_to product_show_path, alert: 'Product was not found'
end
params = params.merge!(:sku, product.sku)
end
if params.has_key?(:sku)
#product = Product.get_product(params)
end
end
When I do: http://0.0.0.0:3000/sku/12345678
I get an instant error message:
undefined method `has_key?' for nil:NilClass
Using my debugger, I find that on entry into the fetch_product method, params is nil
Any idea what's going on?
params = params.merge!(:sku, product.sku) modifies the hash in place and returns nil, don't do that assignment, just call params.merge! (if you still want to do the assignment, remove the "!").
Personally, I wouldn't modify the params hash unless it's really really needed, I would use another variable.
I am trying to learn and write an update API and to start small I am passing a single params in the API and and try to get the response.
the controller :
module Api
module V1
class OrderApiController < ApiController
def order_update
response = Hash.new
result = Hash.new
#order = Order.find(params[:id])
if #order.update_attributes(order_params)
result['order_id'] = order.id
response['result'] = result
response.merge! ApiStatusList::OK
else
response.merge! ApiStatusList::INVALID_REQUEST
end
render :json => response
end
private
def order_params
params.require(:order).permit( :id)
end
end
end
end
the api route in routes.rb is:
match 'mobile/order_update' =>'order_api#order_update'
The url link what I give is
http://localhost:3000/api/v1/mobile/order_update?key=docket&id=1
However this throws the following error
ActionController::ParameterMissing at /api/v1/mobile/order_update
param is missing or the value is empty: order
I dont know what am I doing wrong. I am new to Rails as well as API generation. Please help
This is caused by the order_params method, in which you're requiring order(expecting order to be a nested hash), whereas, you're not nesting it.
An approach you could take is to visit:
http://localhost:3000/api/v1/mobile/order_update?key=docket&order[id]=1
Also, I see you're setting #order instance variable, but in your control block(if #order.update_attributes), you're using a local variable which would give you another error.
I'd recommend you go through the Hartl Tutorial as there are a lot of things you'd be able to learn from there
UPDATE
Based on the new error mentioned in the comment, I think you should rather be visiting:
http://localhost:3000/api/v1/mobile/order_update?order[key]=docket&id=1
This is assuming your orders table has a column key based on the params being set
Also, change your order_params to:
private
def order_params
params.require(:order).permit( :key) #since you cannot update a primary key, but I guess you want to update the key column
end
The solution I used is as follows
In my order_api_controller.rb , I have Changed
def order_update
response = Hash.new
result = Hash.new
#order = Order.find(params[:id])
if #order.update_attributes(order_params)
result['order_id'] = order.id
response['result'] = result
response.merge! ApiStatusList::OK
else
response.merge! ApiStatusList::INVALID_REQUEST
end
render :json => response
end
and edited it to this
def order_update
response = Hash.new
result = Hash.new
debugger
#order = Order.find(params[:order][:id]) # passed the order symbol into params
if #order.update_attributes(order_params)
result['order_id'] = #order.id # Modified local variable to instance variable as per oreoluwa's suggestion
response['result'] = result
response.merge! ApiStatusList::OK
else
response.merge! ApiStatusList::INVALID_REQUEST
end
render :json => response
end
And used the url as Follows
http://localhost:3000/api/v1/mobile/order_update?key=docket&order[id]=1
This seems to do the trick
I have a piece of code in Rails,
def create
#registration = Registration.new(registration_params)
if #registration.save
redirect_to #registration.paypal_url(registration_path(#registration))
else
render :new
end
end
I took it from tutorial. But I need just in this line:
#registration.paypal_url(registration_path(#registration))
Now, about my own controller, feed_controller, where
def create
#feed = Feed.new(check_params)
end
In the view erb file I put:
#feed.paypal_url(feed_path(#feed))
In my feed.rb (model):
def paypal_url(return_path)
values = {
business: "merchant#gotealeaf.com",
cmd: "_xclick",
upload: 1,
return: "#{Rails.application.secrets.app_host}#{return_path}",
invoice: id,
amount: course.price,
item_name: course.name,
item_number: course.id,
quantity: '1'
}
"#{Rails.application.secrets.paypal_host}/cgi-bin/webscr?" + values.to_query
end
Rake routes:
feed GET /:locale/feed(.:format) feed#index
feed#create POST /:locale/feed/create(.:format)
feed#new feed_new GET /:locale/feed/new(.:format)
feed#destroy feed_destroy GET /:locale/feed/destroy(.:format)
feed#edit feed_edit GET /:locale/feed/edit(.:format)
feed#update feed_update GET /:locale/feed/update(.:format)
But it prints the next error:
undefined method `paypal_url' for <#Feed::ActiveRecord_Relation:0x007fee24f5fc98>
How can I fix it? What is the problem?
UPDATE
def index
#current_user_is = current_user.email
session[:email] = #current_user_is
session[:id] = current_user.id
unless (current_user.member.present?)
#member = Member.new(:user_id => current_user.id)
#member.save()
redirect_to '/feed'
else
#new_feed = Feed.new
#feed = Feed.where(:member_id => current_user.member.id)
#category = Category.all
render 'home/uploads'
end
end
Simply use def self.paypal_url(return_path) instead of def paypal_url(return_path).
Explanation
You ran into your problem by defining a Class Method instead of an Instance Method, there's other posts discussing this.
The basic difference is, when defining:
def self.get_some_url
# code to return url of an instance
end
you can easily get the desired url of any objects, as in a view:
<% #feeds.each do |feed| %>
<%= feeds.get_some_url %>
<% end %>
Now calling Feed.get_some_url on the class would make no sense. Which url of the thousands would it call?
But there is a lot of use for class methods (where you define the method without self as you did)
def get_top_5
# code to return the top 5 most viewed feeds
end
Since this has nothing to do with a single instance, you define it for the entire Class. Leading to this call: Feed.get_top_5, which makes perfectly sense.
The second problem was not understanding the difference between where & find, this post will help you out with that.
This is more a style question than anything.
When writing queries, I always find myself checking if the result of the query is blank, and it seems - I dunno, overly verbose or wrong in some way.
EX.
def some_action
#product = Product.where(:name => params[:name]).first
end
if there is no product with the name = params[:name], I get a nil value that breaks things.
I've taken to then writing something like this
def some_action
product = Product.where(:name -> params[:name])
#product = product if !product.blank?
end
Is there a more succinct way of handling nil and blank values? This becomes more of a headache when things rely on other relationships
EX.
def some_action
#order = Order.where(:id => params[:id]).first
# if order doesn't exist, I get a nil value, and I'll get an error in my app
if !#order.nil?
#products_on_sale = #order.products.where(:on_sale => true).all
end
end
Basically, is there something I haven;t yet learned that makes dealing with nil, blank and potentially view breaking instance variables more efficient?
Thanks
If its just style related, I'd look at Rails' Object#try method or perhaps consider something like andand.
Using your example, try:
def some_action
#order = Order.where(:id => params[:id]).first
#products_on_sale = #order.try(:where, {:onsale => true}).try(:all)
end
or using andand:
def some_action
#order = Order.where(:id => params[:id]).first
#products_on_sale = #order.andand.where(:onsale => true).andand.all
end
Well even if you go around "nil breaking things" in your controller, you'll still have that issue in your views. It is much easier to have one if statement in your controller and redirect view to "not found" page rather than having several ifs in your views.
Alternatively you could add this
protected
def rescue_not_found
render :template => 'application/not_found', :status => :not_found
end
to your application_controller. See more here: https://ariejan.net/2011/10/14/rails-3-customized-exception-handling
I have a create method that calls a method in a model that pings some third-party APIs.
What I need to do is if the API sends back a certain message, then I'd display an error.
Below is my current controller and model setup, so how would I get the error back in to the controller (and ultimately the view)?
Here is the method in my controller:
def create
#number = Number.where(:tracking => params[:number][:tracking], :user_id => current_user.id).first
if #number.blank?
#number = Number.new
#number.tracking = params[:number][:tracking]
#number.user_id = current_user.id
#number.notes = params[:number][:notes]
#number.track
end
respond_with(#number) do |format|
format.html { redirect_to root_path }
end
end
Here are the methods in my model:
def track
create_events
end
def create_events(&block)
tracker = fedex.track(:tracking_number => number)
if tracker.valid?
self.assign_tracker(tracker)
tracker.events.each do |e|
self.create_event(e) unless (block_given? && !block.call(e))
end
save
else
# NEED TO THROW THE ERROR HERE
end
end
How about if rather than throwing errors, you just use validation? Something like the following (Just to get your started. This would need work.):
# if you don't cache the tracker in an attribute already, do this so
# you can add errors as if it were a column.
attr_accessor :tracker
def create_events(&block)
tracker = fedex.track(:tracking_number => number)
if tracker.valid?
# ...
else
# add the error with i18n
errors.add(:tracker, :error_type_if_you_know_it)
# or add it from a returned message
errors.add(:tracker, nil, :message => fedex.get_error())
end
end
Then in your controller:
#number.track
respond_with(#number) do |format|
if #number.errors.any?
format.html { redirect_to root_path }
else
format.html { render :some_template_with_errors }
end
end
Alternatively you could do this as part of validation (so calling valid? would work as expected and not destroy your custom "track" errors)
# do your tracking on creation, if number was given
validate :on => :create do
if number.present?
tracker = fedex.track(:tracking_number => number)
unless tracker.valid?
errors.add :tracker, nil, :message => tracker.get_error()
end
end
end
# then do your actual creation of tracking events sometime after validation
before_save :handle_tracker_assignment
def handle_tracker_assignment
self.assign_tracker(tracker)
# note the block method you're using would need to be reworked
# ...
end
Note in the latter case you'd have to change your logic a bit, and simply pass the tracking number and attempt to save a new record, which would trigger the tracking attempt.
You should typically offload the the API calls to a background job and you could either use notifiers (or Rack middleware) to raise self-defined errors and handle them accordingly.