I have a parameter in one of my models called "facebook_url". How can I be sure the user input contains "facebook.com" and isn't some random website?
Right now I have:
unless self.facebook_url[/\Afacebook.com\/\//]
self.facebook_url = "" unless self.facebook_url == ""
end
But this isn't really working how I want it.
The regular expression would be:
\A((http|https):\/\/)?(www.)?facebook.com\/
Will match a string that contains a valid facebook url:
facebook.com/
www.facebook.com/
https://facebook.com/
https://www.facebook.com/
http://facebook.com/
http://www.facebook.com/
Now, to validate your rails model use rails validations on your model file like this:
validates :facebook_url, format: { with: /\A((http|https):\/\/)?(www.)?facebook.com\//,
message: "use a valid facebook url" }
Using your example code, would be:
if self.facebook_url.match(/\A((http|https):\/\/)?(www.)?facebook.com\//)
# valid facebook url
else
# invalid facebook url
end
You can test the regular expression on www.rubular.com
Related
Hi i had created a small ruby project which consists of JSON file. I stored the JSON data into hash keys. AND worte a method to access the data which is present in hash key using user input. But when i try to send use the user input i am getting this error
how_many_ingredients': undefined methodkeys' for nil:NilClass (NoMethodError)
I found this link with same question and tried that solution but still i'm getting the same error
Accessing Hash Keys with user inputted variables, NoMethodError
File one where all the methods are written
require 'json'
class Methods
attr_accessor :name, :text
def initilize(name)
#name = name
#text = text
end
def how_many_ingredients(text)
puts 'text'
file = File.read('a.json')
hash = JSON.parse(file)
#puts hash['recipes']['pizza'].keys
puts hash['recipes'][text].keys
end
end
File 2 where how_Many_ingredients method is accessed, I can see that the variable is passed to that method
require './1'
class Hello < Methods
person = Methods.new
person.test
puts "enter recipie"
person.name
str = gets
person.how_many_ingredients str
end
Note that when you use gets, the input can contain newline and carriage return characters. You'll need to use gets.chomp to filter these. This is likely the cause of the issue in your program.
Compare the following two:
> puts gets.size
"Hello!"
# 7
> puts gets.chomp.size
"Hello!"
# 6
Note that you'll still need to extend your program to account for user inputted keys that are not in your hash.
Your code assumes that there will always be a hash stored at hash['recipes'][text] - you need to cater to the cases where it isn't.
A simple way to do this is to work your way down through the hash with && symbols - if any step is nil (or false), the line will return nil (or false) rather than exploding. eg
puts hash['recipes'] && hash['recipes'][text].is_a?(Hash) && hash['recipes'][text].keys
Note i'm testing that hash['recipes'][text] is a hash (rather than just a string for example) before calling .keys on it.
I'm working on a small project where the user should type the phone numbers, so I would like to validate that information using the "ClientSideValidations" gem.
validates_format_of :telcasa, :celular, :tel_flia, :tel_trab, :tel_ref_2, :tel_ref_1,
length: { in: 10 },
:with => /\A(\d{10}|\(?\d{3}\)?[-. ]\d{3}[-.]\d{4})\z/,
:message => "Formato invalido"
But, for the region where this project is going to be used I have to validate the three first numbers of the phone that correspond to the area code ("809"/"829"/"849"). How can I validate that the user correctly typed the phone number with one of the three area codes?
Change /\A(\d{10}|\(?\d{3}\)?[-. ]\d{3}[-.]\d{4})\z/ to:
/\A(\(?(809|829|849)\)?[-. ]\d{3}[-.]\d{4})\z/
I took the liberty of dropping the part where you are matching any ten digit number - not sure why it was there or how it should be used in your context.
You can write some custom validation
validate do
valid_phone_codes = [ "007", "042", ...]
valid_phone_codes.each do |valid_code|
# Also handle optional parenthesis
return true if self.phone_number.starts_with?(valid_code, "(#{valid_code})")
end
errors.add(:phone_numbers, "Must start with a valid country code (one of #{valid_phone_codes.join(', ')}")
false
end
Or if you prefer, you can declare this code in a function def valid_country_codes, and then add a line
validate :valid_country_codes
I'm playing around with Netflix's Workflowable gem. Right now I'm working on making a custom action where the user can choose choices.
I end up pulling {"id":1,"value":"High"} out with #options[:priority][:value]
What I want to do is get the id value of 1. Any idea how to pull that out? I tried #options[:priority][:value][:id] but that seems to through an error.
Here's what the action looks like/how I'm logging the value:
class Workflowable::Actions::UpdateStatusAction < Workflowable::Actions::Action
include ERB::Util
include Rails.application.routes.url_helpers
NAME="Update Status Action"
OPTIONS = {
:priority => {
:description=>"Enter priority to set result to",
:required=>true,
:type=>:choice,
:choices=>[{id: 1, value: "High"} ]
}
}
def run
Rails.logger.debug #options[:priority][:value]
end
end
Here's the error:
Error (3a7b2168-6f24-4837-9221-376b98e6e887): TypeError in ResultsController#flag
no implicit conversion of Symbol into Integer
Here's what #options[:priority] looks like:
{"description"=>"Enter priority to set result to", "required"=>true, "type"=>:choice, "choices"=>[{"id"=>1, "value"=>"High"}], "value"=>"{\"id\":1,\"value\":\"High\"}", "user_specified"=>true}
#options[:priority]["value"] looks to be a strong containing json, not a hash. This is why you get an error when using [:id] (this method doesn't accept symbols) and why ["id"] returns the string "id".
You'll need to parse it first, for example with JSON.parse, at which point you'll have a hash which you should be able to access as normal. By default the keys will be strings so you'll need
JSON.parse(value)["id"]
I'm assuming the error is something like TypeError: no implicit conversion of Symbol into Integer
It looks like #options[:priority] is a hash with keys :id and :value. So you would want to use #options[:priority][:id] (lose the :value that returns the string).
In my Rails API / Angular app, I want to be able to search Rails tables using field values. I currently have this code below working, which allows searching the users table by email id, and it returns the users record as JSON.
api/controllers/users_controller.rb
def query # by email
queried_user = User.where(email: params[:email]).first
if !queried_user.nil?
render json: queried_user, root: false
else
render json: {error: 'Does not exist'}, status: :not_found
end
end
config/routes.rb
get 'api/users/:id/query' => 'api/users#query'
Example url
http://0.0.0.0:8080/api/users/1/query?email=testuser1#example.com
Example returned JSON
{"id":14,"title":"Dr.","first_name":"John","last_name":"Smith","email":"testuser1#example.com","job_title":"Head Bioligist","organisation":"NIH","phone_office":null,"city":null,"country":null,"approved":true,"admin":false,"template":false}
This is all working fine at present, but there are two issues I cannot resolve.
I would like the url to not contain an :id I find when I leave the id out of the url, Rails treats the query parameter as the id. I can made it work by hard-coding a fake id, but it doesn't seem like the right answer to me.
I would like to pass an abitary param hash to the query method. It should map the columns based on the hash contents.
if params = {email: 'testuser1#example.com'} then it should work as now, but other desired options might be:
{job_title: 'Manager'}
{city: 'LA', last_name: 'Smith'}
I expect I will change this code, but don't know how to pass arbitrary elements to the where.
queried_user = User.where(email: params[:email])
The where method can accept a hash, therefore you can pass the param hash containing the condition for the query. Just note only equality and range conditions can be used when passing a hash to the where method. Just be sure that in terms of security of your application you are covered. example:
queried_user = User.where(params[:user])
To get rid of the :id in your routes file define a new route similar to this:
match 'api/users/query', to: 'users#query', as 'user_search'
and then use the 'user_search_path' for sending the search to the query action of the users controller.
I've got myself a little stuck trying to get a quick bit of HTTP Digest authentication up and running, pretty much like suggested within the guide:
Ruby on Rails Guides: Action Controller Overview > HTTP Digest Authentication
class ApplicationController < ActionController::Base
protect_from_forgery
USERS = { "sam" => "ruby" }
before_filter :authenticate
private
def authenticate
authenticate_or_request_with_http_digest do |username|
USERS[username]
end
end
end
I get prompted for a username and password although when inputting the above the authentication seems to fail and I get prompted again. So I started to dig into the code that validates the request here:
GitHub: http_authentication.rb > validate_digest_response
def validate_digest_response(request, realm, &password_procedure)
secret_key = secret_token(request)
credentials = decode_credentials_header(request)
valid_nonce = validate_nonce(secret_key, request, credentials[:nonce])
if valid_nonce && realm == credentials[:realm] && opaque(secret_key) == credentials[:opaque]
password = password_procedure.call(credentials[:username])
return false unless password
method = request.env['rack.methodoverride.original_method'] || request.env['REQUEST_METHOD']
uri = credentials[:uri][0,1] == '/' ? request.fullpath : request.url
[true, false].any? do |password_is_ha1|
expected = expected_response(method, uri, credentials, password, password_is_ha1)
expected == credentials[:response]
end
end
end
I can't see how it is handling the password as plain text. How is password_is_ha1 being set? I'm also a little confused on how any? block is working which might not be helping :-/
Just as quick note: I know that I shouldn't really be storing passwords in plain text, and in source code like this. I'm just trying to build up an understanding and will refactor this later.
A massive thanks for all your help in advance :-D
The any? method acts like collect, except it returns true the first time its block returns true. Here, it acts like a loop over the array [true, false]:
The block is first run with password_is_ha1 set to true. If the block returns true, any? immediately returns true, and since this is the last statement of validate_digest_response, the method as a whole returns true.
Otherwise, the block is run again with password_is_ha1 set to false. If the block returns true, any? immediately returns true, and since this is the last statement of validate_digest_response, the method as a whole returns true.
If neither of those runs returned true, any? returns false. Since this is the last statement of validate_digest_response, the method as a whole returns false.
Thus, the effect of that line is to first assume it's a hashed password and check if it's valid, then assume it's a plaintext password and check if it's valid. Another, more verbose, way to write it would have been:
expected = expected_response(method, uri, credentials, password, true)
return true if expected == credentials[:response]
expected = expected_response(method, uri, credentials, password, false)
return true if expected == credentials[:response]
return false