Rails 5 custom validate - ruby-on-rails

I am learning Rails and I stuck with this custom validation. I tried many different approaches to pass the form field called (:cpf) to validate without success. I also tried using ActiveModel::EachValidator, but I get more confused. The validation is a check digit using modulo 11. So this is what I ended up.
controllers/drivers_controller.rb
def new
#driver = Driver.new
end
def create
#driver = Driver.new(driver_params)
if #driver.save
flash[:success] = "Motorista cadastrado com sucesso!"
redirect_to #driver
else
render 'new'
end
end
models/driver.rb
class Driver < ApplicationRecord
validate :checkcpf
private
def checkcpf
return false if cpf.nil?
nulos = %w{12345678909 11111111111 22222222222 33333333333 44444444444 55555555555 66666666666 77777777777 88888888888 99999999999 00000000000}
valor = cpf.scan /[0-9]/
if valor.length == 11
unless nulos.member?(valor.join)
valor = valor.collect{|x| x.to_i}
soma = 10*valor[0]+9*valor[1]+8*valor[2]+7*valor[3]+6*valor[4]+5*valor[5]+4*valor[6]+3*valor[7]+2*valor[8]
soma = soma - (11 * (soma/11))
resultado1 = (soma == 0 or soma == 1) ? 0 : 11 - soma
if resultado1 == valor[9]
soma = valor[0]*11+valor[1]*10+valor[2]*9+valor[3]*8+valor[4]*7+valor[5]*6+valor[6]*5+valor[7]*4+valor[8]*3+valor[9]*2
soma = soma - (11 * (soma/11))
resultado2 = (soma == 0 or soma == 1) ? 0 : 11 - soma
return true if resultado2 == valor[10] # CPF VALIDO
end
end
end
return false # CPF INVALIDO
errors.add(:cpf, "CPF INVÁLIDO")
end
end
I think I am missing how to pass my cpf field to validate. Either I got a error message or miss the validation.
I will try to use ActiveModel::EachValidator later when I get better understanding from Rails. This is how I usually do, try to get things to work the way I can understand then refactor the Rails way.
I appreciate any help! Thank you!

First of all, no need to return either true or false from this method, just return - you want to either return from the method because the field is valid or add an error. I didn't quite follow the logic of your validation, but regardless of the result you always return false # CPF INVALIDO and the line errors.add(:cpf, "CPF INVÁLIDO") is never reached. Bear in mind - whenever you return from a method, you stop the execution, so you have to be sure that there is at least one execution path, that can reach the line, which adds the actual error.

Related

Running a Ruby script via web

I am following the Rails Tutorial by Michael Hart and I am already on Chapter 7. But I wanna do something different right now, which the tutorial doesn't teach. I wanna run a script file inside my webpage. How I can do that? I saw other posts here saying to use Sinatra, but since I am following this tutorial I don't think it is such a good idea to use it because it can make everything different from the tutorial.
Here is the simple script I wanna run on my webpage:
#Somando idades
def soma_vetor_excluindo(index,vet)
soma = 0
for i in 0..9
if(i!=index)
soma = soma + vet[i].to_i
end
end
return soma
end
def soma_vetor(vet)
soma = 0
for i in 0..9
soma = soma + vet[i].to_i
end
return soma
end
def maior_vetor(vet)
maior = 0
for i in 0..9
if(maior < vet[i])
maior = vet[i]
end
end
return maior
end
idades = (0..9).collect{rand(99)+1}
soma_idades = (0..9).collect{0} soma = 0
print "#{idades} \n"
for i in 0..9
soma_idades[i] = soma_vetor_excluindo(i,idades)
end
print "#{soma_idades} \n"
div = soma_vetor(soma_idades) / 9
resp = div - maior_vetor(soma_idades)
puts "#{resp}"
The simplest way to do it would be to make the method soma_vetor_excluindo, soma_vetor, maior_vetor, etc, controller methods, so when you send data through a form or ajax, the action would trigger, calculate the values and return you a result.
Knowing this, you can have a controller, let's say MathController.rb, and inside it, the soma_vetor_excluindo method:
class MathController < ApplicationController
def soma_vetor_excluindo
end
def soma_vetor
end
def maior_vetor
end
end
To access this, you probably need a route, so on your routes.rb add something like this:
get 'math/soma_vetor_excluindo/:index/:vet', to 'math#soma_vetor_excluindo'
get 'math/soma_vetor/:vet', to 'math#soma_vetor'
get 'math/maior_vetor/:vet', to 'math#maior_vetor'
This means that when your browser hit localhost/math/soma_vetor_excluindo/1/2 or the other urls, it would send a get request to the controller calling the soma_vetor_excluindo method and putting in the parameters, params[:index] and params[:vet], so theoretically the script would run.
The thing is, you can adapt your controller to do something like this with very little work.
I believe the simplest solution is to load a page per script. First you add a path for your script into the routes.rb with something like:
get 'scripts/your_script', to 'scripts#your_script
And in the controller (app/scripts_controller.rb) you should add your code like this:
class ScriptsController < ApplicationController
#Somando idades
def soma_vetor_excluindo(index,vet)
soma = 0
for i in 0..9
if(i!=index)
soma = soma + vet[i].to_i
end
end
return soma
end
def soma_vetor(vet)
soma = 0
for i in 0..9
soma = soma + vet[i].to_i
end
return soma
end
def maior_vetor(vet)
maior = 0
for i in 0..9
if(maior < vet[i])
maior = vet[i]
end
end
return maior
end
def your_script
idades = (0..9).collect{rand(99)+1}
soma_idades = (0..9).collect{0}
soma = 0
answer = "#{idades} \n"
for i in 0..9
soma_idades[i] = soma_vetor_excluindo(i,idades)
end
answer << "#{soma_idades} \n"
div = soma_vetor(soma_idades) / 9
resp = div - maior_vetor(soma_idades)
answer << "#{resp}"
render(text: answer)
end
end
when you access the page scripts/your_script, it should render a plain text presentation of your script result.
Although this is not the most elegant solution, it should solve your problem.

Caching, when nil or false are acceptable results [duplicate]

This question already has answers here:
How can I memoize a method that may return true, false, or nil in Ruby?
(2 answers)
Closed 8 years ago.
In some ruby classes, it is useful to cache the results of an expensive operation using the ||= operator, as in the following snippet:
class CacheableCalculations
def foobar
#foobar ||= lambda { some_expensive_calculation }.call
end
end
The issue arrises when the returned value is either nil or false, as this test shows:
class Example
attr_accessor :counter
def initialize(value)
#counter = 0
#value = value
end
def fancy_calculation
#foo ||= lambda { #counter += 1; #value }.call
end
end
first = Example.new(true)
5.times { first.fancy_calculation }
puts first.counter # <== 1, expected
second = Example.new(false)
5.times { second.fancy_calculation }
puts second.counter # <== 5, not caching
third = Example.new(nil)
5.times { third.fancy_calculation }
puts third.counter # <== 5, not caching
Is there any pros or cons with using the defined? operator instead, as in the following block of code?
class Example
attr_accessor :counter
def initialize(value)
#counter = 0
#value = value
end
def fancy_calculation
(defined? #foo) ? #foo : (#foo = lambda { #counter += 1; #value }.call)
end
end
This is still one 1 line, but is quite repetitive.
Is there a better way of easily returning cached results, regardless of what the value is?
The problem with the way it is written is that the ternary operator ?: has higher precedence than assignment = so it is parsed as
def fancy_calculation
((defined? #foo) ? #foo : #foo) = lambda { #counter += 1; #value }.call # NO
end
which you don't want, because #foo is always assigned to.
Instead, do this
def fancy_calculation
defined?(#foo) ? #foo : (#foo = lambda { #counter += 1; #value }.call)
end
This is probably about as succinct as can be without using a separate package/function specifically for memoization.
What you are trying to achieve is called memoization. There used to be a method doing what you need in Rails but at some point they extracted to a separate memoist gem. Check it out: https://github.com/matthewrudy/memoist
There is an alternative one as well: https://github.com/dkubb/memoizable

How do I make this Ruby method more 'ruby-esque' - i.e. DRY and sleek?

Consider:
def first_login?
if (self.sign_in_count <= 20)
return true
else
return false
end
end
It would be nice if I could just have it be 1 line of code...if possible.
def first_login?
self.sign_in_count <= 20
end
Your comparison already returns boolean value
You don't need self as well because methods are invoked on self implicitly
Exactly one line :)
def first_login?
sign_in_count <= 20
end

Rails server hangs when a User method I created is called. No errors and I don't know how to test it

I call this method (with helper method detailed below as well), defined in User.rb model
def get_random_items
return nil unless groups_as_member
if groups_as_member == 1
assignments = groups_as_member.assignments.limit(5)
random_items = Post.rand_by_post(assignments)
end
random_groups = groups_as_member.sort_by{rand}.slice(0,5)
random_items = Array.new
i=0
return unless random_groups
until i == 10 do
random_groups.each do |group|
assignments = group.assignments.limit(5)
if y = Post.rand_by_post(assignments)
random_items << y
i+=1
if random_items == 5
return random_items
end
else
return random_items
end
end
end
return random_items
end
helper method rand_by_post in Post.rb
def self.rand_by_post(assignments)
find_by_id(assignments.rand.post_id)
end
in the user controller show action:
def show
#public_groups = Group.public
#groups_member = #user.groups_as_member
#groups_as_owner = #user.groups_as_owner
#random_items = #user.get_random_items
end
when I comment out the call in the user show action, the user show works fine on my development and production server. But when I try to user the method the servers will just hang there, not doing anything. I can't find any errors in the server logs or the heroku logs. My test writing skills are pretty limited, and I am having trouble writing one for the entire method.
Can anyone spot a problem?
if your random_groups is empty, your helper method get_random_items will go into an endless loop until i == 10 do ... end. That could be the reason.
You might want to change return unless random_groups to return if random_groups.empty?

Rails - Triggering Flash Warning with method returning true

I'm trying to trigger a warning when a price is entered too low. But for some reason, it always returns true and I see the warning regardless. I'm sure there something wrong in the way I'm doing this as I'm really new to RoR.
In model:
def self.too_low(value)
res = Class.find_by_sql("SELECT price ……. WHERE value = '#{value}'")
res.each do |v|
if #{value} < v.price.round(2)
return true
else
return false
end
end
end
In controller:
#too_low = Class.too_low(params[:amount])
if #too_low == true
flash[:warning] = 'Price is too low.'
end
I would write it somewhat different. You iterate over all items, but you are only interested in the first element. You return from inside the iteration block, but for each element the block will be executed. In ruby 1.9.2 this gives an error.
Also i would propose using a different class-name (Class is used to define a class)
So my suggestion:
Class YourGoodClassName
def self.too_low(amount)
res = YourGoodClassName.find_by_sql(...)
if res.size > 0
res[0].price.round(2) < 1.00
else
true
end
end
end
You can see i test if any result is found, and if it is i just return the value of the test (which is true or false); and return true if no price was found.
In the controller you write something like
flash[:warning] = 'Price is too low' if YourGoodClassName.too_low(params[:amount])

Resources