The loading another class has side effect with mongoid - ruby-on-rails

Can somebody explain me this:
User < AbstractUser
store_in collection: 'users'
InvitedUser < AbstractUser
store_in collection: 'invited_users'
Then when I am loading the InvitedUser while working with a user object I have the following side effect.
[3] pry(#<UserInvitationsController>)> User.collection
=> #<Moped::Collection:0x007f8f008f21e0
...
#name="users">
[4] pry(#<UserInvitationsController>)> InvitedUser
=> false
[5] pry(#<UserInvitationsController>)> User.collection
=> #<Moped::Collection:0x007f8f00202d30
#name="invited_users">
And from then on the mongoid operations on User won't work properly...
I also reported an issue on github:
https://github.com/mongoid/mongoid/issues/3408

It seems that the side effect is caused by the common ancestor.
Without common ancestor everything is like it should be.
=> User
[2] pry(main)> User.collection.name
=> "users"
[3] pry(main)> InvitedUser.collection.name
=> "invited_users"
[4] pry(main)> User.collection.name
=> "users"
[5] pry(main)> InvitedUser.collection.name
=> "invited_users"
[6] pry(main)> exit
With common ancestor the side effect appears
→ ./bin/rails c
Loading development environment (Rails 4.0.0)
[1] pry(main)> User.collection.name
=> "users"
[2] pry(main)> InvitedUser.collection.name
=> "invited_users"
[3] pry(main)> User.collection.name
=> "invited_users"
[4] pry(main)> InvitedUser.collection.name
=> "invited_users"
[5] pry(main)>

Related

Why does this where query no longer work properly for ShopifyAPI?

I am trying to do a simple where query on both the Checkout class and Order class via the REST ShopifyAPI, but it keeps returning inaccurate data.
Here are two examples:
[12] pry(main)> Order.count
=> 9
[13] pry(main)> Order.where(created_at: (Time.now - 1.minute)..(Time.now)).count
=> 9
[14] pry(main)> Order.where(created_at: (Time.now - 1.second)..(Time.now)).count
=> 9
[15] pry(main)> Order.first.created_at
=> "2021-05-15T02:59:36-05:00"
[16] pry(main)> Order.last.created_at
=> "2021-04-23T02:43:44-05:00"
And the same thing for Checkout:
[8] pry(main)> Checkout.where(created_at: (Time.now - 24.hours)..(Time.now)).count
=> 6
[9] pry(main)> Checkout.where(created_at: (Time.now - 10.minutes)..(Time.now)).count
=> 6
[10] pry(main)> Checkout.where(created_at: (Time.now - 1.minute)..(Time.now)).count
=> 6
[11] pry(main)> Checkout.count
=> 6
[18] pry(main)> Checkout.first.created_at
=> "2021-04-29T00:13:16-05:00"
[19] pry(main)> Checkout.last.created_at
=> "2021-05-15T03:00:37-05:00"
What could be the cause of this?
Edit 1
Strangely enough, this issue seems to not show itself when I try another model like Product:
[28] pry(main)> Product.where(title: "High Coverage Foundation").count
=> 1
[29] pry(main)> Product.count
=> 6
That's the correct feedback I was expecting. I am expecting something similar for the Order model too, but for some reason it isn't working properly.
I have restarted my local terminal session, and reinstalled my Shopify-api-cli...to no avail.
Edit 2
See more examples from both classes that show the existence of those objects which indicates that the query should work.
[51] pry(main)> Order.first.id
=> 3779683877046
[52] pry(main)> Order.last.id
=> 3741986750646
[53] pry(main)> Order.first.created_at
=> "2021-05-15T02:59:36-05:00"
[54] pry(main)> Order.last.created_at
=> "2021-04-23T02:43:44-05:00"
[55] pry(main)> Checkout.first.id
=> "ef8dd57a1b1911da9077df7789698847"
[56] pry(main)> Checkout.last.id
=> "7133c7aeb0ff3b4b506c2a66e4190ee3"
[57] pry(main)> Checkout.first.created_at
=> "2021-04-29T00:13:16-05:00"
[58] pry(main)> Checkout.last.created_at
=> "2021-05-15T03:00:37-05:00"
Edit 3
So it seems that it works for queries done on attributes that have a string, but not an integer and datetime is inconsistent:
[79] pry(main)> Order.where(cart_token: '36e017f94cbf19bee298d61334b68225').count
=> 1 // #RIGHT
[80] pry(main)> Order.where(created_at: '2021-05-15T02:59:36-05:00').count
=> 9 // #WRONG
[81] pry(main)> Order.where(current_total_price: 294.14).count
=> 9 // #WRONG
[82] pry(main)> Order.where(email: 'alex#test.com').count
=> 1 // #RIGHT
[83] pry(main)> Order.where(processed_at: '2021-05-15T02:59:35-05:00').count
=> 3 // #RIGHT STRANGELY ENOUGH
[97] pry(main)> Order.where(processed_at: '2021-05-15T02:59:35-05:00').first.id
=> 3779683877046 // #RIGHT
[98] pry(main)> Order.where(processed_at: '2021-05-15T02:59:35-05:00').last.id
=> 3779668639926 // #RIGHT
I got a response on Shopify's official forum which can be seen here.
TL;DR, Shopify's rubygem doesn't implement ranged dates exactly like ActiveRecord does, they use created_at_min and created_at_max attributes on the model.
So the correct query for the ShopifyAPI for that where query is:
ShopifyAPI::Order.where(created_at_min: Time.now - 6.days, created_at_max: Time.now).count

rails console OR irb find out the RAILS_VERSION of the current environment

Somewhat related to:
Determine ruby version from within Rails
How to find out the RAILS_VERSION from within rails console?
Short info in Rails.gem_version:
Rails.gem_version
# => Gem::Version.new("6.0.1")
Long info in Rails::Info:
Rails::Info
=> About your application's environment
Rails version 6.0.1
Ruby version ruby 2....
RubyGems version 2.7...
Rack version 2.0...
Middleware UTF8Cleaner::Middleware, Rack::Cors, ActionDispatch::HostAuthorization, Rack::Sendfile, ActionDispatch::Static, Rack::Lock, ActionDispatch::Executor, ...
Application root /home/aaa/bb/app
Environment development
Database adapter mysql
Database schema version 20195201212345
One of possible ways is:
pry(main)> Rails.version
=> "5.1.7"
or like above was suggested:
pry(main)> Rails.gem_version
=> Gem::Version.new("5.1.7")
Then you can build some conditions using this technique of versions comparisons:
[20] pry(main)> Rails.version.starts_with?('5.1')
=> true
[21] pry(main)> Gem::Version.new(Rails.version) > Gem::Version.new('5.1.2')
=> true
[22] pry(main)> Gem::Version.new(Rails.version) > Gem::Version.new('5.2.3')
=> false
[23] pry(main)> Gem::Version.new(Rails.version) == Gem::Version.new('5.1')
=> false
[24] pry(main)> Gem::Version.new(Rails.version) == Gem::Version.new('5.1.7')
=> true
OR
[28] pry(main)> Rails.gem_version == Gem::Version.new('5.1.7')
=> true
[29] pry(main)> Rails.gem_version >= Gem::Version.new('5.1.2')
=> true
[30] pry(main)> Rails.gem_version < Gem::Version.new('5.2.2')
=> true

The provided regular expression is using multiline anchors (^ or $), which may present a security risk

I am using Ruby 2.3.0 and Rails 4.2.5 and I am follow this document https://www.sitepoint.com/youtube-rails/
Since here there is regular expression are used to validate a youtube url. I am copy paste it in my rails application its working fine on rails console
[1] pry(main)> YT_LINK_FORMAT = /^(?:https?:\/\/)?(?:www\.)?youtu(?:\.be|be\.com)\/(?:watch\?v=)?([\w-]{10,})/
=> /^(?:https?:\/\/)?(?:www\.)?youtu(?:\.be|be\.com)\/(?:watch\?v=)?([\w-]{10,})/
[2] pry(main)> video_url = "https://www.youtube.com/watch?v=aZngT1Eas4w"
=> "https://www.youtube.com/watch?v=aZngT1Eas4w"
[3] pry(main)> uid = video_url.match(YT_LINK_FORMAT)
=> #<MatchData "https://www.youtube.com/watch?v=aZngT1Eas4w" 1:"aZngT1Eas4w">
[4] pry(main)> uid[2]
=> nil
[5] pry(main)> uid[1]
=> "aZngT1Eas4w"
[6] pry(main)> video_url = "https://youtu.be/aZngT1Eas4w"
=> "https://youtu.be/aZngT1Eas4w"
[7] pry(main)> uid = video_url.match(YT_LINK_FORMAT)
=> #<MatchData "https://youtu.be/aZngT1Eas4w" 1:"aZngT1Eas4w">
[8] pry(main)> uid[1]
=> "aZngT1Eas4w"
But when I run my rails application I got this error
"The provided regular expression is using multiline anchors (^ or $), which may present a security risk. Did you mean to use \A and \z, or forgot to add the :multiline => true option?"
I am also try to use this regular expression
/^(?:https?:\/\/)?(?:www\.)?youtu(?:\.be|be\.com)\/(?:watch\?v=)?([\w-]{10,})/
=> /^(?:https?:\/\/)?(?:www\.)?youtu(?:\.be|be\.com)\/(?:watch\?v=)?([\w-]{10,})/
But the same problem is still there
This regular expression is working for rails 5
YT_LINK_FORMAT = /(http:\/\/|https:\/\/|)(www.)?(youtu(be\.com|\.be|be\.com))\/(video\/|embed\/|watch\?v=|v\/)?([A-Za-z0-9._%-]*)(\&\S+)?/

ObjectSpace.each_object(Foo).count

I'am trying to figure out ObjectSpace.each_object
In console:
class Foo; end
Foo.new
ObjectSpace.each_object(Foo).count
=> 1
GC.start
ObjectSpace.each_object(Foo).count
=> 1
I've seen examples and I know that the second count should be 0.
Any ideas what is going on here?
Thanks.
It depends on your console.
IRB
The last result is saved as _, even if it hasn't been explicitely assigned.
Running GC.start won't remove the last object :
irb(main):001:0> class Foo; end
=> nil
irb(main):002:0>
irb(main):003:0* Foo.new
=> #<Foo:0x007fca7a309f98>
irb(main):004:0> p ObjectSpace.each_object(Foo).count; GC.start; p ObjectSpace.each_object(Foo).count
1
1
=> 1
irb(main):005:0> p ObjectSpace.each_object(Foo).count; GC.start; p ObjectSpace.each_object(Foo).count
1
0
=> 0
Pry
You can access the last result and the second to last result with _ and __ :
[1] pry(main)> 'a'
=> "a"
[2] pry(main)> 'b'
=> "b"
[3] pry(main)> p _, __
"b"
"a"
=> ["b", "a"]
Pry saves all the 100 last results in _out_ Pry::HistoryArray:
[1] pry(main)> class Foo; end
=> nil
[2] pry(main)> Foo.new
=> #<Foo:0x007fd093102118>
[3] pry(main)> ObjectSpace.each_object(Foo).count
=> 1
[4] pry(main)> GC.start
=> nil
[5] pry(main)> ObjectSpace.each_object(Foo).count
=> 1
[6] pry(main)> _out_[2]
=> #<Foo:0x007fd093102118>
You can use _out_.pop! to remove its last element :
[1] pry(main)> class Foo; end
=> nil
[2] pry(main)> Foo.new
=> #<Foo:0x007fa90b1ad360>
[3] pry(main)> ObjectSpace.each_object(Foo).count
=> 1
[4] pry(main)> GC.start
=> nil
[5] pry(main)> ObjectSpace.each_object(Foo).count
=> 1
[6] pry(main)> 5.times{_out_.pop!}
=> 5
[7] pry(main)> GC.start
=> nil
[8] pry(main)> ObjectSpace.each_object(Foo).count
=> 0
Inside a script
If you execute :
class Foo; end
Foo.new
p ObjectSpace.each_object(Foo).count
GC.start
p ObjectSpace.each_object(Foo).count
inside a script, you get :
1
0
GC.start does not force the garbage collector to start.
It is slightly unclear from the documentation, but it just instructs the engine to schedule a garbage collection. That said, one can not rely on GC.start would immediately remove objects from the heap.

Rails serialization of Range of integers is broken

I need to serialize ruby Ranges using YAML, in a rails context.
I wanted to check if ranges of integers and ranges of strings were serialized properly.
Here was my test:
# classic irb
require 'yaml' # => true
YAML::VERSION # => "0.60"
YAML.dump(1..2) # => "--- !ruby/range \nbegin: 1\nend: 2\nexcl: false\n"
YAML.dump("1".."2") # => "--- !ruby/range \nbegin: \"1\"\nend: \"2\"\nexcl: false\n"
The two outputs are dutifully distinct, so I got forward and coded it inside my rails application.
However it seems that within a rails context, ruby forgets how to properly serialize a range of integers!
# ./script/rails console
Rails::VERSION::STRING # => "3.0.15"
RUBY_VERSION # => "1.8.7"
YAML::VERSION # => "0.60"
YAML.dump(1..2) # => "--- !ruby/range\n begin: 1\n end: 2\n excl: false"
YAML.dump("1".."2") # => "--- !ruby/range\n begin: 1\n end: 2\n excl: false"
# The two outputs are identical, the distinction between integers and strings is lost!
Both ruby and ruby on rails seem to use the same version of the YAML library.
If I don't get it wrong, my version of ruby doesn't support switching between multiple coder engines.
I have a few questions:
What is the cause of this difference?
Does this problem arise with newer versions of ruby / rails?
How could I fix that properly, in a compatible manner?
Thank you for your help.
A range is a Ruby internal, not a YAML base type like an integer or string. Rather than encode the range as you are, use its start and end points and reconstruct the range on the receiving end.
I use something like:
[1] (pry) main: 0> range = 0..1
=> 0..1
[2] (pry) main: 0> require 'yaml'
=> true
[3] (pry) main: 0> YAML.dump(range)
=> "--- !ruby/range\nbegin: 0\nend: 1\nexcl: false\n"
[4] (pry) main: 0> YAML.dump({'min' => range.min, 'max' => range.max})
=> "---\nmin: 0\nmax: 1\n"
And then I can recreate the range on the receiving side using something like:
Range.new(*YAML.load(YAML.dump({'min' => range.min, 'max' => range.max})).values)
=> 0..1
or this if you're not sure that 'min' and 'max' will be in the right order:
[19] (pry) main: 0> Range.new(*YAML.load(YAML.dump({'min' => range.min, 'max' => range.max})).values_at('min', 'max'))
=> 0..1
Adding some information regarding Ruby 1.9.3+ serializing ranges of characters:
[2] (pry) main: 0> range = '0'..'1'
=> "0".."1"
[3] (pry) main: 0> YAML.dump(range)
=> "--- !ruby/range\nbegin: '0'\nend: '1'\nexcl: false\n"
[5] (pry) main: 0> RUBY_VERSION
=> "1.9.3"
And again with 1.9.2+:
[2] (pry) main: 0> range = '0'..'1'
=> "0".."1"
[3] (pry) main: 0> YAML.dump(range)
=> "--- !ruby/range \nbegin: \"0\"\nend: \"1\"\nexcl: false\n"
[4] (pry) main: 0> RUBY_VERSION
=> "1.9.2"
And, the workaround maintains the range start/end types:
[6] (pry) main: 0> Range.new(*YAML.load(YAML.dump({'min' => range.min, 'max' => range.max})).values_at('min', 'max'))
=> "0".."1"
In both cases the YAML_VERSION is 0.60.

Resources