Hey guys, I have a time-sensitive application.....not in the sense that its completion is urgent (although like all projects, it is), but rather in the sense that much of the functionality is determined by the actual time/date. As a result, I''ve found that I need to freeze the concept of today both in development and testing (more-so in testing so that I can write static test sets). I''ve started down the road of using an environment variable to store our date (Date.today if production, else a date that all of my tests can be based off of). I''m unhappy with this solution for a few reasons. First, the ENV hash requires my values to be strings (try putting this in your environment.rb file: ENV[''today''] = Date.new(2007, 5, 15) --- I was shocked when I got a TypeError). As such, for this to be useful, I need to write a function that "hides" this implementation---- explicitly a reader/writer pair that converts between strings and dates. Since this reader/writer pair needs to be accessible from all parts of my application (testing, controllers, views), I find myself having to put this pair in multiple places....not very DRY. Furthermore (and perhaps worst of all), I actually want my models to use this redefinition of Date.today. Now, my models _should_ be completely unaware that they''re being used in a Rails app, so I have a big problem with them knowing about this. However, I''m not sure of another way around it. My last problem just stems from the fact that my whole app now needs to know about this "other" way to get Date.today (some global function defined somewhere).....just doesn''t feel right. Another route that I considered was to reopen the Date class and redefine the class method "today" conditionally (based on which environment was loaded). However, I ran into some errors with the following snippet of code in my environment.rb file: class Date def self.today(sg=ITALY) new(2007, 5, 15) end end It''s possible the environment.rb file is the incorrect place to put this. I''m open to any thoughts/suggestions/whatever. How has anyone else handled this problem in the past? What issues did you run into? -John --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
I forgot to mention.....the error I received when I tried to override the Date.today function as coded below was.... C:/Program Files (x86)/Ruby/lib/ruby/site_ruby/1.8/rubygems/ source_index.rb:92:i n `load_specification'': undefined method `_parse'' for Rails::Initializer::Date:C lass (NoMethodError) from C:/Program Files (x86)/Ruby/lib/ruby/site_ruby/1.8/ rubygems/specifi cation.rb:13:in `today'' from C:/Program Files (x86)/Ruby/lib/ruby/site_ruby/1.8/ rubygems/specifi cation.rb:314:in `date='' from C:/Program Files (x86)/Ruby/lib/ruby/site_ruby/1.8/ rubygems/specifi cation.rb:416:in `send'' from C:/Program Files (x86)/Ruby/lib/ruby/site_ruby/1.8/ rubygems/specifi cation.rb:416:in `initialize'' from C:/Program Files (x86)/Ruby/lib/ruby/site_ruby/1.8/ rubygems/specifi cation.rb:412:in `each'' from C:/Program Files (x86)/Ruby/lib/ruby/site_ruby/1.8/ rubygems/specifi cation.rb:412:in `initialize'' from (eval):1:in `new'' from (eval):1:in `load_specification'' ... 29 levels... from C:/Program Files (x86)/Ruby/lib/ruby/gems/1.8/gems/ rails-1.2.3/lib/ commands/server.rb:39 from C:/Program Files (x86)/Ruby/lib/ruby/site_ruby/1.8/ rubygems/custom_ require.rb:27:in `gem_original_require'' from C:/Program Files (x86)/Ruby/lib/ruby/site_ruby/1.8/ rubygems/custom_ require.rb:27:in `require'' from script/server:3 On Jul 4, 10:09 am, John Trupiano <jtrupi...-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:> Hey guys, > > I have a time-sensitive application.....not in the sense that its > completion is urgent (although like all projects, it is), but rather > in the sense that much of the functionality is determined by the > actual time/date. > > As a result, I''ve found that I need to freeze the concept of today > both in development and testing (more-so in testing so that I can > write static test sets). > > I''ve started down the road of using an environment variable to store > our date (Date.today if production, else a date that all of my tests > can be based off of). I''m unhappy with this solution for a few > reasons. > > First, the ENV hash requires my values to be strings (try putting this > in your environment.rb file: ENV[''today''] = Date.new(2007, 5, 15) --- > I was shocked when I got a TypeError). As such, for this to be > useful, I need to write a function that "hides" this > implementation---- explicitly a reader/writer pair that converts > between strings and dates. Since this reader/writer pair needs to be > accessible from all parts of my application (testing, controllers, > views), I find myself having to put this pair in multiple > places....not very DRY. > > Furthermore (and perhaps worst of all), I actually want my models to > use this redefinition of Date.today. Now, my models _should_ be > completely unaware that they''re being used in a Rails app, so I have a > big problem with them knowing about this. However, I''m not sure of > another way around it. > > My last problem just stems from the fact that my whole app now needs > to know about this "other" way to get Date.today (some global function > defined somewhere).....just doesn''t feel right. > > Another route that I considered was to reopen the Date class and > redefine the class method "today" conditionally (based on which > environment was loaded). However, I ran into some errors with the > following snippet of code in my environment.rb file: > > class Date > def self.today(sg=ITALY) > new(2007, 5, 15) > end > end > > It''s possible the environment.rb file is the incorrect place to put > this. > > I''m open to any thoughts/suggestions/whatever. How has anyone else > handled this problem in the past? What issues did you run into? > > -John--~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
John Trupiano wrote:> As a result, I''ve found that I need to freeze the concept of today > both in development and testing (more-so in testing so that I can > write static test sets).I had an app a while back with a similar issue. Depending on the current day would determine the results so I needed to be able to define "today" to have reliable test. Here is the result I came up with: class Time class << self # Time we might be behaving as attr_reader :mock_time # Set new time to pretend we are. def mock_time=(new_now) @mock_time = new_now end # now() with possible augmentation def now_with_mock_time mock_time || now_without_mock_time end alias_method_chain :now, :mock_time end end Just put this in your test_helper.rb. This would redefine "now" to use the value I wanted for today. Everything seems to flow well from that so 3.days.ago returns the right value. It even works with the various time zone plugins to get time zones working right in Rails. So to set "today" to be Feb 1, 2006 simply do: Time.mock_time = Time.gm 2006, 2, 1 To restore the normal space-time continuum just: Time.mock_time = nil Let me know if you need any more pointers on this. Eric --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
This is fantastic. Thanks Eric. I''ll let you know if anything else comes up. -John On Jul 4, 1:42 pm, Eric Anderson <e...-ANzg6odk14w@public.gmane.org> wrote:> John Trupiano wrote: > > As a result, I''ve found that I need to freeze the concept of today > > both in development and testing (more-so in testing so that I can > > write static test sets). > > I had an app a while back with a similar issue. Depending on the current > day would determine the results so I needed to be able to define "today" > to have reliable test. Here is the result I came up with: > > class Time > class << self > # Time we might be behaving as > attr_reader :mock_time > > # Set new time to pretend we are. > def mock_time=(new_now) > @mock_time = new_now > end > > # now() with possible augmentation > def now_with_mock_time > mock_time || now_without_mock_time > end > alias_method_chain :now, :mock_time > end > end > > Just put this in your test_helper.rb. > > This would redefine "now" to use the value I wanted for today. > Everything seems to flow well from that so 3.days.ago returns the right > value. It even works with the various time zone plugins to get time > zones working right in Rails. > > So to set "today" to be Feb 1, 2006 simply do: > > Time.mock_time = Time.gm 2006, 2, 1 > > To restore the normal space-time continuum just: > > Time.mock_time = nil > > Let me know if you need any more pointers on this. > > Eric--~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
I use a variant of the time / date mocking for testing. Also includes this nifty block helper, which iterates over the passed values setting the time to each one before executing the block.. class Date @@forced_today = nil class << self alias :unforced_today :today def forced_today return @@forced_today ? @@forced_today : unforced_today end alias :today :forced_today def forced_today=(now) @@forced_today = now end end end class DateTime @@forced_now = nil class << self alias :unforced_now :now def forced_now return @@forced_now ? @@forced_now : unforced_now end alias :now :forced_now def forced_now=(now) @@forced_now = now end end end def with_dates(*dates, &block) dates.flatten.each do |date| begin DateTime.forced_now = case date when String: DateTime.parse(date) when Time: DateTime.parse(date.to_s) else date end Date.forced_today = Date.new(DateTime.forced_now.year, DateTime.forced_now.month, DateTime.forced_now.day) yield rescue Exception => e raise e ensure DateTime.forced_now = nil Date.forced_today = nil end end end put this all in your test_helper.rb file. You can do something similar with Time as well. now you can do this in your tests.. with_dates(Date.today + 1, Date.today >> 1) do some_code.... end I like this method because you can''t forget to set the clock back to normal. _Kevin On Jul 4, 2:30 pm, John Trupiano <jtrupi...-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:> This is fantastic. Thanks Eric. I''ll let you know if anything else > comes up. > > -John > > On Jul 4, 1:42 pm, Eric Anderson <e...-ANzg6odk14w@public.gmane.org> wrote: > > > John Trupiano wrote: > > > As a result, I''ve found that I need to freeze the concept of today > > > both in development and testing (more-so in testing so that I can > > > write static test sets). > > > I had an app a while back with a similar issue. Depending on the current > > day would determine the results so I needed to be able to define "today" > > to have reliable test. Here is the result I came up with: > > > class Time > > class << self > > # Time we might be behaving as > > attr_reader :mock_time > > > # Set new time to pretend we are. > > def mock_time=(new_now) > > @mock_time = new_now > > end > > > # now() with possible augmentation > > def now_with_mock_time > > mock_time || now_without_mock_time > > end > > alias_method_chain :now, :mock_time > > end > > end > > > Just put this in your test_helper.rb. > > > This would redefine "now" to use the value I wanted for today. > > Everything seems to flow well from that so 3.days.ago returns the right > > value. It even works with the various time zone plugins to get time > > zones working right in Rails. > > > So to set "today" to be Feb 1, 2006 simply do: > > > Time.mock_time = Time.gm 2006, 2, 1 > > > To restore the normal space-time continuum just: > > > Time.mock_time = nil > > > Let me know if you need any more pointers on this. > > > Eric--~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
_Kevin wrote:> I like this method because you can''t forget to set the clock back to > normal.Looks like a nice robust implemention. I just put: Time.mock_time = nil in my teardown method to make sure any time changes are reset after a test. Eric --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---