Working with Boolean fields on Mongoid - ruby-on-rails

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!!

Related

How to deal with boolean value from form submit?

we use checkbox to submit a boolean data in a form.
In rails, when submit the form, a string with "1" or "0" will be submitted to the controller.
In phoenix, when submit the form, a string with "true" or "false" will be submitted to the controller.
It's fine if we directly create object in the database. Either of value will be store as boolean correctly.
But if we need to use the boolean value in our logic, what's the best way to do it?
Convert to boolean:
# Ruby code
def create
admin = ActiveModel::Type::Boolean.new.cast(param[:admin])
if admin
....
end
end
Directly use as string:
# Elixir code
def create(conn, params) do
case params[:admin] do
"true" -> do something
_ -> do others
end
end
Other better ways?
String.to_existing_atom/1 is your friend.
Both true and false are atoms, namely a syntactic sugar for :true and :false. That is because in erlang they are atoms.
:true == true
#⇒ true
:false == false
#⇒ true
String.to_existing_atom "true"
#⇒ true

Rails convert string data received from front-end to Boolean in the backend

I have a drop down menu from front end whose value are"true" and
"false" as a string respectively.
{label: 'No', value: 'false'},
{label: 'Yes', value: 'true'}
They will be insert into a column type with Boolean type.
As you can see, it may be problematic because the type mismatch.
The error i got in the console:
POST http://localhost:3000/api/my_forms.json 422 (Unprocessable Entity)
So is there a efficient way so that once the data is received,it will l be converted into Boolean
I am using Postgresql for my DB and React for my front-end, if that helps.
if it is effectively passed as a string, you could add a callback in your model to ensure the value is correctly transformed
For example :
class YourModel
before_validation :format_field,
if: proc { |model|
model.your_field.is_a?(String)
}
def format_field
your_field = (your_field == "true")
end
end
Or a before action on your controller :
before_action :format_problematic_field
def format_problematic_field
return unless params[:your_field].is_a?(String)
params[:your_field] = params[:your_field] == "true"
end

Update value of array field using mongoid

I'm trying to find a way to update a single array item using mongoid. I'm using Rails 4 and Mongodb.
My model looks like this
class User
include Mongoid::Document
include Mongoid::Timestamps
field :my_book_list, type: Array, default: []
field :book_name, type: String
I'm able to add entry to the array field using the following code:
User.where(id: self.user_id).add_to_set("my_book_list" => self.book_name)
After I have added data to the array, in the database it looks like this
db.collection.users
{
"_id" : ObjectId("56e09d54a0d00b0528000001"),
"status" : true,
"sign_in_count" : 3,
"my_book_list" :
["Learning Ruby", "MongoDB for Dummies"]
}
What I'm struggling with is to find a Rails / Mongoid way of updating the value of an item in the array by looking for the name.
Simply put, how do I change the value of my_book_list[1] by searching for it through name and not knowing its index. In this case index 1 is "MongoDB for Dummies" and needs to be updated to "MongoDB". So that the "my_book_list" array field looks like this after its updated:
db.collection.users
{
"_id" : ObjectId("56e09d54a0d00b0528000001"),
"status" : true,
"sign_in_count" : 3,
"my_book_list" :
["Learning Ruby", "MongoDB"]
}
How do I achieve this ?
Instead of updating, think of it as adding & removing. You can use pull (https://docs.mongodb.org/ecosystem/tutorial/mongoid-persistence/#atomic)
Where your add to set uniquely adds it to an array, pull removes it based on the name. So assuming this:
user = User.find_by(id: self.user_id)
user.add_to_set(my_book_list: 'First Story')
p user.my_book_list
=> ['First Story']
user.add_to_set(my_book_list: 'Second Story')
p user.my_book_list
=> ['First Story', 'Second Story']
user.add_to_set(my_book_list: 'Third Story')
p user.my_book_list
=> ['First Story', 'Second Story', 'Third Story']
user.pull(my_book_list: 'Second Story')
p user.my_book_list
=> ['First Story', 'Third Story']
If you had duplicates in the set you can use pull_all, but you are using add to set so you won't need to.
This was more of a conceptual problem being a ruby beginner. The answer lies that mongo arrays are simple ruby array and we can use standard ruby methods to update the array.
In this case in my rails console I did this and it worked. The second line finds the array item and replaces with the new wird,
u = User.first
u.my_book_list.map! { |x| x == "MongoDB for Dummies" ? "MongoDB": x}
u.save

Rails - permit a param of unknown type (string, hash, array, or fixnum)

My model has a custom_fields column that serializes an array of hashes. Each of these hashes has a value attribute, which can be a hash, array, string, or fixnum. What could I do to permit this value attribute regardless of its type?
My current permitted params line looks something like:
params.require(:model_name).permit([
:field_one,
:field_two,
custom_fields: [:value]
])
Is there any way I can modify this to accept when value is an unknown type?
What you want can probably be done, but will take some work. Your best bet is this post: http://blog.trackets.com/2013/08/17/strong-parameters-by-example.html
This is not my work, but I have used the technique they outline in an app I wrote. The part you are looking for is at the end:
params = ActionController::Parameters.new(user: { username: "john", data: { foo: "bar" } })
# let's assume we can't do this because the data hash can contain any kind of data
params.require(:user).permit(:username, data: [ :foo ])
# we need to use the power of ruby to do this "by hand"
params.require(:user).permit(:username).tap do |whitelisted|
whitelisted[:data] = params[:user][:data]
end
# Unpermitted parameters: data
# => { "username" => "john", "data" => {"foo"=>"bar"} }
That blog post helped me understand params and I still refer to it when I need to brush up on the details.

Field with possible different types mongoid

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.

Resources