Checking if a record exists in Sinatra/DataMapper - ruby

I currently generate a user's profile page using their serial ID, like so:
get '/users/:id' do
#user = User.get(params[:id])
end
This works great, until a number is entered that doesn't exist in the database.
I'm aware I can change User.get to User.get! to return an ObjectNotFoundError error if the record isn't found, but I'm not sure how I can use this to my aid.
I used to use .exists? when using RoR.
Any suggestions?
Thanks in advance!
Edit: I'm going to leave the question unanswered, as I haven't actually found a solution to what I asked in the title; however, I did manage to solve my own problem by checking to see if the :id entered is higher than the amount of users that exist in the database, like so:
if params[:id].to_i > User.count
"This user does not exist."
else
#users_id = User.get(params[:id])
erb(:'users/id')
end

You already have the correct code:
#user = User.get(params[:id])
If no user exists with the given id, then #get will return nil. Then you can just do a conditional:
#user = User.get params[:id]
if #user
# user exists
else
# no user exists
end
This is a very common pattern in Ruby which takes advantage of the "truthiness" of anything other than false or nil. i.e. you can say if 0 or if [] and the condition will evaluate to true
You can recreate a .exists? method:
class User
def self.exists?(id_or_conditions)
if id_or_conditions.is_a? Integer
!! User.get id_or_conditions
else
!! User.first id_or_conditions
end
end
end
#get is similar to #find in rails, except it doesn't raise an error if the record is not found. #first is similar to #find_by in rails.

Related

How do I access submitted params in a rails mailer?

I had a previous question that helped me loop through all users where a certain question is met.
However, I'm realizing I can't hard code that condition. I need to somehow get that data from the submitted form, which doesn't seem to be possible in the mailer.
In other words, I'm trying to loop through all users where the user's state is equal to the home_state of the candidate being entered. Basically when the candidate is created, I want to get the home_state of that candidate, and then loop through all users, and for each user that has same state as that candidate, I want to send them the email via this mailer.
Here's my candidate_mailer.rb file
class CandidateMailer < ApplicationMailer
default from: 'wesleycreations#gmail.com'
def self.send_request(row)
#candidate = Candidate.new(candidate_params) # if I can access this here, how to I create the
# following array?
emails = []
User.where(state: #candidate.home_state).each do |u|
emails << u.email # To insert the user email into the array
end
emails.each do |email|
new_request(email,row).deliver_now
end
end
def new_request(email, row)
#candidate = row
mail(to: email, subject: 'New request')
end
end
But the
#candidate = Candidate.new(candidate_params)
obviously doesn't work because the params aren't available in the mailer.
Here in the candidates_controller.rb I have this
def create
#candidate = Candidate.new(candidate_params) #of course here I can access params
if #candidate.save
row = #candidate
CandidateMailer.send_request(row)
else
render('new')
end
end
SO the question is, how do I access params in rails mailer? And if I can't, then how do I refactor my code so that the lines that check if the user meets certain condition is done in the controller?
I was able to figure this out by doing this. after I saved the candidate, I saved the candidate to a global variable. and THEN I send the mailer.
def create
#candidate = Candidate.new(candidate_params)
if #candidate.save
row = #candidate
$candidate = #candidate
end
CandidateMailer.send_request(row)
else
end
end
This way the mailer had access to the new candidate that been created, and I was able to check my condition in there.
So in my mailer, when I use $candidate.home_state, it returned the correct state, mail went out, and made me very happy :)
emails = []
User.where(state: $candidate.home_state).each do |u|
emails << u.email # To insert the user email into the array
end

How to check if value exists in params.permit?

I can't seem to figure out how to accomplish what I am trying to do here on my create method.
What I have right now works if there are no values, the item is deleted. However, if 1 or more param values exist, it passes and is saved. Not what I needed. I need an all or nothing scenario. I want to save only if all the permitted keys have their value. params.permit(:name, :description, :copyright)
Before an entry is saved using organizations.save!, I need to make sure none of the params that are permitted are nil or empty.
I search all over and can't seem to narrow down on an answer to my exact issue.
Here is my code:
class OrganizationsController < ApplicationController
def index
query_params = params.permit(:id, :name,)
if query_params.blank?
organizations = Organization.all
else
organizations = Organization.where(query_params)
end
render json: organizations, root: "organizations"
end
def create
organizations = Organization.new(organization_params)
if organization_params.present?
organizations.delete
else
organizations.save!
render json: organizations
end
end
private
def organization_params
params.permit(:name, :description, :copyright)
end
end
You should add validations to your model.
From your question i understand that you want to save details only if you get values in all the field, if not you don't want to save, right?. If yes, then adding validations to your model will give you what you wanted.
Add the following to your organization model
validates_presence_of :name
validates_presence_of :description
validates_presence_of :copyright
by doing so, the user won't be allowed to save the details unless and until all three fields have some value in it.
There is no need to use delete as the incomplete information will not be saved.
for more and advanced info click here
To check none of the values of organization_params hash is empty, you can do something like this:
organization_params.values.all? { |x| !x.empty? }
or, this:
organization_params.all? { |k,v| !v.empty? }
You can also check if any param value is empty:
organization_params.any? { |k,v| v.empty? }
So, your create method can be re-written as:
def create
organizations = Organization.new(organization_params)
if organization_params.any? { |k,v| v.empty? }
# at least one param is empty, so delete the record
organizations.delete
else
# all the params values are present, so save the record
organizations.save!
render json: organizations
end
end

Store calculation in database using variables submitted in a form

I'm submitting a simple form with variables that are named in the database.
I am trying to:
Store the submitted variables in the database (which works fine)
Run a calculation, then store that value into the database
No matter what I try I either get an error or 'nil' (upon #kcmil.inspect) as my result for #kcmil. I'm assuming in my current code i'm not passing the variables to the model, but it doesn't work even when it's in the controller.
I'm at a loss here. My variables that are submitted in the form store just fine as expected. I just want to be able to use submitted variables from a form (that are also database items that get stored upon submission) and before saving to the database (or after, should it matter?) run a calculation and store the result in a database item (that is not previously called in or saved from the form). Does that make sense? Any help or hints are GREATLY APPRECIATED!!
Here are my current calculators_controller create and edit actions:
def create
#calculator = Calculator.new(calc_params)
if #calculator.save
flash[:notice] = "Calculation created successfully."
redirect_to(:action => 'index')
else
render('new')
end
end
def update
#calculator = Calculator.find(params[:id])
if #calculator.update_attributes(calc_params)
flash[:notice] = "Calculation updated successfully."
redirect_to(:action => 'index', :id => #calculator.id)
else
render('edit')
end
end
private
def calc_params
params.require(:calculator).permit(:subsection, :amps, :volts, :distance, :vdrop, :phase, :kcmil, :section_id)
end
Here's my model
class Calculator < ActiveRecord::Base
before_create :kcmil_calc
def kcmil_calc
if #phase == 1
self.kcmil = ((13 * #distance.to_i * #amps.to_i ) / #vdrop.to_i).round(2)
else
self.kcmil = ((12.9 * #distance.to_i * #amps.to_i ) / #vdrop.to_i).round(2)
end
end
end
I HAVE IT! I HAVE IT!
before_update :defaults
def defaults
if self.phase == 1
self.kcmil = ((12.9 * distance * amps) / vdrop).round(2)
else
self.kcmil = ((13 * distance * amps) / vdrop).round(2)
end
end
solved it! I had to call self.phase instead of #phase and change before_create to before_update to get it to work . No change in the controller required. Dang - one simple #! I also removed the to_i because it's not needed since my views prevent me from submitting anything other than integers.

Why won't Sequel write my database table?

controller/makenew.rb
class MakeController < Controller
map '/makenew'
#require 'model/debate'
def debate
if request.post? #this line is potentially dangerous!
#---> 1/3 fetch postdata
data = request.subset(:question, :type, :category, :assertion)
data['user_id'] = user.id #id = request.params['id']
#---> 2/3 check permissions
if user.points < 40
flash[:error] = 'You don\'t have enough points to make a debate.'
redirect_referrer
else
debate = Debate.new
end
#---> 3/3 modify database
begin
debate.save(data)
flash[:success] = success
flash[:form_data] = debate
redirect 'debates'
rescue => e
Ramaze::Log.error(e)
#flash[:form_errors] = debate.errors
#flash[:error] = data
flash[:error] = e
#flash[:error] = 'Failure whilst saving. Contact technical support!'
redirect 'debates' #redirect_referrer
end
#|
end #closes posting conditional
end #closes makesave
end
The error I get is.
SQLite3::ConstraintException: debates.question may not be NULL
I have checked the postdata for data.question and it is not null.
What is going on?
You need to pass 'data' to #update. Thus:
debate.save(data)
is wrong, you have to do:
debate.update(data)
debate.save
If you don't do this, your debate object has no member assigned and thus its question member is nil, violating your DB constraints.
See the differences between #save and #update here:
Update : http://sequel.rubyforge.org/rdoc/classes/Sequel/Model/InstanceMethods.html#method-i-update
Save : http://sequel.rubyforge.org/rdoc/classes/Sequel/Model/InstanceMethods.html#method-i-save
In a nutshell: #save will save the current model instance to the database, while #update will change a bunch of instance attributes in one operation.
But you have to remember that changing a model instance's attributes DOES NOT write them to the database. You always have to call #save explicitly.
Are you sure that your model accepts mass assignment of primary keys?
Try calling Debate.unrestrict_primary_key
You can check the rules in the Sequel documentation.

Better way to assert that all user.name in an array of user start with a prefix using rspec?

Here is what I have. And that kind of work.
it "should filter by name" do
users = users.search(:name => "s")
users.each {|u|
u.name.should be_starts_with("s")
}
end
However, the error message returned by rspec is really poor...
expected starts_with?("s") to return true, got false
Is there a way to get a more precise message, showing the element that failed, or at least its index?
In a binary test like this, I would create two users, one that starts with an s, the other without. I would then check that only the expected element was returned.
like
set up a user(:name => "Sam") and user(:name => "Fred")
filtered_users.map(&:name).should =~ ["Sam"]
In the case of failure, you will see something like
expected ["Sam"]
got ["Fred", "Sam"]
This is much more explicit about what you are doing
The reason you are only getting expected true but got false is because the starts_with methods returns true or false and not the actual value.
I'm not sure that this is the best way, but you can output it yourself.
users.each {|u|
p u.name if !u.name.starts_with?("s")
u.name.should be_starts_with("s")
}
Here is the way I used few times in cases like this:
describe 'user' do
before :each do
#users = users.search(:name => "s")
end
#users.each do |u|
it "should filter user with name '#{u.name}'" do
u.name.should be_starts_with("s")
end
end
end
You will have failed user name in you example description.
I found here an interesting extension to the matchers from Rspec concerning each :
http://xtargets.com/2011/08/12/rspec-meta-expectations-over-collections
So I sticked that helper into my spec_helper
RSpec::Matchers.define :each do |meta|
match do |actual|
actual.each_with_index do |i, j|
#elem = j
i.should meta
end
end
failure_message_for_should do |actual|
"at[#{#elem}] #{meta.failure_message_for_should}"
end
that allows me to write
users.should each satisfy {|u| u.name.should be_starts_with 's'}
and the error message is then :
at[1] expected #User to satisfy block
which give me the first index of failure.
With some addition to the error message, I'm sure I could output the details of that object that didn't match, and that seem a pretty good solution.
Any thoughts? I'm not a rubyist, just getting started with rails. Would be nice to get more input from
This should provide you with far better failure messages
it "should filter by name" do
users = users.search(:name => "s")
users.each do |u|
u.name.should match /^s/
end
end
I agree with Corey that calling "be_starts_with" is rough. RSpec expectations are intended to be read fluidly as a sentence. They don't all have to use "be".

Resources