Field with possible different types mongoid - ruby-on-rails

In my model I have
field :is_open, type: Time, default: nil
However previously in this same model, I stored this type as a boolean. I now want to change it to type Time. For new items in my database this works fine, however for old records where it's still stored as a boolean, when I try to access this attribute
modelInstance.is_open
I get the following error:
#<NoMethodError: undefined method `getlocal' for true:TrueClass>
Instead of changing all the booleans in my mongo database to Time objects, is there a way/hook I can do on mongo so that when this attribute is pulled from the document, I can check if it's a boolean and replace it with a timestamp?

You don't have to specify the field type with Mongoid. That means that you can do things like this:
class Model
include Mongoid::Document
field :f
end
with data like this:
> db.models.find()
{ "_id" : ObjectId(...), "f" : true }
{ "_id" : ObjectId(...), "f" : 6 }
{ "_id" : ObjectId(...), "f" : "pancakes" }
And everything will work out just fine:
rails > Model.all.map { |m| puts "#{m.f.class} - #{m.f.inspect}" }
TrueClass - true
Float - 6.0
String - "pancakes"
So you can drop the :type from your field and everything should work okay. Of course, you might want to provide your own def is_open and def is_open= methods if you need to do some sort of manual type conversion or checking while you're waiting to fix up your MongoDB data.

Related

Find model based on jsonb nested data field

I use Ruby on Rails and Postgres 9.5.
In the database I have a table called events.
I want to find all events based on particular value in the data field.
events.data has the following possible json value:
{ 'transition' => { 'from' => 'bacon', 'to' => 'ham' } }
How can I build a query that will find an event with data => transition => from bacon?
Assuming that your model is called Event, you can do it like this:
Event.where("data -> 'transition' ->> ? = ?", 'from', 'bacon')
Here is jsonb operators reference.
This query will return all events where data.transition.from is equal to bacon.
To DRY your queries, you can add it to the repository, e.g.:
# app/repositories/event_repository.rb
module EventRepository
extend ActiveSupport::Concern
included do
scope :where_transition, ->(field, value) { where("data -> 'transition' ->> ? = ?", field, value) }
end
end
After that include it in your model:
include EventRepository
Then you can use it like this:
Event.where_transition('from', 'bacon')
Event.where_transition('to', 'ham')

How to globally override Mongoid::Document._id generation

I wanna override the kind of _id generation in mongoid (another app, which shares the db uses String instead of ObjectId()
I can do it for every model by adding this:
field :_id, type: String, default: -> { BSON::ObjectId.new.to_s }
But how to globally attach this to keep it DRY?
Very valid usecase but looking into the code you might be out of luck at Mongoid::Fields, but you could overwrite mongoize which should go like this
Item.new.id
=> BSON::ObjectId('56e727892ada693ea8000000')
class BSON::ObjectId
def self.mongoize(k)
k.to_s
end
end
Item.new.id
=> "56e7276f2ada693737000002"

Working with Boolean fields on Mongoid

I create a Model that has a Boolean field, but when catch the value it gave me 1 or 0. I discover that it's because BSON type for Boolean is "\x00" and "\x01".
So my question is, how can I get the "boolean" value of the field? Do I need to do a method on a model or a controller that returns me true if value is 1 or false if 0? Or will Mongoid do this for me?
Mongoid Version: 4.0.0 38de2e9
EDIT
Mongo Shell
db.feedbacks.find().limit(1).pretty()
{
"_id" : ObjectId("52290a2f56de969f8d000001"),
"like" : "1",
...
}
Explain:
I create a app with scaffold:
rails g scaffold Feedback like:Boolean
When I insert a new record, in Mongo the Document stay as I sad.
When I do Feedback.first, the field like in Model has the "0" or "1" value.
class Feedback
include Mongoid::Document
include Mongoid::Timestamps
field :comment, type: String
field :like, type: Boolean
def isLike?
like=="1"
end
end
This is the repo:
https://github.com/afucher/4kFeedback/blob/master/app/models/feedback.rb
Mongoid handles that in a transparent manner if you use the Boolean type. Checkout the documentation.
EDIT :
From the rails console (in an app with an Indicator model defining a field global of type Boolean) :
Indicator.first.global?
# => true
Indicator.first.global?.class
# => TrueClass
The equivalent from the mongo shell :
> db.indicators.find().limit(1).pretty()
{
"_id" : ObjectId("52319eeb56c02cc74200009c"),
...
"global" : true,
...
}
EDIT
The spec for the Boolean extension clearly shows that for any of true, "true", "t", "yes", "y", 1, 1.0 on the MongoDB side you'll get a TrueClass instance. Same for false.
I can resolve my problem, reading the check_box documentation:
http://apidock.com/rails/ActionView/Helpers/FormHelper/check_box
The default value of Check Box is "0" or "1". To change this value, is just pass the values that you want to the tag:
check_box(object_name, method, options = {}, checked_value = "1", unchecked_value = "0")
So, I change to this:
<%= f.check_box :like,{}, "true", "false" %>
Thanks Jef for help me!!

Mongoid update attributes : how not to insert when field names don't already exist in the collection

I'm trying to do update_attributes with Mongoid in a Rails 3 application
The problem I'm running into is this:
Let's say my collection has the following fields:
{"Name" : "foo", "email" : "bar" }
Here's the scenario
if I do this:
#person = Person.where(:Name => "foo", :_id = "some_id")
and then I do this:
#person.update_attributes(:Name => "baba-fooka", :Last_Name => "baba-bara")
The line of code above updates the record in mongodb, but also adds a new field to the document.
How can I use the update_attributes method with a validation which doesn't allow inserting fields which don't already exist
It sounds like what you want is allow_dynamic_fields set to false in the mongoid config file. Dynamic fields is on by default which allows attributes to get set and persisted on the document even if a field was not defined for them.
Go into config/mongoid.yml under options set allow_dynamic_fields: false. It should already be there but commented out and set to true. Make sure it says false.

Why does Model.new in Rails 3 do an implicit conversion?

I'm facing a weird behavior in Rails 3 model instantiation.
So, I have a simple model :
class MyModel < ActiveRecord::Base
validates_format_of :val, :with => /^\d+$/, :message => 'Must be an integer value.'
end
Then a simple controller :
def create
#mod = MyModel.new(params[:my_model])
if #mod.save
...
end
end
first, params[:my_model].inspect returns :
{:val => 'coucou :)'}
But after calling #mod = MyModel.new(params[:my_model]) ...
Now, if I call #mod.val.inspect I will get :
0
Why am I not getting the original string ?
At the end the validates succeed because val is indeed an integer.
Is this because val is defined as an integer in the database ?
How do I avoid this behavior and let the validation do his job ?
If val is defined as an integer in your schema then calling #my_model.val will always return an integer because AR does typecasting. That's not new to rails 3, it's always worked that way. If you want the original string value assigned in the controller, try #my_model.val_before_type_cast. Note that validates_format_of performs its validation on this pre-typecast value, so you don't need to specify that there.
EDIT
Sorry I was wrong about the "performs its validation on this pre-typecast value" part. Looking at the code of the validation, it calls .to_s on the post-typecast value which in your case returns "0" and therefore passes validation.
I'd suggest not bothering with this validation to be honest. If 0 is not a valid value for this column then validate that directly, otherwise just rely on the typecasting. If the user enters 123 foo you'll end up with 123 in the database which is usually just fine.
There is also better fitting validator for your case:
http://guides.rubyonrails.org/active_record_validations_callbacks.html#validates_numericality_of

Resources