RABL: change default date and time format to iso8601 - ruby-on-rails

I'm using rabl 0.11.6 for my API, and I find myself doing the following over and over again for every date or time object:
node :created_at do |article|
article.created_at.iso8601
end
It would save a lot of code duplication, if the format for every date object could be set in one place, so that I just could use the attributes method like this
attributes :created_at
I found an issue on github (https://github.com/ccocchi/rabl-rails/issues/68) where they suggest, overwriting some Oj.default_options = { mode: :compat, time_format: :ruby } in an initializer. But I have no idea how to do a .iso8601.
Then I found this one https://gist.github.com/astevens/b80225d96d8889c946ac. But it looks like a hacky monkey patch solution.
And finally I found this issue https://github.com/nesquena/rabl/issues/336 where they overwrite the as_json method from ActiveSupport::TimeWithZone, also a very monkey-patch solution.
Is there no way to tell RABL what date and time format to use? And if not, what would be the best / least-hacky method to achieve this?

FWIW I fixed this problem in rails 4.2.8, Rabl 0.13.1 and OJ 2.18.5
Add a initializer with the following..
Oj.default_options = {:use_as_json => true}

As crazy as it sounds, this fixes it by changing the JSON serializer:
bundle add oj

Related

How to deal with Money in Rails and Mongoid

I know this question has been asked before, but most answers I've found are related to ActiveRecord or old (most cases, both) and I was wondering whether there's a new take on this.
Is short, my Rails app is an API, so please keep this in mind (can't normally use lots of helpful little view related helpers).
I've been reading about this and found the MoneyRails which seems quite neat. The problem I'm having with it is that when I retrieve the data, it returns an object instead of the an usable value:
class MyModel
include Mongoid::Document
...
field :price_GBP, type: Money
...
end
So to create the document I send a number and it created the document fine. Now when I query the same document it returns an object for price_GBP, which is fine, but my main grip is that it return the value fractional as in my_obj.price_GBP[:fractional] as a string rather than a number.
I'd rather not have my client to have to do the conversion fro string to number than to decimal.
I guess I could create a little helper that would convert the value in such circumstances like so (in my Model):
def my_helper
self.price_GBP = BigDecimal(self.price_GBP) # or something along those lines
end
Then in my controller:
#my_model = Model.find(id)
#my_model.price_GBP = #my_model.price_GBP = #my_model.my_helper
render json: #my_model
With the above in mind, would this be the best approach? If yes, what's the point of using the MoneyRails gem then?
Secondly, if not using the MoneyRails gem, should I use BigDecimal or Float as the field type?
When I tried BigDecimal, the data was saved ok, but when I've retrieve it, I got an string rather than a number. Is this the correct behaviour?
When I tried Float it all worked fine, but I read somewhere that Float is not the most accurate.
What are your thoughts?
Avoid using Float if you're planning on performing any type of arithmetic on the currency values. BigDecimal is good or you can also represent the value in cents and store it as an Integer. This is actually how the Money gem works.
My recommendation would be to continue to use the MoneyRails gem and use the built-in helpers to output the values. You mentioned not being able to use the helpers but I don't see what's preventing that - Rails includes jbuilder which allows you to formulate your JSON structure in a view with access to all "helpful little view related helpers" - for example
# app/views/someresource/show.json.jbuilder
# ...other attributes
json.price_GBP = humanized_money(#my_model.price_GBP)

jBuilder ignore_nil! site wide?

I'm working on a Rails API using jBuilder and want to exclude null from all my JSON objects.
jBuilder has the following method:
def ignore_nil!(value = true)
#ignore_nil = value
end
How and where can I override this method to be true for everything? This would make much more sense and be more maintainable than going through every json view I have and adding it.
JBuilder doesn't provide one place to specify :ignore_nil option. You can look at JbuilderHandler class in jbuilder_template.rb for more details.
If anyone else stumbles upon this:
JBuilder.ignore_nil true
will set the default value of ignore_nil to true everywhere.
You could (assuming you are using rails) just put this in an initializer.

How do I do auto conversion of Rails time to Unix epoch time?

I have a Rails app that makes heavy use of the created_at and updated_at fields.
I've found that something like Post.last.created_at.to_f will give me epoch time but I always need epoch time so I was wondering if there is some way to write an automated post-query filter that will do the conversion every time I called created_at and update_at. Right now, every time I read created_at/updated_at I repeat myself, which is bad form, and has already caused bugs when I forget to do the conversion.
I'm using Rails 3.2.13 and Ruby 1.9.3p392.
Also, I can't just write post.created_at.to_f in my view since I'm using render JSON for my output.
Why don't you just make a method you can patch in to ActiveRecord::Base?
def created_epoch
self.created_at.to_f
end
Then you won't have to remember to convert, you can just use that method instead.
Personally I believe approach suggested by #tadman is a better one.
created_at method can be as follows to do what you want:
def created_at
created_at= attributes["created_at"]
created_at ? created_at.to_f : nil
end
Place it in the models or attach it to ActiveRecord::Base. Carefully test it before putting to production.

How can I use US-style dates in Rails using Ruby 1.9?

I'm in the U.S., and we usually format dates as "month/day/year". I'm trying to make sure that my Rails app, using Ruby 1.9, assumes this format everywhere, and works the way it did under Ruby 1.8.
I know that lots of people have this issue, so I'd like to create a definitive guide here.
Specifically:
'04/01/2011' is April 1, 2011, not Jan 4, 2011.
'4/1/2011' is also April 1, 2011 - the leading zeros should not be necessary.
How can I do this?
Here's what I have so far.
Controlling Date#to_s behavior
I have this line in application.rb:
# Format our dates like "12/25/2011'
Date::DATE_FORMATS[:default] = '%m/%d/%Y'
This ensures that if I do the following:
d = Date.new(2011,4,1)
d.to_s
... I get "04/01/2011", not "2011-04-01".
Controlling String#to_date behavior
ActiveSupport's String#to_date method currently looks like this (source):
def to_date
return nil if self.blank?
::Date.new(*::Date._parse(self, false).values_at(:year, :mon, :mday))
end
(In case you don't follow that, the second line creates a new date, passing in year, month and day, in that order. The way it gets the year, month and day values is by using Date._parse, which parses a string and somehow decides what those values are, then returns a hash. .values_at pulls the values out of that hash in the order Date.new wants them.)
Since I know that I will normally pass in strings like "04/01/2011" or "4/1/2011", I can fix this by monkeypatching it like this:
class String
# Keep a pointer to ActiveSupport's String#to_date
alias_method :old_to_date, :to_date
# Redefine it as follows
def to_date
return nil if self.blank?
begin
# Start by assuming the values are in this order, separated by /
month, day, year = self.split('/').map(&:to_i)
::Date.new(year, month, day)
rescue
# If this fails - like for "April 4, 2011" - fall back to original behavior
begin
old_to_date
rescue NoMethodError => e
# Stupid, unhelpful error from the bowels of Ruby date-parsing code
if e.message == "undefined method `<' for nil:NilClass"
raise InvalidDateError.new("#{self} is not a valid date")
else
raise e
end
end
end
end
end
class InvalidDateError < StandardError; end;
This solution makes my tests pass, but is it crazy? Am I just missing a configuration option somewhere, or is there some other, easier solution?
Are there any other date-parsing cases I'm not covering?
Gem: ruby-american_date
This gem was created since I asked this question. I'm now using it and have been pleased.
https://github.com/jeremyevans/ruby-american_date
Date.strptime is probably what you're looking for in ruby 1.9.
You're probably stuck monkeypatching it onto string.to_date for now, but strptime is the best solution for parsing dates from strings in ruby 1.9.
Also, the formats are symmetric with strftime as far as I know.
you can use rails-i18n gem or just copy the en-US.yml and set your default locale "en-US" in config/application.rb
For parsing US-style dates, you could use:
Date.strptime(date_string, '%m/%d/%Y')
In console:
> Date.strptime('04/01/2011', '%m/%d/%Y')
=> Fri, 01 Apr 2011
> Date.strptime('4/1/2011', '%m/%d/%Y')
=> Fri, 01 Apr 2011
Use REE? :D
Seriously though. If this is a small app you have complete control over or you are standardizing on that date format, monkey patching for a project is totally reasonable. You just need to make sure all your inputs come in with the correct format, be it via API or website.
Instead of using to_s for Date instances, get in the habit of using strftime. It takes a format string that gives you complete control over the date format.
Edit:
strptime gives you full control over the parsing by specifying a format string as well. You can use the same format string in both methods.
Another option is Chronic - http://chronic.rubyforge.org/
You just need to set the endian preference to force only MM/DD/YYYY date format:
Chronic::DEFAULT_OPTIONS[ :endian_precedence ] = [ :middle ]
However the default for Chronic is the out-of-order US date format anyway!

Date and Time Conversion in Ruby

I am currently looking on converting Thu Jun 10 16:17:55 +0530 2010 to 15/06/2010 or 15-06-2010 using Ruby
I am getting the dates from the DB as shown below.
#tickets = Ticket.find(:all)
for ticket in #tickets
print ticket.created_at
end
I have been looking around for resources as I a n00b in RoR/Ruby.
I have looked at
Class Date
Class DateTime
and couldn't make much of it.
Please let me know how I could achieve this conversion in Ruby.
P.S: I have tried using regular expressions as well. But with no luck
If you are using Rails (which is indicated by your tag)
Date.today.to_s can take a parameter which represents a key in your DATE_FORMATS hash
http://api.rubyonrails.org/classes/ActiveSupport/CoreExtensions/Date/Conversions.html#M000915
http://api.rubyonrails.org/classes/ActiveSupport/CoreExtensions/Time/Conversions.html#M000837
A much better solution than #strftime, since its reusable without having to remember the #strftime incantation or having loads of helpers to do the same thing.
Do this.
#tickets = Ticket.find(:all)
for ticket in #tickets
print ticket.created_at.strftime("%m/%d/%Y")
end
For complete reference of strftime see here
#tickets = Ticket.find(:all)
for ticket in #tickets
print ticket.created_at.strftime("%d/%m/%Y")
end
Also you may want to check out the strftime method in Ruby's stdlib.

Resources