ActionMailer delivering email even if checkbox is not checked - ruby-on-rails

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

Related

Rails little loop error

I have a job model which has_many offers, User could accept or reject these offers, but how can I do that when User accept an offer, the more offers cannot be created? I try to do this in that way, it works but it duplicates input boxes. So what I'm doing wrong or how can I do it in another way?
#show.html.haml
- if #job.offers.blank?
= render 'offers/form'
- #job.offers.each do |f|
- if #job.offers.find(f).status == false || #job.offers.find(f).status == nil
= render 'offers/form'
- else
cannot do that
#offers/_form.html.haml
= simple_form_for([#job, #job.offers.build]) do |f|
= f.input :pirce_offer
= f.button :submit
You need to equate yourself with scoping in programming; whenever you create a "block" in Ruby, it's essentially like invoking a function, with its own local scope:
- #job.offers.each do |f|
- if f.status == false || f.status == nil
= render 'offers/form'
- else
cannot do that
As an aside to this, I don't get the purpose of calling a form that builds a new offer?
You're looping through an offer of a job, using the status conditional to determine whether the user should create an offer or not.... yet, surely if the offer exists, the user has already posted an offer?
Also, you're using a completely fresh set of data for the form:
= simple_form_for([#job, #job.offers.build]) do |f|
= f.input :pirce_offer
= f.button :submit
Shouldn't you be using something like the following:
= simple_form_for([#job, f]) do |f|
= f.input :pirce_offer
= f.button :submit
Okay I get it -- you have a sort of "auction" system where offers are accepted/declined. This would give you the ability to make a new offer if the previous was rejected........... it's still sketchy as hell.
how can I do that when User accept an offer, the more offers cannot be created
You'll need to use some validation in your view and model.
Something like this:
#app/models/offer.rb
class Offer < ActiveRecord::Base
validates :user_id, uniqueness: { scope: :job, message: "Only one offer per job" }
end
I could create a much more complete answer if you describe how you want the jobs/offers to work.

HAML - how to create form validation and display error messages?

I am able to render a form in HAML but I am not sure how to validate it.
Here is what I have so far:
#disclosure-form-container
= form_for([:mobile,#disclosure], :html => {:id => "disclosure-form", :remote => true}) do |f|
%p
=f.label :display_as, (t :select_disclosure_type)
=f.select :display_as, options_from_collection_for_select(Disclosure.basics, :display_as, :name, f.object.display_as)
%p
=f.label :notes, (t :notes)
=f.text_area :notes, :class => "#{validation_rules(:free_disclosure_notes)}", :rows => 5 , :id => "disclosure_notes_entry"
= buttonset({:submit_label => (t "buttons.influencers.disclosures.create"),:submit_css_class => "call-to-action",:cancel_url => "#",:direction => :left_to_right})
This renders a form with two fields and a button to click submit. But what if people just enter submit without doing much? Where do I put the validation code and how do I validate this form?
Thanks!
A user enters data that should be saved in your model and then stored in your DB. So it's naturally to implement validation on model-level. Rails allows you to create validation for models easy. Here you can read about it. In a few words: adding few lines to your model prevent it to be saved with inconsistent data.
But you can in addition to model-level validation use client-side validation: i.e., data will be checked before sending to server. This way user don't have to submit form to find out that he forgot to fill some required field. But of course this can't be any guaranty as it's easy to bypass. Here more about client-side validation.
So, as you see, Haml can't help you with validations. It's a great tool, but not for this purpose.
In your Disclosure model, you need:
class Disclosure < ActiveRecord::Base
validates_presence_of :display_as, :notes
...
end
Then you can include error messages at the top of your form with something like:
- if #disclosure.errors.any?
%ul.errors
- #disclosure.errors.full_messages.each do |msg|
%li= msg
Or if you use Simple Form you get automatic validation messages inline, and your form would just look like this:
= simple_form_for #disclosure do |f|
= f.input :display_as
= f.input :notes
= f.submit
And you're done.
Note, as Jdoe pointed out, HAML has nothing to do with detecting validations, it only displays what the server tells it. The server is what determines whether there are validation errors.
If you want to try and come up with the equivalent of something like this client side you could give your form an id and do something like this (in CoffeeScript):
jQuery ->
$('form#disclosures').submit (event) ->
unless $('input#display_as').val().length > 0 && $('input#notes').val().length > 0
event.preventDefault()
$(this).append('<div class="error">You must select 'display' and enter notes.</div>')

Accept Rails model attribute only if it was previously blank

I have a Rails model (persisted with Mongoid) that can be collaboratively edited by any registered user. However, I want to allow editing any particular attribute only if it was previously blank or nil.
For example, say someone created an object, and set its title attribute to "Test Product". Then another user comes along and wants to add a value for price, which until now has been nil.
What's the best way to do this, while locking an attribute that has previously been entered?
Look into the ActiveRecord::Dirty module for some nice utility methods you can use to do something like this:
NON_UPDATABLE_ATTRIBUTES = [:name, :title, :price]
before_validation :check_for_previously_set_attributes
private
def check_for_previously_set_attributes
NON_UPDATABLE_ATTRIBUTES.each do |att|
att = att.to_s
# changes[att] will be an array of [prev_value, new_value] if the attribute has been changed
errors.add(att, "cannot be updated because it has previously been set") if changes[att] && changes[att].first.present?
end
end
The easiest way, i think, is by checking for it in the form itself.
Just say add :disabled => true to the input field if the person cannot edit it.
<% if #my_object.name %>
<%= f.text_field :name, :disabled => true %>
<% else %>
<%= f.text_field :name, :disabled => true %>
<% end %>
(i think there is a prettier way to write this code)
But by using this the user has a visual feed back that he can't do something, it is always better to not allor something than to give an error message

Value stored in boolean column as false is evaluated as true by Rails?

I'm having a lot of trouble getting a simple true/false to evaluate properly in Rails. I have this in a helper:
def should_we_ask_to_subscribe?
if #user.email.blank?
true
elsif #user.subscriber == false
true
else
false
end
end
I test for blank because the user object is created by omniauth and sometimes, as with Twitter, I don't get an email. After the authorization is created, the user is directed to update their profile and add an email. If there's no email, I make sure to present the option to subscribe because they can't be subscribed. This is the view:
<% if should_we_ask_to_subscribe? %>
<div class="field">
<%= f.check_box :subscriber %>
<%= f.label :subscriber, "It's okay to send me the occassional email. I know I can unsubscribe at any time." %>
</div>
<% else %>
<div class="field">
You're subscribed to our email list.<br />
<%= f.check_box :unsubscribe %>
<%= f.label :unsubscribe, "Please unsubscribe me." %>
</div>
<% end %>
However, even when the subscribe boolean is set to f -- say a user has entered an email, but not subscribed (or unsubscribed, which is the only way the boolean would be f) should_we_ask_to_subscribe? still evaluates to false. I've tried various permuations but am just not getting it.
Update
Okay, so it's clear that the database is storing the boolean value as false, but rails is interpreting it as true and I can't really get why. Following advice below with inspect, the log shows #user.subscriber = true even though in the console and in the database I get subscriber: false for that particular user.
Second Update
So I've written some unit tests and they all pass. In my logs, I notice the code is doing the following:
(0.4ms) UPDATE "users" SET "subscriber" = 'f', "updated_at" = '2011-07-05 13:40:58.178186' WHERE "users"."id" = 47
and this is the code, in a method in my model, that does that:
self.update_attribute(:subscriber, false)
When I look at the database, both through an SQLite view and the console, I see an f and u.subscriber => false, respectively. But, when I use logger.info "#user.subscriber = #{#user.subscriber.inspect}" to see how the view helper is interpreting it, I get: #user.subscriber = true in the log. So, I'm assuming the problem is with how I set false since it seems rails is interpreting any value in the subscriber column as true (except in the console, where it seems to be saying its false?).
This is a good candidate for a unit test that will exercise your model and determine if the correct behavior is implemented. For instance:
#user = User.create(...)
assert #user.email.blank?
assert !#user.subscriber?
assert_equal true, #user.should_we_ask_to_subscribe?
#user.email = 'test#example.email'
assert_equal false, #user.should_we_ask_to_subscribe?
#user.unsubscribe = true
assert_equal true, #user.should_we_ask_to_subscribe?
As a note, if you're dealing with variables that are interpreted as either true or false, you don't need to be so formal about comparing values to false specifically. A simple refactoring would look like this:
def should_we_ask_to_subscribe?
# Show subscription option if no email has been provided.
return true if #user.email.blank?
# Otherwise ask to subscribe if not a subscriber
!#user.subscriber?
end
Diagnose whether the value of subscriber is not nil (which is a false equivalent) or whether it is not any other value.
Put this line in the should_we_ask_to_subscribe function:
logger.info "#user.subscriber = #{#user.subscriber.inspect}"
Is your subscriber attribute declared as boolean? The checkbox would send a value of "0" (a string), which will evaluate to true in ruby, but usually is magically translated to 'false', but only for boolean attributes.
I am currious to see in what way the subscriber field information is saved in your database.
Could you raise #user.subscriber within your should_we_ask_to_subscribe? function please?
Add this at the top of your function :
raise #user.subscriber.inspect

Accessing a parameter within a plugin

I'm trying to modify the vestal_versions plugin to accept a parameter I set upon saving. This parameter will act as a new flag to determine when to create a revision upon updating. Currently it will always run upon update when a new revision is needed. Here is the affected area of unmodified plugin code:
after_update :create_version, :if => :needs_version?
def create_version
versions.create(:changes => changes.slice(*versioned_columns), :number => (last_version + 1))
reset_version
end
The parameter I am sending in the view upon submit is "forcerevision=n". How would I pull in this parameter and what conditional would I use to allow this to only run when "forcerevision=y"? It seems it would be cleanest to modify the after_update filter?
Here is the log of the data being passed on update.
Processing NotesController#update (for 521.0.0.1 at 2009-12-05 13:25:45) [PUT]
Parameters: {"authenticity_token"=>"#########################+k=", "id"=>"4", "forcerevision"=>"n", "note"=>{"notebook_id"=>"", "public"=>"n", "body"=>"A versioned note", "title"=>"Version Note Test", "flag"=>"important", "star"=>"false", "tag_list"=>""}}
vestal_versions on Github
The cleanest way to do this would be to add an attr_accessor when a model is declared to be versioned.
Then override needs_version? to check that attribute.
Anywhere in LaserLemon::VestalVersions::ClassMethods#versioned add this line:
attr_accessor :force_reversion
Then rewrite LaserLemon::VestalVersions::InstanceMethods#needs_version? to check this attribute:
N.B. due to the way checkboxes are handled "0" is considered false for checkboxes, and boolean fields, but is a true value in ruby. So we can't just check if force_reversion does not evaluate to false.
def needs_version?
!(versioned_columns & changed).empty? ||
![nil, "0", 0, false].include?(force_reversion)
end
And you're all set. Just pass any value for force_reversion as if it were a column.
After the above changes with the following model:
class User
#user.update_attributes(:force_reversion => true, :unversioned_column => new_value)
Or in a form:
<%= form_for #user do |f| %>
<%= f.label :force_reversion, "Force New Version" %>
<%= f.check_box :force_reversion %>
... More fields ...
<% end %>

Resources