Gibbon::MailChimpError (the server responded with status 400 #title="Invalid Resource" - ruby-on-rails

I am trying to use gibbon gem and following their github docs. Here is the code I have
class ChimpController < ApplicationController
def create
send_to_mail_chimp ( params[:email])
end
def send_to_mail_chimp(email)
puts "send email is #{email}"
gibbon = Gibbon::Request.new(api_key: "bla")
gibbon.timeout = 10
gibbon.lists('e61cf2454d').members.create(body: {email_address: email, status: "subscribed"})
end
end
<%= simple_form_for :email, url: newsletter_path, :method => :post do |f| %>
<%= f.input :email, input_html: {class: 'form-control', placeholder: 'enter email'} %>
<% end %>
The exact error message is
Gibbon::MailChimpError (the server responded with status 400 #title="Invalid Resource", #detail="The resource submitted could not be validated. For field-specific details, see the 'errors' array.", #body={"type"=>"http://developer.mailchimp.com/documentation/mailchimp/guides/error-glossary/", "title"=>"Invalid Resource", "status"=>400, "detail"=>"The resource submitted could not be validated. For field-specific details, see the 'errors' array.", "instance"=>"", "errors"=>[{"field"=>"email_address", "message"=>"Schema describes string, object found instead"}]}, #raw_body="{\"type\":\"http://developer.mailchimp.com/documentation/mailchimp/guides/error-glossary/\",\"title\":\"Invalid Resource\",\"status\":400,\"detail\":\"The resource submitted could not be validated. For field-specific details, see the 'errors' array.\",\"instance\":\"\",\"errors\":[{\"field\":\"email_address\",\"message\":\"Schema describes string, object found instead\"}]}", #status_code=400):
app/controllers/chimp_controller.rb:10:in `send_to_mail_chimp'
app/controllers/chimp_controller.rb:3:in `create'

The error message you're getting back is telling you exactly what the problem is:
{
"field": "email_address",
"message": "Schema describes string, object found instead"
}
You're passing the email in as a javascript object (ruby hash) instead of a string. All you need to pass is the raw email address.

I think you need to give the members method a MD5 hash of the lower case email address (via the mailchimp subscriber management). Try
def send_to_mail_chimp(email)
puts "send email is #{email}"
gibbon = Gibbon::Request.new(api_key: "bla")
gibbon.timeout = 10
md5_email = Digest::MD5.hexdigest(email['email'].downcase)
# I prefer 'upsert' to 'create' but should work with either
gibbon.lists('e61cf2454d').members(md5_email).upsert(body: {email_address: email['email'], status: "subscribed"})
end

Related

ActionMailer delivering email even if checkbox is not checked

I have a checkbox (:notify) for Post, and I want to send emails when I create a new post, only if it is checked. However, ActionMailer is delivering the emails even if it is not checked. Here is the code snippet:
if #post.save
unless params[:post][:notify].nil?
PostMailer.notify_new(#post).deliver
end
.........
..............
Form:
= bootstrap_form_for #post, remote: true do |f|
= f.text_area :body
= f.check_box :notify, label: ""
= f.submit "Send", class: "button"
How do I fix it such that emails are delivered only when the notify checkbox is checked?
Thanks!
You should move all this into the Post model...
class Post
attr_accessor_with_default :notify, false
after_create :deliver, :if => Proc.new {|p| p.notify}
def deliver
PostMailer.notify_new(self).deliver
end
end
Then, notify will be treated as a boolean. Don't forget to permit :notify attribute in your controller.
I'd double check that params[:post][:notify] is the correct parameter to be looking for. If those parameters are always showing up, I'd look at your view logic. Also, if you're going to use this method of checking, change you condition to an if. For example:
if params[:post][:notify].present?
PostMailer.notify_new(#post).deliver
end

Customize validation error message depending on the context

I need to have different error messages for the same model depending on the form's context and location.
For the User model which validates presence of first_name:
In the back-office page it is OK to have the validation message "First name can't be blank"
In the registration page the message should be "Please type your first name"
I am looking for a clean and best-practice oriented solution, because I would like not to hack with view helper and such.
Any hint appreciated, thanks
You can use validate method in User model . Something like this
validate do |user|
if user.first_name.blank? && user.id.blank?
# id blank means the user is in registration page as he is new user.
user.errors.add(:base, "Please type your first name")
elsif user.first_name.blank?
user.errors.add(:base, "First name can't be blank")
end
end
May be using hidden_field and attr_accessor, I hope you can achieve what you want,
Form 1:
<%= f.hidden_field :check_form, :value => true %>
Form 2:
<%= f.hidden_field :check_form, :value => false %>
You need to pass check_form value also to the model.
Model:
attr_accessor :check_form
validates_presence_of :first_name, :if => :check_form_is_true?, :message => "First Name can't be blank"
validates_presence_of :first_name, :unless => :check_form_is_true? //here you need to use i18n oriented translation to show the custom error message
private
def check_form_is_true?
check_form == true
end
config/locales/en.yml
en:
activerecord:
attributes:
user:
first_name: ""
errors:
models:
user:
attributes:
first_name:
blank: "Please type your first name"
Hope it helps :)

I can't update my rails form

I am a rails beginner,now I follow the guide and create a test project as below:
admin_controller.rb
def blogEdit
#btnName = "更新"
#submitURL = "blogUpdate"
#blog=Blog.find(params[:id])
end
def blogUpdate
#blog=Blog.find(params[:id])
#blog.update(params[:blog])
redirect_to "/admin/blogList"
end
blogEdit.slim
= render "blogForm"
_blogForm.slim
=form_for :blog,url:{action: #submitURL} do |f|
=f.label :title ,"标题"
=f.text_field :title
=f.label :body ,"内容"
=f.text_area :body,rows:10,style:'width:500px;'
=f.label :author ,"作者"
=f.text_field :author
=f.hidden_field :id
=f.submit #btnName,class:'btn'
but when I update a blog,I get the error:
Couldn't find Blog without an ID
Rails.root: /home/hxh/share/ruby/myblog
Application Trace | Framework Trace | Full Trace
app/controllers/admin_controller.rb:28:in `blogUpdate'
Request
Parameters:
{"utf8"=>"✓",
"authenticity_token"=>"3SBp+xSft8SzA5poczxNSU1zKReO2OpZQqgESn6ZURs=",
"blog"=>{"title"=>"违法的",
"body"=>"的说法都是",
"author"=>"似懂非懂",
"id"=>"2"},
"commit"=>"更新"}
so It shows that I not get the params[:id]
#blog=Blog.find(params[:id])
I had follow the guide,why I got the result?
You're passing =f.hidden_field :id into form. So your id in in params[:blog][:id].
But id have to be passing via #submitURL. Judging by your params #submitUrl aren't contains id of the blog.
You are not following guide convensions or your guide doesn't do it. Because there is common rules for name actions. It's edit and update instead your blogEdit and blogUpdate.
The url that is created while submitting is not resourceful instead the URL should be created like /2/blogupdate but in your case it is /blogupdate.
What you can do here change these lines
def blogEdit
#btnName = "更新"
#submitURL = "/#{params[:id]}/blogUpdate"
#blog=Blog.find(params[:id])
end
In your form, instead of #submitURL, why don't you use blogUpdate_blog_path(#blog)?

Rails not generating validation fail messages

I have a number of standard rails validations within my model:
validates_presence_of :url_string
validates_uniqueness_of :url_string
validates_presence_of :stream_source
validates_presence_of :width
validates_presence_of :height
validates_presence_of :name
validates_uniqueness_of :name
validates_presence_of :customer_name
validates_presence_of :iframe_background_color
If I don't fill out one of these fields within my form then I am taken back to the form as expected but the odd thing is no error messages are displayed. I am using the code below to display the error messages:
<% #camera.errors.full_messages.each do |error| %>
<p><%= error %></p>
<% end %
I also attempted to print out the #camera.errors object and this is what is shown:
#<ActiveModel::Errors:0x12db19bc #base=#<Camera id: 1, stream_source: "test", width: 640, height: 360, active: true, name: "test", url_string: "CAYD19Vp", customer_name: "test", iframe_background_color: "#FFFFFF", online: true, created_at: "2011-08-30 15:54:16", updated_at: "2011-09-06 15:52:48", audio: true, iframe_text_color: "#FF00FF", iframe_link_color: "#FF0000", notes: "Some notes!", offline_image_file_name: "Cake.jpg", offline_image_content_type: "image/jpeg", offline_image_file_size: 196591, offline_image_updated_at: "2011-09-06 12:12:38", pull_stream_url: "test", bitrate: "300-500", show_branding: false>, #messages={}>
#
As you can see the messages hash is empty. I tried setting the validation error message manually by doing the following:
validates_presence_of :name, :message => "No name present"
but it did not populate the messages hash either.
Controller update action is shown below:
def update
#camera = Camera.find(params[:id])
if #camera.update_attributes(params[:camera])
flash[:notice] = "Camera updated"
redirect_to nwcadmin_camera_path
else
redirect_to :action => :edit
end
end
I am using Ruby version ruby 1.9.2p290 and Rails version 3.1.0.
Any assistance would be great!
Thanks
Just a heads up that you'll get a Validation failed (ActiveRecord::RecordInvalid) error with an empty error message (if there are no other errors) when you have before_validation declarations and any of them returns false.
Note that before_validation callbacks must not return false (nil is okay) and this can happen by accident, e.g., if you are assigning false to a boolean attribute in the last line inside that callback method. Explicitly write return true in your callback methods to make this work (or just true at the end if your callback is a block (as noted here)).
UPDATE: This will no longer be an issue starting Rails 5.0, as return false will no longer halt the callback chain (throw :abort will now halt the callback chain).
UPDATE: You might also receive ActiveRecord::RecordNotSaved: Failed to save the record if a callback returns false.
I managed to get to the bottom of my problem. In the controller I was using:
redirect_to :action => :edit
I should have been using:
render :action => :edit
By using redirect_to I was hitting the edit action within the controller which was then getting a new camera object from the database rather than preserving the current camera object from the update action.
Unless you call #camera.save or #camera.valid?, the errors hash will not be populated with the validation errors. Please check your controller code.
You can use flash[:message] or flash[:notice] in controller code to store the error message, which can be used in view to display the errors.Link Have a look in the link,it's clearly explained, how to append the error messages and use them to display.The instance variable doestnot contains any errors as no validation runs in update.
You can use #camera_errors = #camera.save to collect the errors and then
<% #camera_errors.errors.full_messages.each do |error| %>
<p><%= error %></p>
<% end %>
I'm not sure if this is something that you might be interested in or not, but you can use this official Rails gem: dynamic_form
This gem provides you two helper methods: error_messages and error_messages_for
Refer to the following Rails guide for more: http://guides.rubyonrails.org/active_record_validations_callbacks.html#displaying-validation-errors-in-the-view

Syntax question for delayed_jobs and email

I am getting a beautiful error :
failed with NoMethodError: You have a nil object when you didn't expect it!
You might have expected an instance of ActiveRecord::Base.
The error occurred while evaluating nil.[] - 3 failed attempts
My Controller:
CardSignup.all.each do |user|
Delayed::Job.enqueue MassEmail.new(user, params[:subject], params[:editor1])
end
mass_email.rb
class MassEmail < Struct.new(:user, :subject, :message)
def perform
Notifier.deliver_email_blast(user, subject, message)
end
end
_form.html.haml
- form_tag admin_email_blast_path do
Subject
%br
= text_field_tag 'subject'
%br
Body
%br
= text_area_tag 'message', '', :name => 'editor1'
%br
= submit_tag 'Send Email', :class => 'button'
:plain
<script type="text/javascript">
CKEDITOR.replace( 'editor1' );
</script>
The problem is because i'm not passing the varis right.. how do I do this proper?
note: It was working perfectly before i implemented delayed_jobs with params[:subject], and params[:editor1], so I have to retain those somehow.
I tried this without Delayed_Jobs, and the MassEmail.new doesn't even leave a backtrace in my log.
I also tried this in my console, and it froze.
There's got to be something wrong with the mass_email.rb or the call in def perform
This is definitively wrong, you gave two parameters the same name:
class MassEmail < Struct.new(:user, :params, :params)
How does the perform method now which param you're referring to?
You can try something like this
class MassEmail < Struct.new(:user, :subject, :editor1)
def perform
Notifier.deliver_email_blast(user, subject, editor1)
end
end
Kudos to JigFox for getting me to write the params right.
And I'm not entirely sure why this works, but I moved the loop into the MassEmail.new function.
Delayed::Job.enqueue MassEmail.new(params[:subject], params[:editor1])
class MassEmail < Struct.new(:subject, :editor1)
def perform
CardSignup.all.each do |user|
Notifier.deliver_email_blast(user, subject, editor1)
end
end
end

Resources