Date range in ruby/rails - ruby-on-rails

I want to get date range between from and till in Rails seed.
When I try to generate date range ((Date.today - 10)..Date.today) exception occurred.
Exception message: bad value for range
But in the Rails Console everything all right.
I think ActiveSupport are reasonable for that (my debugger told me that).
Ralls 3.1.3
What's going on?

You can understand what's going on by splitting the two edges and check their class like so:
Date.today.class # => Date
(Date.today - 10).class # => Date
((Date.today - 10)..Date.today).each {|d| puts d.class} # => 10 Date works for me
The error you're experiencing is something like this:
('a'..10) # => ArgumentError: bad value for range
Can you post the classes of your 2 edges of the range?
(Date.today - 10).class => ?
Date.today.class => ?
Have you overwritten any class in your rails environment? Does it work in irb?
PS: As you're in rails you can use 10.days.ago but you'll need to use to_date as it's a ActiveSupport::TimeWithZone

begin
((Date.today - 10)..Date.today).each { |date| puts date }
rescue
$! # => #<NameError: uninitialized constant Date>
end
require 'date'
((Date.today - 10)..Date.today).each { |date| puts date }
# >> 2012-04-06
# >> 2012-04-07
# >> 2012-04-08
# >> 2012-04-09
# >> 2012-04-10
# >> 2012-04-11
# >> 2012-04-12
# >> 2012-04-13
# >> 2012-04-14
# >> 2012-04-15
# >> 2012-04-16

Related

Rails render json with right timezone

I'm using rails app as iOS backend. There is small problem - how to render timestamps with user's timezone in json? I'm using default UTC timezone. How to converted it for example to +2? Maybe there is way how to override as_json method to convert timestamps before rendering?
Thanks for help!
You can convert the time to user's timezone before rendering json, then the timezone will be included.
1.9.3p125 :006 > t = Time.now.utc
=> 2012-10-20 13:49:12 UTC
1.9.3p125 :007 > {time: t}.to_json
=> "{\"time\":\"2012-10-20T13:49:12Z\"}"
1.9.3p125 :008 > t = Time.now.in_time_zone("Beijing")
=> Sat, 20 Oct 2012 21:49:19 CST +08:00
1.9.3p125 :009 > {time: t}.to_json
=> "{\"time\":\"2012-10-20T21:49:19+08:00\"}"
UPDATE: To convert the time into user's timezone for serialization, you can override this method read_attribute_for_serialization. See lib/active_model/serialization.rb of ActiveModel:
# Hook method defining how an attribute value should be retrieved for
# serialization. By default this is assumed to be an instance named after
# the attribute. Override this method in subclasses should you need to
# retrieve the value for a given attribute differently:
#
# class MyClass
# include ActiveModel::Validations
#
# def initialize(data = {})
# #data = data
# end
#
# def read_attribute_for_serialization(key)
# #data[key]
# end
# end
#
alias :read_attribute_for_serialization :send

How to test for a time span parameter?

I'm struggling with the following issue:
$ rails c
Loading development environment (Rails 3.2.1)
irb(main):001:0> 2.class
=> Fixnum
irb(main):002:0> Fixnum === 2
=> true
irb(main):003:0> 2.hours.class
=> Fixnum
irb(main):004:0> Fixnum === 2.hours
=> false
irb(main):005:0>
I'd like to test whether some specified parameter is a symbol or a time span. The way I thought would be the natural way in Ruby/Rails is:
case param
when :today then
# do this...
when Fixnum then
# do that...
else
raise ArgumentError ...
end
As far as I can tell from ActiveSupport's source code === is not overridden for Fixnum. So, what am I doing wrong?
You can see from the console, that 2.hours can't be Fixnum, because it's #inspect method is overridden. It just pretends to be Fixnum, but it is ActiveSupport::Duration (see the source)
> 2.hours
=> 7200 seconds
> 2.hours.to_i
=> 7200
Better check if it responds to some methods. If it responds to to_i, it is not a symbol.
> :today.respond_to?(:to_i)
=> false
> 2.hours.respond_to?(:to_i)
=> true

Map string to another string in Ruby Rails

Hey guys,
i have 5 model attributes, for example, 'str' and 'dex'. A user has strength, dexterity attribute.
When i call user.increase_attr('dex') i want to do it through 'dex' and not having to pass 'dexterity' string all the way.
Of course, i can just check if ability == 'dex' and convert it to 'dexterity' when i will need to do user.dexterity += 1 and then save it.
But what is a good ruby way to do that ?
Look at Ruby's Abbrev module that's part of the standard library. This should give you some ideas.
require 'abbrev'
require 'pp'
class User
def increase_attr(s)
"increasing using '#{s}'"
end
end
abbreviations = Hash[*Abbrev::abbrev(%w[dexterity strength speed height weight]).flatten]
user = User.new
user.increase_attr(abbreviations['dex']) # => "increasing using 'dexterity'"
user.increase_attr(abbreviations['s']) # => "increasing using ''"
user.increase_attr(abbreviations['st']) # => "increasing using 'strength'"
user.increase_attr(abbreviations['sp']) # => "increasing using 'speed'"
If an ambiguous value is passed in, (the "s"), nothing will match. If a unique value is found in the hash, the returned value is the full string, making it easy to map short strings to the full string.
Because having varying lengths of the trigger strings would be confusing to the user you could strip all elements of the hash that have keys shorter than the shortest unambiguous key. In other words, remove anything shorter than two characters because of the collision of "speed" ("sp") and "strength" ("st"), meaning "h", "d" and "w" need to go. It's a "be kind to the poor human users" thing.
Here's what is created when Abbrev::abbrev does its magic and it's coerced into a Hash.
pp abbreviations
# >> {"dexterit"=>"dexterity",
# >> "dexteri"=>"dexterity",
# >> "dexter"=>"dexterity",
# >> "dexte"=>"dexterity",
# >> "dext"=>"dexterity",
# >> "dex"=>"dexterity",
# >> "de"=>"dexterity",
# >> "d"=>"dexterity",
# >> "strengt"=>"strength",
# >> "streng"=>"strength",
# >> "stren"=>"strength",
# >> "stre"=>"strength",
# >> "str"=>"strength",
# >> "st"=>"strength",
# >> "spee"=>"speed",
# >> "spe"=>"speed",
# >> "sp"=>"speed",
# >> "heigh"=>"height",
# >> "heig"=>"height",
# >> "hei"=>"height",
# >> "he"=>"height",
# >> "h"=>"height",
# >> "weigh"=>"weight",
# >> "weig"=>"weight",
# >> "wei"=>"weight",
# >> "we"=>"weight",
# >> "w"=>"weight",
# >> "dexterity"=>"dexterity",
# >> "strength"=>"strength",
# >> "speed"=>"speed",
# >> "height"=>"height",
# >> "weight"=>"weight"}
def increase_attr(attr)
attr_map = {'dex' => :dexterity, 'str' => :strength}
increment!(attr_map[attr]) if attr_map.include?(attr)
end
Basically create a Hash that has the key of 'dex', 'str' etc and points to the expanded version of that word(in symbol format).

I am not able to subtract two dates from each other using Ruby...why?

this is the code I have. It is a method for a contact.rb (Contact is a model):
def event_delay event
# find instance of contact_event
puts "+++++++++++++++inside event_delay method"
event_class = event.class.name
event_id = event_class.foreign_key.to_sym
#puts event_id
contact_event_class = "Contact#{event_class}".constantize
#puts contact_event_class
contact_event = contact_event_class.first(:conditions =>
{:contact_id => self.id,
event_id => event.id})
#puts "inspect contact_event"
#puts contact_event.inspect
if !contact_event.nil?
puts "---- #{contact_event.title} not nill"
puts contact_event.id
date_sent = contact_event.date_sent
target_send_date = self.date_entered + event.days
puts "date_sent:"
puts date_sent.to_date
puts "target send date:"
puts target_send_date
return date_sent - target_send_date
end
end
The goal is to return the difference in days between the time a date_sent to the time target_send_date. But I get the following error:
undefined method `-#' for Sun, 08 Aug 2010:Date
Maybe both values are not dates? The - method is defined so that's why I'm thinking that you might not have two dates. Does this work in irb for you?
irb(main):001:0> require "date"
=> true
irb(main):002:0> Date.new(2010, 10, 20) - Date.new(2010, 9, 20)
=> Rational(30, 1)
irb(main):003:0>
After migrating to Rails 3.2, I got the same strange error when running specs, but was able to isolate it:
3.days.ago - Date.today
=> NoMethodError: undefined method `-#' for Sun, 29 Apr 2012:Date
'3.days.ago' produces an ActiveSupport::TimeWithZone object, which (apparently) does not mix with the Date object that Date.today gives you.

Is it possible to simulate the behaviour of sprintf("%g") using the Rails NumberHelper methods?

sprintf("%g", [float]) allows me to format a floating point number without specifying precision, such that 10.00 is rendered as 10 and 10.01 is rendered as 10.01, and so on. This is neat.
In my application I'm rendering numbers using the Rails NumberHelper methods so that I can take advantage of the localization features, but I can't figure out how to achieve the above functionality through these helpers since they expect an explicit :precision option.
Is there a simple way around this?
Why not just use Ruby's Kernel::sprintf with NumberHelper? Recommended usage with this syntax: str % arg where str is the format string (%g in your case):
>> "%g" % 10.01
=> "10.01"
>> "%g" % 10
=> "10"
Then you can use the NumberHelper to print just the currency symbol:
>> foo = ActionView::Base.new
>> foo.number_to_currency(0, :format => "%u") + "%g"%10.0
=> "$10"
and define your own convenience method:
def pretty_currency(val)
number_to_currency(0, :format => "%u") + "%g"%val
end
pretty_currency(10.0) # "$10"
pretty_currency(10.01) # "$10.01"
I have solved this by adding another method to the NumberHelper module as follows:
module ActionView
module Helpers #:nodoc:
module NumberHelper
# Formats a +number+ such that the the level of precision is determined using the logic of sprintf("%g%"), that
# is: "Convert a floating point number using exponential form if the exponent is less than -4 or greater than or
# equal to the precision, or in d.dddd form otherwise."
# You can customize the format in the +options+ hash.
#
# ==== Options
# * <tt>:separator</tt> - Sets the separator between the units (defaults to ".").
# * <tt>:delimiter</tt> - Sets the thousands delimiter (defaults to "").
#
# ==== Examples
# number_with_auto_precision(111.2345) # => "111.2345"
# number_with_auto_precision(111) # => "111"
# number_with_auto_precision(1111.2345, :separator => ',', :delimiter => '.') # "1,111.2345"
# number_with_auto_precision(1111, :separator => ',', :delimiter => '.') # "1,111"
def number_with_auto_precision(number, *args)
options = args.extract_options!
options.symbolize_keys!
defaults = I18n.translate(:'number.format', :locale => options[:locale], :raise => true) rescue {}
separator ||= (options[:separator] || defaults[:separator])
delimiter ||= (options[:delimiter] || defaults[:delimiter])
begin
number_with_delimiter("%g" % number,
:separator => separator,
:delimiter => delimiter)
rescue
number
end
end
end
end
end
It is the specific call to number_with_delimiter with the %g option which renders the number as described in the code comments above.
This works great for me, but I'd welcome thoughts on this solution.

Resources