validates_acceptance_of still saves the record - ruby-on-rails-3

I am using ruby 1.9.2-p180, rails 3.0.7. I have used validates_acceptance_of since the user has to agree to our terms and conditions. We don't have a column for this, but I understand that "If the database column does not exist, the terms_of_service attribute is entirely virtual. " from http://ar.rubyonrails.org/classes/ActiveRecord/Validations/ClassMethods.html#M000082
Anyway, I double checked this by smoke testing the app manually and I see from the logs that the record is still inserted into the db, which is weird because upon submitting the form, I am redirected back to the form with the error: "Must agree to terms and conditions"(which made me think it worked before)
Am I doing something wrong here?
_form.haml:
%label.checkbox-label{:for => "operator_terms_and_conditions"}
= f.check_box :terms_and_conditions
I agree to
= link_to "Terms and Conditions", operator_terms_path, :target => "_blank"
operators_controller:
def create
user_params = params[:operator][:user]
user_params.merge!(:login => user_params[:email])
#password = params[:operator][:user][:password]
Operator.transaction do # don't save User if operator is invalid
#operator = Operator.create(params[:operator])
end
respond_to do |format|
unless #operator.new_record?
UserMailer.operator_confirmation_email(#operator, #password).deliver
UserMailer.operator_registration_admin_notification_email(#operator).deliver
UserSession.create(#operator.user)
format.html {redirect_to new_operator_aircraft_path}
else
format.html { render :action => "new" }
end
end
end
and in the model:
validates_acceptance_of :terms_and_conditions

Found the answer. The problem was not with validates_acceptance_of but rather with how I was saving the data. When an operator was created, a user was also created that was tied to it and it was this user that was being inserted into the db.
This happens because although the operator was being rolled back(because it wasn't valid) the user was still created(because it was not in a transaction).
I solved this by using nested_transactions:
operator model:
...
User.transaction(:requires_new => true) do
create_user
raise ActiveRecord::Rollback unless self.valid?
end
...

Related

Process form data before creating table entry

Backstory: I'm building a site that takes in a Soundcloud URL as part of a post. Currently, I store the link they provide and, when a user loads their feed view, I retrieve the associated image / title / favorite count etc. via my post_helper. I have quickly come to realize that this is not scalable and is hurting load times.
So, what I think I should do (feel free to tell me that there is a better way), is to retrieve the SC/YT metadata on form submit and store it along with the other post data (id, user, content etc.) in the posts' table entry. How would I go about calling the helper methods to retrieve such on form submit and include the metadata in the submitted params?
post_helper.rb excerpt:
def soundcloud_info(soundcloud_url, type)
begin
resolve = scClient.get('/resolve', :url => soundcloud_url)
track_info = scClient.get("/tracks/#{resolve.id}")
rescue Soundcloud::ResponseError => e
%Q{ Error: #{e.message}, Status Code: #{e.response.code} }
end
if type == "title"
%Q{#{track_info['title']}}
elsif type == "image"
%Q{#{track_info['artwork_url']}}
elsif type == "favCount"
%Q{Favorite count: #{track_info['favoritings_count']}}
end
end
post_controler.rb excerpt:
def create
#post = current_user.posts.build(params[:post])
if #post.save
flash[:success] = "Your post was successful!"
redirect_to root_url
else
#feed_items = current_user.feed.paginate(page: params[:page])
render 'static_pages/home'
end
end
So apparently it's pretty straight forward... all I need to do is modify the parameters in the controller before I call #post = current_user.posts.build(params[:post]). My issue was that I was trying to do so in the helper.
I haven't quite adapted the whole thing to get all my required fields, but here's an example of how I have adapted the create method to pull the api URL out if someone submits SoundCloud's embed iframe.
micropost_controller.rb excerpt:
def create
#url = params[:post][:link_html]
if #url[/^.*src="(https|http):\/\/w.soundcloud.com\/player\/\?url=(.*)">/]
params[:post][:link_html] = CGI::unescape($2)
end
#post = current_user.posts.build(params[:post])
if #post.save
flash[:success] = "Your post was successful!"
redirect_to root_url
else
#feed_items = current_user.feed.paginate(page: params[:page])
render 'static_pages/home'
end
end

Populating Rails Fields Based on Calculation

In my Rails 3.2 app, I want to populate some fields based on calculations where the field values users enter are the variables. However, with my current code, the calculation seems to only work based on the values already in the database - it doesn't calculate correctly on the initial save, but it will calculate correctly if I go back in the record and save it a second time.
I have these four fields in my model (Trade):
entry_price
exit_price
percent_result
dollar_result
The user creates a trade with an entry price, and then later edits the trade with the exit_price. When the exit_price is entered, the app should calculate percent_result and dollar_result. However, right now, these result fields are not populating correctly on the first update - it seems to be because it doesn't read the exit_price from the field (when a user enters it in the form), only once it is saved in the DB.
What is going wrong in my controller?
my controller:
def update
#trade = Trade.find(params[:id])
exit_price = params[:trade][:exit_price]
if !exit_price.blank?
#trade.percent_result = ((exit_price.to_f - #trade.entry_price)/#trade.entry_price) * 100
#trade.dollar_result = exit_price.to_f - #trade.entry_price
end
params[:trade][:exit_date] = Date.strptime(params[:trade][:exit_date], '%m/%d/%Y') unless params[:trade][:exit_date].blank?
params[:trade][:entry_date] = Date.strptime(params[:trade][:entry_date], '%m/%d/%Y') unless params[:trade][:entry_date].blank?
respond_to do |format|
if #trade.update_attributes(params[:trade])
format.html { redirect_to #trade, :flash => {:share =>"Your trade was successfully updated. Don't forget to share it with your friends, so you can profit together!"} }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #trade.errors, status: :unprocessable_entity }
end
end
end
the view
<%= simple_form_for(#trade, :html=>{:class=> "form-horizontal well"}) do |f| %>
<%= f.text_field :entry_price, :class=>"input-small" %>
<%= f.text_field :exit_price, :class=>"input-small" %>
<%= submit_tag "Edit Trade" %>
<% end %>
This would probably be better accomplished with a before_save filter in your model.
Add
before_save :calculate_results
to the top of your model and then define
def calculate_results
unless self.exit_price.blank? || self.entry_price.blank?
self.percent_result = ((self.exit_price - self.entry_price)/self.entry_price) * 100
self.dollar_result = self.exit_price - self.entry_price
end
end
in your model as well. Taking this approach ensures that your results will always be consistent with your values for entry and exit price. Enforcing this in the controller violates the Rails principle of "thick model and thin controller" and may also lead to data consistency issues.
An even more consistent way of doing this would be to define dollar_result and percent_result as methods in your model. As your model is now, you have dollar_result stored in the database even though it is a derived value. As a general rule, you should only have one representation of each piece of data whereas here you have two. A helper method might look something like
def dollar_result
self.exit_price - self.entry_price unless self.exit_price.blank? || self.entry_price.blank?
end
You would define a similar method for percent_result. Using this method, you can guarantee that all of your data is consistent because it only has one, canonical representation in the system.

Destroy method failing in controller test

I'm experiencing a bizarre issue testing a destroy method. I'm using FactoryGirl and Rspec.
Here's a look at the method in question. As you can see, it doesn't actually destroy the dealer, just set it and it's dependent object's active attributes to false:
dealers_controller.rb
def destroy
#dealer = Dealer.find(params[:id])
#dealer.active = false
#dealer.save!
#dealer.leads.each { |lead|
lead.active = false
lead.save!
}
#dealer.users.each { |user|
user.active = false
user.save!
}
redirect_to dealers_path
end
When I run this method in the application it does exactly what it should do. Now, on to the test.
dealers_controller_spec.rb
describe "#destroy" do
context "when deleting a valid record" do
let(:dealer) { FactoryGirl.create(:dealer_with_stuff) }
before do
#user = FactoryGirl.build(:admin_user)
login_user
delete :destroy, :id => dealer.id
end
it { should assign_to(:dealer).with(dealer) }
it { should redirect_to(dealers_path) }
it { should set_the_flash }
it "is no longer active" do
dealer.active.should be_false
end
it "has no active users" do
dealer.users.each do |user|
user.active.should be_false
end
end
it "has no active leads" do
dealer.leads.each do |lead|
lead.active.should be_false
end
end
end
end
The first 3 tests pass, but the last 3 all fail (weirdly, the user.active.should be_false test only fails if I put a sleep(10) after delete :destroy up above, but let's not get into that issue now). So when I check the test log, it goes through the entire destroy process, but then does a ROLLBACK, so for some reason it doesn't save any of the records; but it doesn't give me any more information than that.
Does anyone have any thoughts on this? I've tried everything I can possibly think of.
What if you reload the dealer? The dealer in your tests is different from the #dealer object in the controller (ActiveRecord doesn't do identity maps).
before do
#user = FactoryGirl.build(:admin_user)
login_user
delete :destroy, :id => dealer.id
dealer.reload # << add this
end

How to tell if something has been read in Rails for notification

I'd like to implement a simple notification system on my site where an unseen/unread item is displayed to the user. Similar to the one used across Stack Exchange for the user's inbox where unread comments on questions, etc are displayed.
I came across this question that provides an overview of how I'd do this. What I'm confused about is how to figure out if something has been read. I could add a read_at column but how do I actually fill it? If anyone could help me with some basic guidance I'd appreciate it!
UPDATE #1: What if I add a conditional to my Item#show action where I check the user_id (ID of the user creating the item) against current_user.id. Something like the below:
unless #item.user_id == current_user.id
#item.read_at = Time.now
end
UPDATE #2: Using the code below, I'm attempting to update the message's read_at if its recipient_id matches the current_user ID. However it's not working.
def show
#message = Message.find(params[:id])
if #message.recipient_id == current_user.id
#message.read_at == Time.now
#message.save
end
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => #message }
end
end
FINAL UPDATE: Thanks to #prasvin, here's my solution. I added a read_at column to the object. The object also has an existing recipient_id column. So in my Controller's show action, I put the following:
def show
#message = Message.find(params[:id])
if #message.recipient_id == current_user.id
#message.update_attributes(:read_at => Time.now)
end
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => #message }
end
end
Then in my helper.rb file:
def new_messages
Message.where(:recipient_id => current_user.id, :read_at => nil).count
end
And in my layout:
<% if new_messages > 0 %><span class="notification"><%= new_messages %></span><% end %>
How about filling in read_at column in show action, i.e. we have the object in the show action,and then update its read_at attribute before redering the page

Ensuring that at least one related record still exists

Is there a better way to ensure that I don't delete the last record of a relation? I feel like this should be done through a validation, but could not make that stop the destroy action.
FYI - #organization is present because nested routes
class LocationsController < ApplicationController
....
....
def destroy
#organization = Organization.find(params[:organization_id])
#location = #organization.locations.find(params[:id])
count = Location.find_all_by_organization_id(#location.organization_id).count
if count > 1
#location.destroy
flash[:notice] = "Successfully destroyed location."
redirect_to #organization
else
flash[:notice] = "Could not destroy the only location."
redirect_to #organization
end
end
end
You might also consider the before_destroy callback (though I don't think your version is all that bad):
http://edgeguides.rubyonrails.org/active_record_validations_callbacks.html#destroying-an-object