How could we implement a method (eg. rails_version?) that be able to check the current Rails full version like this:
if rails_version?("6.0.3+") # return true if rails version >= 6.0.3.0
# new way
else
# old way
end
Use Rails.version to get the current version as a string.
However, that alone is not enough to compare it to other versions reliably. To do that, the best tool is Gem::Version.
Rails.version #=> "6.0.3.6"
Gem::Version.new(Rails.version) > Gem::Version.new('6.0.3.6') #=> false
Gem::Version.new(Rails.version) > Gem::Version.new('6.0.3') #=> true
Gem::Version.new(Rails.version) >= Gem::Version.new('6.0.3.6') #=> true
Though these would all work with strings as well, there are some cases that don't
Gem::Version.new(Rails.version) > Gem::Version.new('6.0.10') #=> false (correct)
Rails.version > '6.0.10' #=> true (incorrect)
Here is my solution:
def rails_version?(version)
Rails::VERSION::STRING >= version
end
Related
I want to create a test that returns either true or false for email handling.
For now, if the email address starts with r+ then it's true otherwise it's false. This will help our server ignore a lot of the SPAM we are getting hit with.
Examples:
r+kldslkadslkadslk#site.com .. true
r+123123312#site.com .. true
vigraaaa#site.com .. FALSE
What's the most efficient way to handle this with Rails/ruby/regex?
Thanks
GOAL
Is a one liner in rails/ruby with:
ABORT if XXXXX == 0
This will match:
/^r\+.*#site.com$/
Examples:
>> 'r+kldslkadslkadslk#site.com' =~ /^r\+.*#site.com$/ #=> 0
>> 'vigraaaa#site.com' =~ /^r\+.*#site.com$/ #=> nil
Since everything that isn't nil or false is truthy in Ruby, you can use this regex in a condition. If you really want a boolean you can use the !! idiom:
>> !!('vigraaaa#site.com' =~ /^r\+.*#site.com$/) #=> false
>> !!('r+kldslkadslkadslk#site.com' =~ /^r\+.*#site.com$/) #=> true
If you're in Rails, there's a starts_with? method on strings:
"foo".starts_with?('f') # => true
"foo".starts_with?('g') # => false
Outside of Rails, regexes are a reasonable solution:
"foo" =~ /^f/ # => true
"foo" =~ /^g/ # => false
Because Ruby uses truthiness in if statements, if you do end up using regexes, you can just use the return value to switch:
if "foo" =~ /^f/
puts "Was true!"
else
puts "Was false!"
end
If you're writing a method and want to return a boolean result, you could always use the double bang trick:
def valid_email?
!!("foo" =~ /^f/)
end
Rubular (rubular.com) is a good site for testing Ruby regexes pre-1.9. (1.9's regexes added things like lookahead.)
If you don't want to use an "!!" operator:
!!("foo" =~ /^f/)
you could use a ternary operator (might look more obvious):
"foo" =~ /^f/ ? true : false
You can use the '===' operator as well
/f/ === 'foo' #=> true
/f/ === 'bat' #=> false
Note: The regex part is on the left:
/YOUR_REGEX/ === 'YOUR_STRING'
Regexp#match? was added to Ruby-2.4.0. See Regexp#match documentation.
irb(main):001:0> "ack!".match? /a(b|c)/
=> true
irb(main):002:0> "add".match? /a(b|c)/
=> false
This is for Rails 3, almost always I think a content_for?(:foo) is followed by content_for(:foo) (in haml):
%title= content_for?(:title_for_page) ? "#{content_for(:title_for_page)} - Our great website" : 'Our great website'
So instead of doing 2 lookups, isn't it better to just do 1 lookup and use longer code:
- title_for_page = content_for(:title_for_page) # is "" when not previously set
%title= title_for_page.blank? ? 'Our great website' : "#{title_for_page} - Our great website"
? But if content_for? is implemented as a hash, then maybe it is super quick anyway, comparable to the blank? anyways?
A one-liner to solve the problem:
- title_for_page = (c = content_for(:title_for_page)).blank? ? 'Our great website' : "#{c} - Our great website"
Only way to find out is to test :)
ruby-1.9.2-p136 :001 > h = {:mike => "test"}
=> {:mike=>"test"}
ruby-1.9.2-p136 :004 > Benchmark.ms do
ruby-1.9.2-p136 :005 > h[:mike].present?
ruby-1.9.2-p136 :006?> end
=> 0.029087066650390625
ruby-1.9.2-p136 :007 > Benchmark.ms do
ruby-1.9.2-p136 :008 > h[:mike].blank?
ruby-1.9.2-p136 :009?> end
=> 0.011205673217773438
I am using present?, as per the source of content_for?
Interesting that blank? is faster than present?, isn't it? Time to explore.
Lets look at the source code for present?:
Woah, it turns out present? just calls blank? and negates it.
I have an Image, which contains carrierwave uploads:
Image.find(:first).image.url #=> "/uploads/image/4d90/display_foo.jpg"
In my view, I want to find the absolute url for this. Appending the root_url results in a double /.
root_url + image.url #=> http://localhost:3000//uploads/image/4d90/display_foo.jpg
I cannot use url_for (that I know of), because that either allows passing a path, or a list of options to identify the resource and the :only_path option. Since I do't have a resource that can be identified trough "controller"+"action" I cannot use the :only_path option.
url_for(image.url, :only_path => true) #=> wrong amount of parameters, 2 for 1
What would be the cleanest and best way to create a path into a full url in Rails3?
You can also set CarrierWave's asset_host configĀ setting like this:
# config/initializers/carrierwave.rb
CarrierWave.configure do |config|
config.storage = :file
config.asset_host = ActionController::Base.asset_host
end
This ^ tells CarrierWave to use your app's config.action_controller.asset_host setting, which can be defined in one of your config/envrionments/[environment].rb files. See here for more info.
Or set it explicitly:
config.asset_host = 'http://example.com'
Restart your app, and you're good to go - no helper methods required.
* I'm using Rails 3.2 and CarrierWave 0.7.1
try path method
Image.find(:first).image.path
UPD
request.host + Image.find(:first).image.url
and you can wrap it as a helper to DRY it forever
request.protocol + request.host_with_port + Image.find(:first).image.url
Another simple method to use is URI.parse, in your case would be
require 'uri'
(URI.parse(root_url) + image.url).to_s
and some examples:
1.9.2p320 :001 > require 'uri'
=> true
1.9.2p320 :002 > a = "http://asdf.com/hello"
=> "http://asdf.com/hello"
1.9.2p320 :003 > b = "/world/hello"
=> "/world/hello"
1.9.2p320 :004 > c = "world"
=> "world"
1.9.2p320 :005 > d = "http://asdf.com/ccc/bbb"
=> "http://asdf.com/ccc/bbb"
1.9.2p320 :006 > e = "http://newurl.com"
=> "http://newurl.com"
1.9.2p320 :007 > (URI.parse(a)+b).to_s
=> "http://asdf.com/world/hello"
1.9.2p320 :008 > (URI.parse(a)+c).to_s
=> "http://asdf.com/world"
1.9.2p320 :009 > (URI.parse(a)+d).to_s
=> "http://asdf.com/ccc/bbb"
1.9.2p320 :010 > (URI.parse(a)+e).to_s
=> "http://newurl.com"
Just taking floor's answer and providing the helper:
# Use with the same arguments as image_tag. Returns the same, except including
# a full path in the src URL. Useful for templates that will be rendered into
# emails etc.
def absolute_image_tag(*args)
raw(image_tag(*args).sub /src="(.*?)"/, "src=\"#{request.protocol}#{request.host_with_port}" + '\1"')
end
There's quite a bunch of answers here. However, I didn't like any of them since all of them rely on me to remember to explicitly add the port, protocol etc. I find this to be the most elegant way of doing this:
full_url = URI( root_url )
full_url.path = Image.first.image.url
# Or maybe you want a link to some asset, like I did:
# full_url.path = image_path("whatevar.jpg")
full_url.to_s
And what is the best thing about it is that we can easily change just one thing and no matter what thing that might be you always do it the same way. Say if you wanted to drop the protocol and and use the The Protocol-relative URL, do this before the final conversion to string.
full_url.scheme = nil
Yay, now I have a way of converting my asset image urls to protocol relative urls that I can use on a code snippet that others might want to add on their site and they'll work regardless of the protocol they use on their site (providing that your site supports either protocol).
I used default_url_options, because request is not available in mailer and avoided duplicating hostname in config.action_controller.asset_host if haven't specified it before.
config.asset_host = ActionDispatch::Http::URL.url_for(ActionMailer::Base.default_url_options)
You can't refer to request object in an email, so how about:
def image_url(*args)
raw(image_tag(*args).sub /src="(.*?)"/, "src=\"//#{ActionMailer::Base.default_url_options[:protocol]}#{ActionMailer::Base.default_url_options[:host]}" + '\1"')
end
You can actually easily get this done by
root_url[0..-2] + image.url
I agree it doesn't look too good, but gets the job done.. :)
I found this trick to avoid double slash:
URI.join(root_url, image.url)
I was expecting it to be the union of all changes between 2 versions. (using vestal versions 1.0.2)
ruby-1.8.7-p174 > contact.version
=> 12
ruby-1.8.7-p174 > contact.latest_approved_version
=> 8
ruby-1.8.7-p174 > contact.changes
=> {}
ruby-1.8.7-p174 > contact.versions.last.changes
=> {"first_name"=>["I changed this one baby", "AdminF"]}
ruby-1.8.7-p174 > contact.changes_between(8,12)
=> {}
ruby-1.8.7-p174 > contact.changes_between(9,12)
=> {"deleted"=>[true, false]}
ruby-1.8.7-p174 > contact.changes_between(10,12)
=> {}
ruby-1.8.7-p174 > contact.changes_between(11,12)
=> {"first_name"=>["I changed this one baby", "AdminF"]}
As you can see no changes between 8 and 12, but changes between some of them.
The weird thing is I swear this was working yesterday!
Here is the method vestal is using, im not sure what the problem is:
def changes_between(from, to)
from_number, to_number = versions.number_at(from), versions.number_at(to)
return {} if from_number == to_number
chain = versions.between(from_number, to_number).reject(&:initial?)
return {} if chain.empty?
backward = from_number > to_number
backward ? chain.pop : chain.shift unless from_number == 1 || to_number == 1
chain.inject({}) do |changes, version|
changes.append_changes!(backward ? version.changes.reverse_changes : version.changes)
end
end
It is possible that there is nothing wrong with the example you provided. If for example there are no differences between version 8 and 12. Even though there has been changes in version 9, 10 and 11, the method changes_between will not show any changes if the model's attributes has been reverted to the same values.
I think the easiest way to verify that is to check:
>> contact.revert_to(8)
=> "8"
>> contact.inspect
=> "#<Contact ... >"
>> contact.revert_to(12)
=> "12"
>> contact.inspect
=> "#<Contact ... >"
And then compare the output. I can't think of any other reason for the result you described.
By the way, the call you made to "last_approved_version", is that something you manually added to your model or is it something built-in in vestal_versions? Because I also use the 1.0.2 vestal_version and I can't find any reference to that. So if it really is something built-in then we probably have different forks of vestal_version and that might be the cause...
when I do this
ip = request.env["REMOTE_ADDR"]
I get the client's IP address it it. But what if I want to validate whether the value in the variable is really an IP?
How do I do that?
Please help.
Thanks in advance. And sorry if this question is repeated, I didn't take the effort of finding it...
EDIT
What about IPv6 IP's??
Ruby has already the needed Regex in the standard library.
Checkout resolv.
require "resolv"
"192.168.1.1" =~ Resolv::IPv4::Regex ? true : false #=> true
"192.168.1.500" =~ Resolv::IPv4::Regex ? true : false #=> false
"ff02::1" =~ Resolv::IPv6::Regex ? true : false #=> true
"ff02::1::1" =~ Resolv::IPv6::Regex ? true : false #=> false
If you like it the short way ...
require "resolv"
!!("192.168.1.1" =~ Resolv::IPv4::Regex) #=> true
!!("192.168.1.500" =~ Resolv::IPv4::Regex) #=> false
!!("ff02::1" =~ Resolv::IPv6::Regex) #=> true
!!("ff02::1::1" =~ Resolv::IPv6::Regex) #=> false
Have fun!
Update (2018-10-08):
From the comments below i love the very short version:
!!(ip_string =~ Regexp.union([Resolv::IPv4::Regex, Resolv::IPv6::Regex]))
Very elegant with rails (also an answer from below):
validates :ip,
:format => {
:with => Regexp.union(Resolv::IPv4::Regex, Resolv::IPv6::Regex)
}
Why not let a library validate it for you? You shouldn't introduce complex regular expressions that are impossible to maintain.
% gem install ipaddress
Then, in your application
require "ipaddress"
IPAddress.valid? "192.128.0.12"
#=> true
IPAddress.valid? "192.128.0.260"
#=> false
# Validate IPv6 addresses without additional work.
IPAddress.valid? "ff02::1"
#=> true
IPAddress.valid? "ff02::ff::1"
#=> false
IPAddress.valid_ipv4? "192.128.0.12"
#=> true
IPAddress.valid_ipv6? "192.128.0.12"
#=> false
You can also use Ruby's built-in IPAddr class, but it doesn't lend itself very well for validation.
Of course, if the IP address is supplied to you by the application server or framework, there is no reason to validate at all. Simply use the information that is given to you, and handle any exceptions gracefully.
require 'ipaddr'
!(IPAddr.new(str) rescue nil).nil?
I use it for quick check because it uses built in library. Supports both ipv4 and ipv6. It is not very strict though, it says '999.999.999.999' is valid, for example. See the winning answer if you need more precision.
As most of the answers don't speak about IPV6 validation, I had the similar problem.
I solved it by using the Ruby Regex Library, as #wingfire mentionned it.
But I also used the Regexp Library to use it's union method as explained here
I so have this code for a validation :
validates :ip, :format => {
:with => Regexp.union(Resolv::IPv4::Regex, Resolv::IPv6::Regex)
}
Hope this can help someone !
Use http://www.ruby-doc.org/stdlib-1.9.3/libdoc/ipaddr/rdoc/IPAddr.html it performs validation for you. Just rescue the exception with false and you know that it was invalid.
1.9.3p194 :002 > IPAddr.new('1.2.3.4')
=> #<IPAddr: IPv4:1.2.3.4/255.255.255.255>
1.9.3p194 :003 > IPAddr.new('1.2.3.a')
ArgumentError: invalid address
from /usr/local/rvm/rubies/ruby-1.9.3-p194/lib/ruby/1.9.1/ipaddr.rb:496:in `rescue in initialize'
from /usr/local/rvm/rubies/ruby-1.9.3-p194/lib/ruby/1.9.1/ipaddr.rb:493:in `initialize'
from (irb):3:in `new'
from (irb):3
from /usr/local/rvm/rubies/ruby-1.9.3-p194/bin/irb:16:in `<main>'
require 'ipaddr'
def is_ip?(ip)
!!IPAddr.new(ip) rescue false
end
is_ip?("192.168.0.1")
=> true
is_ip?("www.google.com")
=> false
Or, if you don't mind extending core classes:
require 'ipaddr'
class String
def is_ip?
!!IPAddr.new(self) rescue false
end
end
"192.168.0.1".is_ip?
=> true
"192.168.0.512".is_ip?
=> false
All answers above asume IPv4... you must ask yourself how wise it is to limit you app to IPv4 by adding these kind of checks in this day of the net migrating to IPv6.
If you ask me: Don't validate it at all. Instead just pass the string as-is to the network components that will be using the IP address and let them do the validation. Catch the exceptions they will throw when it is wrong and use that information to tell the user what happened. Don't re-invent the wheel, build upon the work of others.
Try this
Use IPAddr
require 'ipaddr'
true if IPAddr.new(ip) rescue false
This regular expression I use which I found here
/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/
IP address in a string form must contain exactly four numbers, separated by dots. Each number must be in a range between 0 and 255, inclusive.
Validate using regular expression:
\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}
for match a valid IP adress with regexp use
^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$
instead of
^([01]?[0-9][0-9]?|2[0-4][0-9]|25[0-5])(\.([01]?[0-9][0-9]?|2[0-4][0-9]|25[0-5])){3}$
because many regex engine match the first possibility in the OR sequence
you can try your regex engine : 10.48.0.200
test the difference here
for ipv4
def ipv4?(str)
nums = str.split('.')
reg = /^\d$|^[1-9]\d$|^1\d\d$|^2[0-4]\d$|^25[0-5]$/
nums.length == 4 && (nums.count {|n| reg.match?(n)}) == 4
end