(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)
Related
I'm trying to subscribe users to Mailchimp with Gibbon 2.2.4 with a generic subscribe method I've been using, and then shortly after I want to add in some extra fields to track the results of a quiz they took.
I want to store this data on Mailchimp because I'd like to manage the emails I send off directly from Mailchimp's dashboard.
The service I created to handle my subscriptions:
class MailchimpService
def subscribe(list_id,email,first_name)
GIBBON.lists(list_id).members.create({
body: {
email_address: email,
status: 'subscribed',
merge_fields: {
FNAME: first_name,
},
double_optin: false,
update_existing: true
}
})
end
def subscribe_to_quiz(first_name, email, user_id, quiz_id)
list_id = ENV['QUIZ_MAILCHIMP_LIST_ID']
if subscribe(list_id,email,first_name)
attempt = QuizAttempt.where("user_id = ? AND quiz_id = ?", user_id, quiz_id).last
correct = attempt.correct_answer_count
total = attempt.questions_answered
successful = attempt.successful?
send_quiz_results(list_id, email, correct, total, successful)
end
end
def send_quiz_results(list_id, email, correct, total, successful)
GIBBON.lists(list_id).members(email).upsert(
body: {
email_address: email,
status: 'subscribed',
merge_fields: {
correct_answers: correct,
total_answers: total,
successful: successful
},
update_existing: true
})
end
end
In subscribe_to_quiz, I'm subscribing the user to my quiz_list in Mailchimp. The values of the fields I'm updating here are irrelevant, but I think they're quite explanatory. When I try to run my upsert statement in send_quiz_results, I get the following error:
the server responded with status 400
#title="Member Exists",
#detail="foo#bar.baz is already a list member. Use PUT to insert or update list members.",
#body={"type"=>"http://developer.mailchimp.com/documentation/mailchimp/guides/error-glossary/", "title"=>"Member Exists", "status"=>400, "detail"=>"foo#bar.baz is already a list member. Use PUT to insert or update list members.", "instance"=>""},
#raw_body="{\"type\":\"http://developer.mailchimp.com/documentation/mailchimp/guides/error-glossary/\",\"title\":\"Member Exists\",\"status\":400,\"detail\":\"foo#bar.baz is already a list member. Use PUT to insert or update list members.\",\"instance\":\"\"}",
#status_code=400
I have no clue why it won't let me do this... It seems like it's referencing a create statement, but the extracted source for the error references my upsert statement.
I know I'm using the corresponding PUT verb for Gibbon, since the following is taken straight from the documentation:
Of course, body is only supported on create, update, and upsert calls. Those map to HTTP POST, PATCH, and PUT verbs respectively.
I have no clue why this isn't working... I've tried taking out the other fields and just simply throwing in the ones I'm updating. I've also tried running it straight from the terminal to make sure nothing's overlapping.
The MailChimp API docs show that when updating a member you must provide the member's subscriber_hash, which the MD5 hash of the lowercase version of the members email address.
Use Digest::MD5.hexdigest to hash the email address with MD5:
GIBBON.lists(list_id).members(Digest::MD5.hexdigest(email.downcase)).upsert
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
I am testing my Tire / ElasticSearch queries and am having a problem with a custom method I'm including in to_indexed_json. For some reason, it doesn't look like it's getting indexed properly - or at least I cannot filter with it.
In my development environment, my filters and facets work fine and I am get the expected results. However in my tests, I continuously see zero results.. I cannot figure out where I'm going wrong.
I have the following:
def to_indexed_json
to_json methods: [:user_tags, :location_users]
end
For which my user_tags method looks as follows:
def user_tags
tags.map(&:content) if tags.present?
end
Tags is a polymorphic relationship with my user model:
has_many :tags, :as => :tagable
My search block looks like this:
def self.online_sales(params)
s = Tire.search('users') { query { string '*' }}
filter = []
filter << { :range => { :created_at => { :from => params[:start], :to => params[:end] } } }
filter << { :terms => { :user_tags => ['online'] }}
s.facet('online_sales') do
date :created_at, interval: 'day'
facet_filter :and, filter
end
end
end
I have checked the user_tags are included using User.last.to_indexed_json:
{"id":2,"username":"testusername", ... "user_tags":["online"] }
In my development environment, if I run the following query, I get a per day list of online sales for my users:
#sales = User.online_sales(start_date: Date.today - 100.days).results.facets["online_sales"]
"_type"=>"date_histogram", "entries"=>[{"time"=>1350950400000, "count"=>1, "min"=>6.0, "max"=>6.0, "total"=>6.0, "total_count"=>1, "mean"=>6.0}, {"time"=>1361836800000, "count"=>7, "min"=>3.0, "max"=>9.0, "total"=>39.0, "total_count"=>7, "mean"=>#<BigDecimal:7fabc07348f8,'0.5571428571 428571E1',27(27)>}....
In my unit tests, I get zero results unless I remove the facet filter..
{"online_sales"=>{"_type"=>"date_histogram", "entries"=>[]}}
My test looks like this:
it "should test the online sales facets", focus: true do
User.index.delete
User.create_elasticsearch_index
user = User.create(username: 'testusername', value: 'pass', location_id: #location.id)
user.tags.create content: 'online'
user.tags.first.content.should eq 'online'
user.index.refresh
ws = User.online_sales(start: (Date.today - 10.days), :end => Date.today)
puts ws.results.facets["online_sales"]
end
Is there something I'm missing, doing wrong or have just misunderstood to get this to pass? Thanks in advance.
-- EDIT --
It appears to be something to do with the tags relationship. I have another method, ** location_users ** which is a has_many through relationship. This is updated on index using:
def location_users
location.users.map(&:id)
end
I can see an array of location_users in the results when searching. Doesn't make sense to me why the other polymorphic relationship wouldn't work..
-- EDIT 2 --
I have fixed this by putting this in my test:
User.index.import User.all
sleep 1
Which is silly. And, I don't really understand why this works. Why?!
Elastic search by default updates it's indexes once per second.
This is a performance thing because committing your changes to Lucene (which ES uses under the hood) can be quite an expensive operation.
If you need it to update immediately include refresh=true in the URL when inserting documents. You normally don't want this since committing every time when inserting lots of documents is expensive, but unit testing is one of those cases where you do want to use it.
From the documentation:
refresh
To refresh the index immediately after the operation occurs, so that the document appears in search results immediately, the refresh parameter can be set to true. Setting this option to true should ONLY be done after careful thought and verification that it does not lead to poor performance, both from an indexing and a search standpoint. Note, getting a document using the get API is completely realtime.
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);
I am trying to create a Webhook through the API.
When the Customer installs the app (Controller):
def init_webhooks
topics = ["products/create", "products/update", "products/delete"]
topics.each do |topic|
webhook = ShopifyAPI::Webhook.create(format: "json", topic: topic, address: "http://#{#current_host}/webhooks/#{topic}")
raise "Webhook invalid: (#{topic}) #{webhook.errors}" unless webhook.valid?
end
end
Here is the error from the log:
RuntimeError (Webhook invalid: (products/create) #<ActiveResource::Errors:0x00000003bd7358>):
EDIT:
I have even tried just creating one webhook without the block code like so:
webhook = ShopifyAPI::Webhook.create topic: "products/create", address: "http://myapp.com/webhooks/products/create", format: "json"
But I get the same thing.
From my Routes file:
match 'webhooks/products/create' => 'webhook#product_new'
match 'webhooks/products/update' => 'webhook#product_updated'
match 'webhooks/products/delete' => 'webhook#product_deleted'
I know that the authorization and shop is in fact installing correctly because if I Comment out the 'Raise' error line, I then proceed to the index page which displays some test orders and test products that I created within the Shopify Admin.
I'm not sure where to go from here. Thanks
b
The params you use (topic,format,address) look good to me, but shouldn't it be ShopifyAPI::Webhook.new instead of create?
Did you now there is a shopify console where you can easily test your ruby code?