Ember remove record on invalid server validation - ruby-on-rails

I am trying to create a record and on the server side I am using rails. The rails validations are failing and I am returning a 422 status code but when I delete it in the becameInvalid callback, it doesn't get removed from the template. It just shows a blank entry.
When it is waiting for the server to load it is just showing the name, which is expected.
Ember Model code
App.Job = DS.Model.extend({
name : DS.attr("string"),
user : DS.belongsTo("App.User", {embedded : "load"}),
plans : DS.hasMany("App.Plan", {embedded : "load"}),
shares : DS.hasMany("App.Share", {embedded : "load"}),
becameInvalid : function(data){
this.deleteRecord();
}
});
Ember controller call
PlanSource.Job.createRecord({"name" : name});
job.save();
Rails create method
def create
if can? :create, Job
#job = Job.new(name: params["job"]["name"], user_id: current_user.id)
if !#job.save
render :json => {job: #job}, status: :unprocessable_entity
return
end
if
render :json => {:job => #job}, include: [:plans, :user, :shares => {except: :token, include: [:user, :job]}]
else
render_no_permission
end
else
render_no_permission
end
end
My question is what is the best way to handle server side validation errors. I don't want to try to resubmit the record, I just want to delete it. I was looking for something to make Ember wait for server response but found nothing.
This method isn't working because it causes undefined errors down the model pipeline after deleting.

My question is what is the best way to handle server side validation errors.
Not sure there is one best way. Depends on what you want to happen in your UI. Typically you will want to let the user know that the record was not saved and present some information what went wrong.
I don't want to try to resubmit the record, I just want to delete it.
OK. If the record is new, delete does not really make sense but probably what you want to do is rollback the transaction? Try this.transaction.rollback() or this.rollback(). For example:
App.Job = DS.Model.extend({
name : DS.attr("string"),
user : DS.belongsTo("App.User", {embedded : "load"}),
plans : DS.hasMany("App.Plan", {embedded : "load"}),
shares : DS.hasMany("App.Share", {embedded : "load"}),
becameInvalid : function(data){
this.transaction.rollback();
}
});
See: How to deleteRecord when it was never committed on the backend?
I was looking for something to make Ember wait for server response but found nothing.
model.save() returns a promise. That means you can add success/failure handlers like this:
PlanSource.Job.createRecord({"name" : name});
var success = function(model) {
alert('ok');
};
var failure = function(model) {
alert('fail');
};
job.save().then(success, failure);

Related

How to Consume SOAP with Savon in Rails app

I need to communicate to a service called ifthenpay via Soap using Savon on a Rails app that i'm working on.
The service generates payment references so users could pay on home banking or in cash machines.
The app needs to communicate to the service to see if the payment was made or not.
I'm using Savon and this is what i have so far in the checkout model(don't know if this is the right place to put the above code):
def self.check_status!
client = Savon.client(
wsdl: "http://www.ifthensoftware.com/IfmbWS/IfmbWS.asmx?WSDL",
endpoint: "http://localhost:3000",
namespaces: {"xmlns:ift"=>"https://www.ifthensoftware.com/"}
)
begin
response = client.call(:get_payments, message: check_status_hash)
rescue Savon::SOAPFault => error
#...
end
end
def self.check_status_hash
{
"ift:get_payments" => {
"ift:chavebackoffice" => { "ift:chavebackoffice" => "0000-0000-0000-0000" },
"ift:entidade" => {"ift:entidade" => "11202"},
"ift:subidentidade" => {"ift:subidentidade" => "202"},
"ift:dtHrInicio" => {"ift:dtHrInicio" => ""},
"ift:dtHrFim" => {"ift:dtHrFim" => ""},
"ift:referencia" => {"ift:referencia" => ""},
"ift:valor" => {"ift:valor" => ""}
}
}
end
I've an admin page where i need to list all the payments that have been made, so i can manage what was selled.
You can see the service operations here
What do i need to put in the controller and in the view for this to work?
I really appreciate your help, because i'm struggling with this for a long time.
From my point of view, and pardon me because I'm not very experienced with the use of savon, you are slightly overkilling this.
To start with, you are providing the client with a WSDL url, so what is the use of attaching a doubtfully necessary endpoint?
A namespace is, to my understanding, necessary, once again, in case there is no standard WSDl interface.
I would go, to start off, I would simply go for:
#client = Savon.client(wsdl: "http://www.ifthensoftware.com/IfmbWS/IfmbWS.asmx?WSDL")
Watch the #client instead of client. We need to assign the client to a variable that will be reachable throughout the entire process (request, process, response).
Next, you will need to prepare your request. Parsing the above url, there is a banch of methods. You are providing in your example getPayments request.
I will not use this space to tell you how to construct the hash, but the hash should look something like this:
request_hash = {
chavebackoffice: "0000-0000-0000-0000",
entidade: "11202",
subidentidade: "202",
dtHrInicio: "",
dtHrFim: "",
referencia: "",
valor: ""
}
To make the call to the api, you should simply do this:
#response = #client.call(:get_payments) do
message request_hash
end
And then, parse the #response. You will probably need to turn it to a hash first. Maybe something like this:
#data = #response.to_hash[:get_payments_response][:get_payments_result][:ifmb]
I hope this will help you enough. It should be more than enough.
Putting all up: Controller code, adapt to your need
before_action :set_client, only: [:get_payments, :other_actions_perhaps]
def get_payments
# params[:whatever] in case you post to #whatever object
# params without [:whatever] if you are using "GET" method
request_hash = {
chavebackoffice: params[:whatever][:chavebackoffice],
entidade: params[:whatever][:entidade],
subidentidade: params[:whatever][:subidentidade],
dtHrInicio: params[:whatever][:dtHrInicio],
dtHrFim: params[:whatever][:dtHrFim],
referencia: params[:whatever][:referencia],
valor: params[:whatever][:valor]
}
response = #client.call(:get_payments) do
message request_hash
end
# use this #data in your view
#data = response.to_hash[:get_payments_response][:get_payments_result][:ifmb]
end
def set_client
#client = Savon.client(wsdl: "http://www.ifthensoftware.com/IfmbWS/IfmbWS.asmx?WSDL")
end

Angular.js Rails Resource Association Update

I am working on an angular app w/ rails using Angular Rails Resource. When a user updates a photo, I want to query an association and update an attribute to true.
I have read through the Angular Rails Resource docs but I don't see much info on querying associations. Would this be an angular thing, or a rails thing?
Here is my angular code:
$scope.uploadHeadshot = ->
Upload.upload(
url: window.apiUrl + '/personas/' + $routeParams.unique_code + '/' + $routeParams.slug
method: 'PUT'
data: persona: headshot_attributes:
image: $scope.headshot
crop_x: $scope.cropAttributes.cropImageLeft
crop_y: $scope.cropAttributes.cropImageTop
crop_w: $scope.cropAttributes.cropImageWidth
crop_h: $scope.cropAttributes.cropImageHeight
# right here
badge_attributes: completed: true).then ((response) ->
$mdDialog.hide response.data
resizeImage(false)
return
)
rails code
def update
#persona = Persona.friendly.find params[:id]
#persona.assign_attributes persona_params
return unprocessable_entity 'Invalid parameters', #persona.errors unless #persona.save
render json: #persona, include: %w(modules owner badges)
end
and in the persona_params I have the attrribute
badge_attributes: [:id, :completed]
I also have a attr on badge of badge_type so from the angular side I could get the current user, do a badge.where(badge_type: 'badge type').update(completed: true) but I'm not sure how to do that from angular. Or would this be a rails thing where you could do something like this from #update
if params[:badge_attributes]
Badge.where(badge_type: 'badge type').first.update(completed: true)
end
What I have posted doesn't do anything, but it doesnt break the photo upload from happening, and I can see the badge_attribute params being sent. Does anyone know how I could accomplish this?
I was able to get this solved by doing the heavy lifting with rails.
if params[:badge_attributes]
badge = Badge.where(persona_id: current_persona.id, badge_type: params[:badge_attributes][:badge_type]).first
badge.update_attributes(completed: true)
end

Destroying child record destroys parent

EDIT: I solved my problem. Instead of a type 'DELETE' AJAX call, I used a type 'POST' call with the {:method: 'DELETE'} as its data value. This seemed to do the trick.
I'm trying to allow deletion of a child element in Rails 4 via an AJAX request, and I'm having this issue. I have a Badge model which has many BadgeElements. The BadgeElements controller contains a destroy method which I know works because in my Show view, the elements are properly being deleted. It's when I try to delete them in my JavaScript file with AJAX that I'm having trouble. Here's my code:
function deleteElement(id) {
var path_root = $('form').attr('action');
var delete_path = path_root + "/badge_elements/" + id;
$.ajax({
url: delete_path,
type: 'DELETE'
});
$('form').load(path_root + "/edit form");
}
The delete_path attribute evaluates to /badges/[badge_id]/badge_elements/[id], where [id] is the id of the specific element in question. When this function is executed, I get a 400 error at the path ending in /badges, which tells me the badge itself is being deleted. I can confirm this by reloading the page and getting the error "Could not find Badge with badge_id = 4", etc. For reference, here's the model and destroy method for BadgeElement:
badge_element.rb:
class BadgeElement < ActiveRecord::Base
belongs_to :badge
belongs_to :font
scope :front, -> {where(side:"front")}
scope :back, -> {where(side:"back")}
validates :font_id, presence: true
end
in badge_elements_controller.rb:
def destroy
badge = Badge.find(params[:badge_id])
badge_element = BadgeElement.find(params[:id])
badge_element.destroy
redirect_to badge_path(badge)
end
Does anyone know what's going on? Thanks.
I've just had this problem.
If your controller has a redirect_to and no sensitivity to the format of the request it will redirect to badge_path(badge) as instructed but the method will still be DELETE, thereby deleting the parent as well as the child.
I think you'll just need a respond_to block like so
def destroy
...
respond_to do |format|
format.html { redirect_to badge_path(badge) }
format.js { }
end
end

POST method/Create action works for some methods but not others, Feedzirra

Ok I am making a JQuery .post request to a server to insert some data. It only works sometimes depending on what methods are in the Rails create method.
Here are the specifics. I have a Rails app with Backbone.js on the frontend. In my frontend code I make this .post request
$.post('/publications');
Seems simple enough. I have a Publication model as well as
resources :publications
in the router. Now in my Publications controller I augmented the create method to the following:
def create
feed = Feedzirra::Feed.fetch_and_parse(publication_params[:url])
params = {:name => feed.title}
#publication = Publication.new(params)
respond_to do |format|
if #publication.save
format.html { redirect_to #publication, notice: 'Publication was successfully created.' }
format.json { render action: 'show', status: :created, location: #publication }
else
format.html { render action: 'new' }
format.json { render json: #publication.errors, status: :unprocessable_entity }
end
end
end
Feedzirra is a gem that parses RSS feeds. When I make the POST request like this I get a 500 (Internal Server Error) and this message from my server logs
NoMethodError (undefined method `title' for {}:Hash):
app/controllers/publications_controller.rb:28:in `create'
Rendered /Users/ericabt1/.rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0/gems/actionpack-4.0.0/lib/action_dispatch/middleware/templates/rescues/_source.erb (40.3ms)
Rendered /Users/ericabt1/.rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0/gems/actionpack-4.0.0/lib/action_dispatch/middleware/templates/rescues/_trace.erb (1.1ms)
Rendered /Users/ericabt1/.rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0/gems/actionpack-4.0.0/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb (1.0ms)
Rendered /Users/ericabt1/.rbenv/versions/2.0.0-p247/lib/ruby/gems/2.0.0/gems/actionpack-4.0.0/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb within rescues/layout (54.7ms)
What is confusing me is that if I choose a different method than 'title' like 'entries' or 'nil?' the POST request works just fine. I know that 'title' is indeed a method because when I go into Rails console and create a test Feedzirra object and look at the various methods available I see that 'title' is one of them.
Why does my POST request work for some of these method but not others?!?!?!?!
* UPDATE ****
After taking krabbi and Alexander Zolotko's advice I started playing with what FeedZirra is returning. It looks like the line
feed = Feedzirra::Feed.fetch_and_parse(publication_params[:url])
is returning an empty hash.
Now when I run the same line in the rails console and hardcode a url in there it returns the proper hash and I am able to grab the title and other values. So it looks like the issue lies with
publication_params[:url]
Still working on this and open to suggestions:)
* UPDATE PART TWO *
I thought the problem was that there was no url column in the Publication Model. So I made the proper migration. So here is the schema:
create_table "publications", force: true do |t|
t.string "name"
t.datetime "created_at"
t.datetime "updated_at"
t.string "url"
end
Back to the controller I have at the bottom:
private
# Use callbacks to share common setup or constraints between actions.
def set_publication
#publication = Publication.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def publication_params
params.permit(:name, :url)
end
publication_params[:url] is still returning nil. I have also tried the line:
params.require(:publication).permit(:url, :name)
which just gives me 400 Bad request error
As Alexander Zolotko points out Feedzirra::Feed.fetch_and_parse seems to return a hash.
Try for example
params = { :name => feed[:title] }
Assuming frontend is correct and REST resource publications is properly set up in Rails backend.
Controller should for example look like:
def create
# requesting the feed from passed url
feed = Feedzirra::Feed.fetch_and_parse(publication_params[:url])
# mapping feed keys to publication attributes
feed_to_publication_hash = { :name => feed[:title], anything else }
# instantiating new publication
#publication = Publication.new(publication_params.merge(feed_to_publication_hash))
...
end
private
def publication_params
params.require(:publication).permit(:url, everything else you need)
end
I am quite sure that even if that works it is not good practice. Usually you strife for Object Orientation, that is in this case one purpose per action.
But I don't know right now how to refactor fetching the feed and perhaps mapping the hash. Maybe it is a controller concern or something like that.
After reviewing the Feedzirra::Feed.fetch_and_parse the only option I see is that your publication_params[:url] contains something other than String. In that case fetch_and_parse returns a Hash of results (even if only one url was passed). Could you please check whether publication_params[:url].is_a?(String) is true.

Ember PUT request using JSON is giving a weird error

(If the next is too much to read for you, please read the last line). I'm using Ember and Rails backend from a while, but I'm still a bit stick with transactions or what other way is used to send actions to the backend. I have Users and Events. My Users can participate in one or several Events, and for each Event several Users can attend. I'm maintaining this relationship using the Mongoid macro has_and_belongs_to_many in this way:
Event
# ...
has_and_belongs_to_many :attendings, class_name: "User" , inverse_of: :attendances
# ...
User
# ...
has_and_belongs_to_many :attendances, class_name: "Event" , inverse_of: :attendings
# ...
In my template I'm showing a list of all the Events stored in my backend and a button "Attend" if the User is not already attending, and "Not attend" it the User is attending. For attending, I've tried to make the PUT request using transactions, making the next steps in my Ember Event controller:
Event controller (Ember), actionAttending method using transactions:
actionAttending: function() {
var userId = this.get('controllers.app.model.id');
this.transaction = this.get('store').transaction();
this.get('attendings').pushObject(App.User.find(userId));
this.transaction.add(this.get('model'));
this.transaction.commit();
}
When I try this, my Event JSON request is containing all the parameters and relations but attendings attribute. So I decided to try using a jQuery request:
Event controller (Ember), actionAttending method using jQuery ajax:
// ...
var eId = this.get('id');
this.get('attendings').pushObject(App.User.find(userId));
var eAttendings = this.get('attendings');
var url = "/events/" + eId + ".json";
var data = { event: { id: eId, attendings: eAttendings } };
$.ajax({
type: "PUT",
url: url,
data: data,
dataType: "JSON"
});
// ...
Well, this is pretty much working, except for the line where I declare eAttendings, where Ember complains about init function is not called, or somehing similar. After googling a bit I found a "solution", which is convert to array, so the line changed like this:
var eAttendings = this.get('attendings').toArray();
My nice error now is:
TypeError: fullName is undefined
var nameParts = fullName.split(":"),
This is raised in Firebug before my ajax request is launched. I don't know what's the exact meaning or how to fix it...
Anyway, I wanted to try if my ajax request is working, so I tried it in curl:
curl --request PUT localhost:3000/events/521b97ef5ef9095ba211bf70 --data "id=521b97ef5ef9095ba211bf70&attendings=521b7eda99027121d1533015"
The answer is:
{"errors":{"attendings":["is invalid"]}}
And the bakend is returning a 422 Unproccesable entity answer... I've got no validations on my model on this field, so I don't know what's going on here either... My update action on Rails Event controller is like this:
def update
e = Event.find(params[:id])
u = User.find(params[:attendings])
if params[:attendings]
e.attendings << u
e.save
respond_with e, api_template: :general_event, status: :no_content
end end
Last detail: I'm using Ember 1.0.0.rc6, jQuery 1.10.2 and gems acts_as_api and active_model_serializers in Rails.
So, to summarize, I want to get working just my PUT request in Ember to add Users attending to an Event through the has_many_and_belongs_to macro (using transactions, jQuery, or something else)

Resources