Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MONGOID-5490: Deprecate :use_activesupport_time_zone #5488

Merged

Conversation

johnnyshields
Copy link
Contributor

@johnnyshields johnnyshields commented Sep 30, 2022

I'll raise a follow-up PR for Mongoid 9.0.

@johnnyshields
Copy link
Contributor Author

Ready for review

@johnnyshields johnnyshields changed the title [Mongoid 8.1] MONGOID-5490: Deprecate :use_activesupport_time_zone [READY FOR REVIEW] [Mongoid 8.1] MONGOID-5490: Deprecate :use_activesupport_time_zone Feb 10, 2023
@jamis jamis changed the title [READY FOR REVIEW] [Mongoid 8.1] MONGOID-5490: Deprecate :use_activesupport_time_zone MONGOID-5490: Deprecate :use_activesupport_time_zone Feb 28, 2023
@jamis jamis merged commit 1a97bc7 into mongodb:8.1-stable Feb 28, 2023
@johnnyshields johnnyshields deleted the use_activesupport_time_zone_deprecated branch February 28, 2023 21:51
jamis added a commit that referenced this pull request Mar 14, 2023
…) to master (#5561)

* MONGOID-5334 ensure localized demongoization works with symbol keys (#5416)

* MONGOID-5334 ensure localized demongoization works with symbol keys

* MONGOID-5334 add type check

* MONGOID-5334 change back type check

* MONGOID-5437 extract fallback enabling logic into macro (#5428)

* MONGOID-5438 initial changes from Johnny's PR

* MONGOID-5438 additional changes from johnny's
PR

* MONGOID-5437 fix tests

* MONGOID-5437 fix uniqueness test

* MONGOID-5334 enforce Hash type in localized demongoize (#5430)

* MONGOID-5334 add type check

* MONGOID-5334 change back type check

* MONGOID-5334 enforce Hash type in localized demongoize

* MONGOID-5456 cast castable values for integer/float/big decimal (#5431)

* MONGOID-5456 cast castable values for integer/float/big decimal

* MONGOID-5456 assert duration

* MONGOID-4403 Support Dirty Tracking changed from/to (#5432)

* MONGOID-4403 Support Dirty Tracking changed from/to

* Apply suggestions from code review

Co-authored-by: Oleg Pudeyev <[email protected]>

* MONGOID-4403 add false example

Co-authored-by: Oleg Pudeyev <[email protected]>

* MONGOID-5456 Add documentation for responding to to_* (#5434)

* MONGOID-5456 Add documentation for responding to to_*

* Apply suggestions from code review

Co-authored-by: Oleg Pudeyev <[email protected]>

Co-authored-by: Oleg Pudeyev <[email protected]>

* MONGOID-5461 Change custom field options example (#5438)

* MONGOID-5461 Change custom field options example

* Apply suggestions from code review

Co-authored-by: Oleg Pudeyev <[email protected]>

Co-authored-by: Oleg Pudeyev <[email protected]>

* MONGOID-5458 Document fallbacks option on localized fields (#5439)

* MONGOID-5460 Update custom field type protocol documentation (#5441)

* MONGOID-5457 Document + example of non-String field types in localized fields (#5444)

* MONGOID-4698 update warning when overriding criteria methods in scope (#5442)

* MONGOID-4698 update warning when overriding criteria methods in scope

* Apply suggestions from code review

Co-authored-by: Oleg Pudeyev <[email protected]>

Co-authored-by: Oleg Pudeyev <[email protected]>

* MONGOID-5417 Fix memory leak when call 'with' (#5409)

* MONGOID-4528 Add more dirty methods (#5440)

Co-authored-by: Oleg Pudeyev <[email protected]>

* MONGOID-4698 Correct warning to point out that klass.band doesnt conflict (#5450)

* MONGOID-5226 Allow setting "store_in collection" on document subclass (#5449)

* MONGOID-5226 Allow setting "store_in collection" on document subclass

* remove comment

* MONGOID-5226 add docs and testing

* Apply suggestions from code review

Co-authored-by: Oleg Pudeyev <[email protected]>

* MONGOID-5226 answer comments

Co-authored-by: Oleg Pudeyev <[email protected]>

* MONGOID-5293 put legacy_attributes in 7.5 defaults (#5425)

* MONGOID-5293 put legacy_attributes in 7.5

* switch order

* MONGOID-5293 fix defaults tests

* MONGOID-5438 Implement local override for i18n parameters (#5427)

* MONGOID-5438 Implement local override for i18n parameters

* MONGOID-5438 get rid of enfore_available_locale changes

* MONGOID-5438 get rid of before alls

* MONGOID-5438 fix tests caused by rspec precedence

* MONGOID-5438 attempt to fix tests when fallbacks disabled

* MONGOID-5438 make fallbacks default to return [self]

* MONGOID-5438 reset fallbacks

* MONGOID-5438 attempt to fix tests

* MONGOID-5438 retry_test

* MONGOID-5438 undefine fallback methods after fallback tests

* MONGOID-5438 add back defaults

* MONGOID-5438 use new macros

* MONGOID-5438 use before and after suite

* MONGOID-5438 update mrss

* MONGOID-5438 remove suite checks

* MONGOID-5370 Add collection options support (#5452)

Co-authored-by: Oleg Pudeyev <[email protected]>

* MONGOID-5474 add readonly! and raise on save/update (#5455)

* MONGOID-5474 add readonly! and raise on save/update

* MONGOID-5474 flip flag for 9.0 and add default

* MONGOID-5474 fix error tests

* Update spec/mongoid/config_spec.rb

* Apply suggestions from code review

Co-authored-by: Oleg Pudeyev <[email protected]>

* Update lib/mongoid/config.rb

Co-authored-by: Oleg Pudeyev <[email protected]>

Co-authored-by: Oleg Pudeyev <[email protected]>

* MONGOID-5474/MONGOID-5478 add docs and release notes (#5457)

* MONGOID-5474 add docs and release notes

* MONGOID-5474 use read-only

* MONGOID-5474 add configuration docs

* MOGOID-5474 switch order of flag docs

* add MONGOID-5477 use case

* MONGOID-5477 / MONGOID-5474 / MONGOID-5478 add user test (#5459)

* MONGOID-5474 add user test

* MONGOID-5474 change to read-only

* MOGOID-5474 switch order of flag docs

* MONGOID-5476 update 8.1 installation version (#5464)

* MONGOID-5474 flip the feature flag in 8.1 (#5456)

* MONGOID-5474 flip the feature flag in 8.1

* MONGOID-5474 flip flag in configuration

* MONGOID-5474 update config test

* MONGOID-5227 Add test and adjust docs for dotted attribute assignment in default scope (#5474)

* MONGOID-5227 Add test and adjust docs for dotted attribute assignment in default scope

* MONGOID-5227 adjust docs

* MONGOID-5227 clarify the docs and add tests

* Update docs/reference/queries.txt

Co-authored-by: Oleg Pudeyev <[email protected]>

Co-authored-by: Oleg Pudeyev <[email protected]>

* MONGOID-5493: Update Gem::Specification team info (#5476)

* MONGOID-5312 Document attributes method in reference (#5477)

* MONGOID-5312 Document attributes method in reference

* Apply suggestions from code review

Co-authored-by: Oleg Pudeyev <[email protected]>

* MONGOID-5312 update languagw

Co-authored-by: Oleg Pudeyev <[email protected]>

* MONGOID-5082 Create contributing guidelines (#5472)

* MONGOID-5082 Create contributing guidelines

* MONGOID-5082 propose test coverage

* MONGOID-5082 whitespace

* MONGOID-5082 update language

* add intro

* MONGOID-5445 Add load_async to criteria (#5454)

Co-authored-by: Neil Shweky <[email protected]>
Co-authored-by: Oleg Pudeyev <[email protected]>

* MONGOID-5291 Clarify whether :touch option is applicable for all updates (#5481)

* MONGOID-5261 Use config_override when a test requires a certain configuration option value (#5479)

* MONGOID-5261 Use config_override when a test requires a certain configuration option value

* fix syntax error

* MONGOID-5261 a little more cleanup

* MONGOID-5261 more cleanup

* more clean up

* MONGOID-5261 add persistence context override

* MONGOID-5261 add comment

* MONGOID-5100 allow selector arguments for .exists? (#5466)

* MONGOID-5100 allow selector arguments for .exists?

* MONGOID-5100 add examples to the docstrings

* MONGOID-5100 add docs and release notes

* Empty-Commit

* MONGOID-5100 allow false to be passed

* MONGOID-5100 update docs and docstrings

* MONGOID-5100 remove false as a param

* MONGOID-5100 put back nil/false

* MONGOID-5100 fix memory tests and fix limit == 0

* MONGOID-5262 Implement local time zone override for tests (#5480)

* MONGOID-5506 Update 8.x docs to document unscoped behavior changing in 9.0 (#5483)

* MONGOID-5506 Update 8.x docs to document unscoped behavior changing in 9.0

* Update lib/mongoid/scopable.rb

* RUBY-2942: Update documentation links (#5482)

* MONGOID-5363 Change #upsert to perform updating upsert rather than replacing upsert (#5492)

* MONGOID-5363 add :replace option to upsert method

* MONGOID-5363 add docs, release notes for 8.1/9.0

* MONGOID-5363 switch default in 8.1 (#5493)

* MONGOID-5363 update docstrings, flag default, and tests for 8.1

* specify default in mongoid 9

* MONGOID-5331 Document hash assignment to associations (#5494)

* MONGOID-5331 change buildable docs

* MONGOID-5331 add proxy tests and fix embedded_in proxy

* MONGOID-5331 update models

* Update spec/mongoid/association/embedded/embedded_in/proxy_spec.rb

* MONGOID-5331 update doc strings in batchable

* MONGOID-5331 add docs

* MONGOID-5331 add docs/release notes

* Apply suggestions from code review

* Update spec/mongoid/association/embedded/embedded_in/proxy_spec.rb

* RUBY-3097 add/use monotonic time for deadlines (#5491)

* RUBY-3097 add/use monotonic time for deadlines

* remove license and fix tests

* MONGOID-3834 Document association availability in custom getters and setters (#5503)

* MONGOID-3834 Document association availability in custom getters and setters

* add callbacks note

* MONGOID-5496: Update Getting Started (Rails) tutorial for Rails 7 (#5509)

* Clone Rails Tutorial and create variation for Rails 7

* Update Existing Application docs for Rails 7

* MONGOID-5532: Fix path to comments partial (#5511)

* MONGOID-5533: Unnecessary edits to Comment model in Rails Getting Started (#5512)

* Add note about workaround for MONGOID-4885 in Rails 6 docs

* Fix whitespace

* MONGOID-5521 migrate Mongoid docs to snooty (#5513)

* fix code blocks

* MONGOID-5521 fix all parse errors

* build docs with github action

* Revert "build docs with github action"

This reverts commit 9ffda44.

* MONGOID-5496: Update Rails 7 Tutorial (#5515)

* Remove `--skip-bundle` from _Optionally Skip Tests_ section

* MONGOID-5518: Add Puma dependency to Sinatra tutorial (#5516)

* MONGOID-5453 Add .none_of query method (#5524)

* "none_of" query method

* update docs and release notes

* fix underline for header

* MONGOID-5539: Fix example in aggregation reference documentation (#5536)

* Update aggregation.txt

* Update aggregation.txt

* MONGOID-5228 disallow _id to be updated on persisted documents (ported to 8.1-stable) (#5545)

* port #5542 to 8.1-stable

* test tweak

* update 8.1 release notes to mention immutable_ids

* MONGOID-5490: Deprecate :use_activesupport_time_zone (#5488)

* Deprecate the use_activesupport_time_zone_deprecated config option.

* Typo

* Update config.rb

* Update mongoid-8.1.txt

---------

Co-authored-by: shields <[email protected]>

* MONGOID-5509: Deprecate all feature flags which were introduced in Mongoid 7.x (#5489)

* Deprecate the use_activesupport_time_zone_deprecated config option.

* Typo

* Mongoid 8.1 should deprecate all feature flags which were introduced in the 7.x series, with intent to remove them in Mongoid 9.0.

---------

Co-authored-by: shields <[email protected]>

* Remove :use_activesupport_time_zone option + improve readme around timezone usage given the change

* Remove deprecated :broken_aggregables config option

* Remove deprecated :broken_alias_handling config option

* Remove deprecated :broken_and config option

* Remove deprecated :broken_scoping config option

* Remove deprecated :broken_updates config option

* Remove deprecated :compare_time_by_ms config option

* Remove deprecated :legacy_attributes config option

* Remove deprecated :legacy_pluck_distinct config option

* Remove deprecated :legacy_triple_equals config option

* Remove deprecated :object_id_as_json_oid config option

* Remove deprecated :overwrite_chained_operators config option

* Drop support for config.load_defaults versions "7.3", "7.4", "7.5", as their options are no longer available.

* Fix broken spec in Mongoid 8.1

---------

Co-authored-by: Neil Shweky <[email protected]>
Co-authored-by: Oleg Pudeyev <[email protected]>
Co-authored-by: Dmitry Rybakov <[email protected]>
Co-authored-by: Alex Bevilacqua <[email protected]>
Co-authored-by: Jamis Buck <[email protected]>
Co-authored-by: Jamis Buck <[email protected]>
Co-authored-by: shields <[email protected]>
@sandstrom
Copy link

sandstrom commented Oct 3, 2024

Is there a way to continue using Time instances?

We have this across 85 database fields, and changing to ActiveSupport::TimeWithZone would be quite a lot of work throughout our app (affects type checking, etc).

For example, would a workaround be for us to defined Time as our own custom field type? Would that take precedence over the default ext (https://github.com/mongodb/mongoid/blob/master/lib/mongoid/extensions/time.rb)?

https://www.mongodb.com/docs/mongoid/master/reference/fields/#custom-field-types

Our app is only using UTC internally and is never concerned with timezones (for us that's a client concern), so we'd rather not be forced to use TimeWithZone.


Also, I must say I don't really see the benefit that this change brings.

Instead of allowing storage of one of Ruby's built-in objects, it's no longer going to be possible to store vanilla Time in the database.

And the change itself seems to be just this:

# BEFORE
if use_activesupport_time_zone == false
  return time
else
  return time.in_time_zone()
end

# AFTER
return time

In other words, the maintenance burden of keeping this option shouldn't be huge.

@jamis
Copy link
Contributor

jamis commented Oct 3, 2024

Hello @sandstrom,

Please forgive me if I'm misunderstanding your concern, but are you worried that the following will no longer be supported?

class MyModel
  include Mongoid::Document
  field :timestamp, type: Time
end

m = MyModel.create(timestamp: Time.now)

Using Time as a field type is still supported, even after this change. Have you experienced something breaking as a result of the PR? If so, please let us know; it's probably a bug.

@sandstrom
Copy link

sandstrom commented Oct 4, 2024

@jamis Thanks for chiming in! 😄

Our problem is that:

MyModel.create(timestamp: Time.now).timestamp.class #=> 'ActiveSupport::TimeWithZone
# instead of returning an instance of `Time`

This causes problems with typing (e.g. Sorbet) and type checks such as m.timestamp.class === 'Time' now being false.

Although TimeWithZone is using some trickery to pretend being an instance of Time, it de-facto isn't.

https://github.com/rails/rails/blob/a11f0a63673d274c59c69c2688c63ba303b86193/activesupport/lib/active_support/time_with_zone.rb#L499-L501


So my question regarding this change would be, how important is this deprecation, and why is it needed?

Supporting transparent (in/out) support for Ruby's built-in types into the database, i.e. let me just pass in my Time and get it back unchanged, seems like a pretty basic feature. One that Mongoid also has had for a long time! So why remove that?

I see how some might feel that timezones should be handled by the database, but I'd argue that it's quite common to do what we are doing, and just operating at UTC everywhere internally in our system (timezone handling, which we do a lot btw, are a client-side concern for us).

@jamis
Copy link
Contributor

jamis commented Oct 4, 2024

@sandstrom -- what version of Mongoid are you using? It seems to work okay for me with Mongoid 9:

class MyModel
  include Mongoid::Document

  field :timestamp, type: Time
end

m = MyModel.create(timestamp: Time.now)
p m.timestamp.class #=> Time
p m.reload.timestamp
p m.timestamp.class #=> Time

@sandstrom
Copy link

@jamis We've downgraded to 8.0 for now, because this was causing problems in our code base.

Hmm, strange. I cannot really square that with the information in the deprecation message, this PR and the source code.

This is what the code on master does:

https://github.com/mongodb/mongoid/blob/master/lib/mongoid/extensions/time.rb#L59

This is 9.0:

https://github.com/mongodb/mongoid/blob/v9.0.2/lib/mongoid/extensions/time.rb#L59

@johnnyshields
Copy link
Contributor Author

johnnyshields commented Oct 5, 2024

@sandstrom

The changes in Mongoid 9.0 affect how time values are deserialized from the database, specifically it now returns ActiveSupport::TimeWithZone (AS::TWZ) in 100% of cases, and removes the previously deprecated settings to allow returning Time. As @jamis mentions, this happens (intentionally) even when you specify field type: Time. Aside from affecting Sorbet checks, given that AS::TWZ is fully compatible with Time I would not expect it to affect your app behavior itself.

Returning AS::TWZ is the correct default for the vast majority of Rails apps that use Mongoid--this is also what ActiveRecord does. This article provides some background; if you need to use timezones at all (most apps do) Time is unwieldy.

@jamis -- I do think makes sense to re-introduce a non-default option that allows returning Time (not AS::TWZ) for apps that are strictly UTC-only, maybe call it use_native_time. Strictly speaking, Time has ~3x better CPU performance than AS::TWZ (but both are very fast, so this is a negligible difference in most real-world apps)

@sandstrom
Copy link

The changes in Mongoid 9.0 affect how time values are deserialized from the database, specifically it now returns ActiveSupport::TimeWithZone (AS::TWZ) in 100% of cases, and removes the previously deprecated settings to allow returning Time. As @jamis mentions, this happens (intentionally) even when you specify field type: Time. Aside from affecting Sorbet checks, given that AS::TWZ is fully compatible with Time I would not expect it to affect your app behavior itself.

Thanks, yes I know how it works, I looked at the source code.

Returning AS::TWZ is the correct default for the vast majority of Rails apps that use Mongoid--this is also what ActiveRecord does. This article provides some background; if you need to use timezones at all (most apps do) Time is unwieldy.

I've read the article and I see the point for server-generated HTML. But in our case the app is an API that treats everything as UTC, and timezones is a client-side concern (Javascript layer). This works really well for single-page apps and mobile apps, where we have access to the browser/phone local timezone and can 'convert at the edge'.

But for us, I'd argue that using vanilla Time works great and comes with zero downsides. We support all the IANA timezones in our app with this approach, and have had no problems with it (again, since it's treated as a front-end concern).

I do think makes sense to re-introduce a non-default option that allows returning Time (not AS::TWZ) for apps that are strictly UTC-only, maybe call it use_native_time.

This would be awesome and would totally solve our use-case (and I'm guessing we're not the only ones)! 😄 🎉

@jamis
Copy link
Contributor

jamis commented Oct 7, 2024

We've downgraded to 8.0 for now, because this was causing problems in our code base.

@sandstrom -- I'm really sorry that this has been causing you grief. I'd like to understand better exactly how this is problematic; can you share an example or two of the kind of problem you're running into? As @johnnyshields said, ActiveSupport::TimeWithZone is supposed to be API-compatible with Time, and the translation to-and-from in Mongoid ought to be transparent. I feel like any exceptions to that are probably bugs that we should address.

I'm generally uncomfortable with adding more knobs and dials to Mongoid, as they usually make it harder to understand behavior. We can look into adding that config option back, but as I said, I really want to make sure I understand (and can replicate) the issue being reported here, first.

@sandstrom
Copy link

sandstrom commented Oct 8, 2024

@jamis Same thing as I wrote above basically. Even though it has the same methods, it's still a different class.

This breaks type checking in a bunch of places for us.

Below are a few simple code examples on how to see that they are different classes:

t1 = Time.now #=> 2024-10-08 08:13:04.232558 +0000
t2 = t1.in_time_zone('UTC') # => Tue, 08 Oct 2024 08:13:04.232558000 UTC +00:00
t1.class #=> Time
t2.class #=> ActiveSupport::TimeWithZone

# different output
t1.to_s #=> "2024-10-08 08:13:04 +0000"
t2.to_s #=> "2024-10-08 08:13:04 UTC"

# different classes
t1.class == t2.class #=> false

@jamis
Copy link
Contributor

jamis commented Oct 8, 2024

@sandstrom -- the bit that I'm struggling with is that when I query the class of a Time-valued field, I get Time, not ActiveSupport::TimeWithZone. I really need a concrete example that I can use to evaluate the impact here.

Please let me know:

  • Your OS
  • Your Ruby version
  • Your Rails version
  • Your mongoid gem version
  • Your mongo gem version
  • The version of MongoDB that you're querying
  • A minimal script that I can run locally that exhibits the issue
  • Any other relevant environmental information that might help me debug this

I know that, for you, the problem is obvious, and painful, and I'm sorry. Please help it to be obvious and painful to me, as well, so that I can understand what is fundamentally going on here.

@sandstrom
Copy link

sandstrom commented Oct 8, 2024

@jamis Happy to help, but the way I'm reading the code it's configured to return an instance of ActiveSupport::TimeWithZone? Isn't that what this PR was all about?

https://github.com/mongodb/mongoid/blob/master/lib/mongoid/extensions/time.rb#L59

@johnnyshields What you said above doesn't see to square with the experience that @jamis has, i.e. he doesn't get an instance of ActiveSupport::TimeWithZone back.

The changes in Mongoid 9.0 affect how time values are deserialized from the database, specifically it now returns ActiveSupport::TimeWithZone (AS::TWZ) in 100% of cases, and removes the previously deprecated settings to allow returning Time.

But maybe I've misunderstood something and you are talking about different things?

Details

  • macOS Sonoma 14.5 (23F79)
  • ruby 3.2.4 (2024-04-23 revision af471c0e01) [arm64-darwin23]
  • Rails 7.1.4
  • mongoid 8.1.6 (now), but we had 9.0.1 when this was a problem (we've reverted for now)
  • mongo gem 2.20.1
  • mongodb 7.0.12
  • [script to reproduce; I can get back to you on this one if needed]

@jamis
Copy link
Contributor

jamis commented Oct 8, 2024

@sandstrom Let's try it this way, then. Here's a script that, when I run it, prints the following:

$ ruby 5490.rb
Ruby: 3.2.4
Mongo: 2.20.1
Mongoid: 9.0.1
Rails: 7.1.4
after creation, `t.time` is a Time
after reload, `t.time` is a Time

Can you run it and let me know what it tells you?

The script itself:

require 'bundler/inline'

gemfile do
  source 'https://rubygems.org'
  gem 'mongo', '=2.20.1'
  gem 'mongoid', '=9.0.1'
  gem 'rails', '=7.1.4'
end

puts "Ruby: #{RUBY_VERSION}"
puts "Mongo: #{Mongo::VERSION}"
puts "Mongoid: #{Mongoid::VERSION}"
puts "Rails: #{Rails.version}"

Mongoid.connect_to '5490-sandbox'

class Troubleshooting
  include Mongoid::Document

  field :time, type: Time
end

t = Troubleshooting.create!(time: Time.now)

puts "after creation, `t.time` is a #{t.time.class.inspect}"

t = Troubleshooting.find(t.id)
puts "after reload, `t.time` is a #{t.time.class.inspect}"

@jamis
Copy link
Contributor

jamis commented Oct 8, 2024

Ah, I think I understand the issue now. Mongoid.use_utc defaults to false, which will cause Mongoid.time_zone to return whatever time zone is configured on Time.zone (nil, by default). When that is nil, Time#in_time_zone returns a plain Time instance.

I suspect you've got Mongoid.use_utc = true, though, which uses "UTC" as the time zone. With this set, I also get ActiveSupport::TimeWithZone instances.

Does that sound like how you've got things configured on your end?

@sandstrom
Copy link

@jamis Correct!

Time.zone #=> #<ActiveSupport::TimeZone:0x00000001051a8758 @name="UTC", @tzinfo=#<TZInfo::DataTimezone: Etc/UTC>, @utc_offset=nil>

Mongoid.use_utc #=> true

@jamis
Copy link
Contributor

jamis commented Oct 8, 2024

I'm still not clear on exactly how the ActiveSupport::TimeWithZone instances are causing problems. Pretend I know nothing about Sorbet, and have never used any type checkers with Ruby before. Can you give me something I could run locally, that would help me feel the pain you're experiencing there?

@sandstrom
Copy link

sandstrom commented Oct 9, 2024

@jamis Thanks for asking!

In addition to type checking here are two examples of code that would break:

## EXAMPLE 1

# This is a simplified example of type checking that we have in a few places,
# in our system. For example when interfacing with external APIs, that only accept
# some types of data, we need to first verify that our upstream code doesn't send anything
# that the API cannot handle.
my_object = {
  'created_at': Time.now,
  'updated_at': Time.now,
  'name': 'John Doe',
  'email': '[email protected]',
  'age': 30,
  '…': '…',
}

# make sure we only send scalar values
my_object.each do |key, value|
  permitted_scalars = [String, Integer, Float, TrueClass, FalseClass, NilClass, Time, Date]

  unless permitted_scalars.include?(value.class)
    my_object.delete(key) # this broke when we updated to Mongoid 9
  end
end

# continue with the rest of the code

## EXAMPLE 2

# Here is an example of an internal serializer class expecting a time
# class. It will raise if it's passed a value that isn't Time.
class UserSerializer < Base
  type 'User'
  description 'An employee at your company'

  field :name, type: String, description: 'Full name'
  field :email, type: String, description: 'Primary email'
  field :company_name, type: String, description: 'Company name'
  field :previous_sign_ins, type: Array, subtype: Time
  field :last_sign_in, type: Time, description: 'Last sign in time'

  association :home_location, nullable: true, blueprint: LocationSerializer
  association :work_location, nullable: true, blueprint: LocationSerializer
end

In short, all our data is stored in the database and will pass through Mongoid, and 100% of our objects have one or more time fields.

We have ~100 models and a code base of ~800k lines of code. Swapping out one class for another is bound to cause a lot of trouble.

One could argue that we should move the entire code base over to using ActiveRecord::TimeWithZone instead. But that's a pretty daunting task, with tens of thousands of lines of code that we would have to update.

@jamis
Copy link
Contributor

jamis commented Oct 9, 2024

Thanks @sandstrom -- I appreciate the additional information. That does help.

As you said in your last paragraph:

One could argue that we should move the entire code base over to using ActiveRecord::TimeWithZone instead. But that's a pretty daunting task, with tens of thousands of lines of code that we would have to update.

I think this is spot on, on both counts. In an ideal world, you'd be able to update your type checking to at least include TimeWithZone, or to accommodate duck typing (object.acts_like?(:time)). With a legacy code base, this is not always a luxury we can afford, though.

I am still reluctant to restore the :use_activesupport_time_zone flag; it's a legacy feature, and though it's not a complicated one, it is one more fiddly bit that we have to support and remember.

However, it should be possible for you to work around this by monkey patching Mongoid::Config.time_zone:

module PatchTimeZone
  def time_zone
    nil
  end
end

Mongoid::Config.extend(PatchTimeZone)

The Mongoid::Config.time_zone method is only used in that one place in Mongoid, and so changing it to return nil shouldn't have any other side effects (unless you happen to be calling it yourself, or possibly using a library that does). It's worth trying, at least, and seeing how it works for you.

@sandstrom
Copy link

sandstrom commented Oct 9, 2024

@jamis Your decision since you are the maintainer! 😄

I understand why you want to deprecate and remove the existing flag. To signal that people should avoid using it. That makes sense to me!

However, if I were you I'd also consider what Johnny suggested above (#5488 (comment)), adding use_native_time or a similar configuration value, that would default to false.

You'd provide a migration path for others, and supporting it would be trivial since it's just an if/else block.

if config.use_native_time == true
  return time
else
  return time.in_time_zone()
end

@sandstrom
Copy link

@jamis Do you want me to provide a PR in line with my suggestion in the comment above?

@jamis
Copy link
Contributor

jamis commented Nov 1, 2024

Hey @sandstrom, I'm reluctant to add another dial here, especially since it would be primarily a crutch for legacy code. There would be little or no benefit to new projects that are just now adopting Mongoid, which means the interface adds (just a little) more mental overhead for new adopters.

It's easy to justify adding something like this because it adds "just a little" overhead, but I hope you can see that it's the precedent that concerns me here. We justify adding this one, which makes it easier to justify adding another one somewhere else, and so forth. At some point, we need to draw a line in the sand and say "this is how it is." And sadly, whenever a line is drawn, there will always be someone on the other side of it who wishes it had been drawn "just a little" bit closer to them. I really do hate saying "no," but I think it needs to be said, here.

I'd hate for you to put in the work on something that I honestly don't feel has much hope of being merged. In your case, a shim (the monkey patch I proposed) will hopefully help your existing code work with the newer behavior.

Things that would make me reconsider this include:

  • Discovering a reasonably common use case that would cause new code (as opposed to legacy code) to be impacted by the current behavior.
  • Learning that a significant number of users have stumbled over this. (As it is, you're the first person we've heard from who has tripped over this in the 6 months since Mongoid 9 was released.)

I hope you know that we have heard you, and we understand that this isn't an ideal solution for you. I really am sorry about that. :/

@sandstrom
Copy link

@jamis I understand, it's a reasonable argument!

Have a great weekend! 😄

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants