How to update a specific field in Mongodb knowing it's name? - ruby-on-rails

I have the following code
def update_field(db_entity, field_name)
db_entity_value = db_entity.attributes[field_name]
if db_entity_value == false
db_entity.update(field_name: true) # db_entity.update(person_name: true) works!
end
end
it takes two vars: db_entity and field_name(it's a string value like "age" or "weight"). I want to update the value of field_name.
I can get its value by using db_entity.attributes[field_name] and then I want to set the value to true if it was false, but I don't know how to update it because field_name it's a variable which holds a field name that I want to update. Please, help.

def update_field(db_entity, field_name)
db_entity_value = db_entity.attributes[field_name]
if db_entity_value == false
db_entity.update(field_name => true)
end
end
In Ruby when you construct hashes with the hashrocket (=>) syntax the keys can be variables.
You can also use update_attribute(name, value).

Related

self.boolean_claas_value instance of a model doesnt store a value if its boolean

I'm working on a Rails project and I'm having trouble storing a boolean value. Works fine if its a string.
Here is a shorter version of the model
require 'active_record'
# Public: Class representing data
class RandomClass
include ActiveModel::Serializers::JSON
# Public: Returns boolean hopefully
attr_accessor :why_does_this_not_work
def initialize(values = {})
#why_does_this_not_work = values[:why_does_this_not_work]
end
def update_with(new_data)
self.why_does_this_not_work = new_data.why_does_this_not_work || why_does_this_not_work
end
When the value why_does_this_not_work is initialized it is set to a boolean but when the update_with is called with new_data although the value exists the assignment doesn't work.
self.why_does_this_not_work = true gets stored but when I do self.why_does_this_not_work = new_data.why_does_this_not_work || why_does_this_not_work or self.why_does_this_not_work = new_data.why_does_this_not_work the value doesn't get stored.
The same with why_does_this_not_work, the value gets stored when its initialized but it doesn't return anything when called.
All of the above works fine if a string is passed instead.
I'm assuming this maybe be something to do with boolean's in ruby. I tried to look at the class and the value comes back as TrueClass or FalseClass
Any help would be appreciated. I'm unsure if adding the controller code would play any role.
Env:
Ruby: 2.6.2,
Rails: 5.2.4.3
Does it not work when new_data.why_does_this_not_work is set to false? The || operator will disregard it if it's false.
false || true # => true
How about you try
def update_with(new_data)
new_value = new_data.why_does_this_not_work.nil? ? why_does_this_not_work : new_data.why_does_this_not_work
self.why_does_this_not_work = new_value
end
This will take whatever the value of new_data unless it's nil. It'll then fallback to the existing value.

Use merge! or deep_merge! to update hash in activerecord model

I have a JSON column in my model that by default is an empty hash.
I want to check if specific keys are present and if not to merge the empty hash with a default hash with the keys.
In my model I am checking if a utility (water, gas, or electric is present) and if not then insert this default hash:
def default_config
{:config => {"features" => {"utilities" => {"water" => true, "gas" => true, "electric" => true}}}}
end
this is how I'm checking for whether a utility key is present:
def water
has_water? || parent.has_water?
end
which in turn calls these methods (all in my model):
def utility(util)
self[:config].try(:fetch, "features", nil).try(:fetch, "utilities", nil).try(:fetch, "#{util}", nil)
end
def has_water?
utility("water") == true
end
This is in order to be able to configure the JSON column whether or not the keys already present, which I'm attempting here:
def set_water(boolean)
new_val = cleaned_boolean(boolean)
water ? nil : self[:config].deep_merge!(default_config)
self[:config]["features"]["utilities"]["water"] = new_val
end
When I test this I'm getting
undefined method `[]=' for nil:NilClass
error when trying to set a utility value indicating that my default_config is not being merged into the existing empty hash.
reverse_merge! is the usual way to set defaults for a Hash in rails.
self[:config].reverse_merge!(default_config)
this is essentially equal to:
default_config.merge!(self[:config])
Leaving everything in self[:config] untouched and just merging in the missing key value pairs from default_config.
Also this ternary expression:
water ? nil : self[:config].deep_merge!(default_config)
is more idiomatically written as (using Hash#reverse_merge!)
self[:config].reverse_merge!(default_config) unless water
and since water returns a boolean value it is generally written as a question e.g. water? (like in has_water?) Not sure if the water method is used frequently but I would refactor as
def has_water?(include_parent=false)
utility("water") == true || (include_parent && parent.has_water?)
end
Then call as:
self[:config].reverse_merge!(default_config) unless has_water?(true)

Ruby custom method to fill hash

I'm working on a method that will allow me to add in a "word" and its "definition", into a hash.
Here's what I have:
class Dictionary
def entries
#entries ||= {}
end
def add word, definition = nil
entries[word] = definition
"#{entries}"
end
end
Note: I want the definition parameter to be optional, hence my initialization to nil. However, for some reason that is showing up in my output.
Example: Passing in "fish" and "aquatic animal":
My output: {{"fish"=>"aquatic animal"}=>nil}
Desired output: {"fish"=>"aquatic animal"}
It seems like the problem is that it's putting both values that I pass to the method into the first key in the hash, and is putting that "nil" value into that key's value. Where am I making an error?
Edit: Adding the relevant RSpec block that is doing the method call so that I can better understand exactly how RSpec is making this call:
describe Dictionary do
before do
#d = Dictionary.new
end
it 'is empty when created' do
#d.entries.should == {}
end
it 'can add whole entries with keyword and definition' do
#d.add('fish' => 'aquatic animal')
#d.entries.should == {'fish' => 'aquatic animal'}
#d.keywords.should == ['fish']
end
Thanks!
If you want to optionally accept a hash entry...
def add word, definition = nil
if word.class == Hash
entries.merge!(word)
else
entries[word] = definition
end
"#{entries}"
end
You don't want to do
#d.add('fish' => 'aquatic animal')
You want to do...
#d.add('fish', 'aquatic animal')
As it is, you're passing a hash as the first argument, second argument is empty.
Your RSpec is wrong.
Change #d.add('fish' => 'aquatic animal') to #d.add('fish', 'aquatic animal')
Your #add method is accepting 2 parameters, with one being optional. With your current code, you're passing in a single hash 'fish' => 'aquatic animal'. Therefor setting word to the hash, and def to nil.

Check string is a valid number or not in ruby

I want to check weather variable contains a valid number or not.
I can validate correctly for null and blank but can not validate text as a "Integer"...
I tried:
if(params[:paramA].blank? || (params[:paramA].is_a?(Integer)) )
I have also tried is_numeric, is_numeric(string), is_number? and other ways...
but did not get success...
I saw such patch:
class String
def is_number?
true if Float(self) rescue false
end
end
if (params[:paramA].blank? || !params[:paramA].is_number?)
Or without the patch:
if (params[:paramA].blank? || (false if Float(params[:paramA]) rescue true))
It supports 12, -12, 12.12, 1e-3 and so on.
If your parameter is for an ActiveRecord model, then you should probably use validates_numericality_of. Otherwise...
You only want integers, right? How about:
if (params[:paramA].blank? || params[:paramA] !~ /^[+-]?\d+$/)
That is, check whether the parameter consists of an optional + or -, followed by 1 or more digits, and nothing else.
If the thing you want to do is this:
I want to check weather variable contains a valid number or not.
You can get it with regex. See it here
s = 'abc123'
if s =~ /[-.0-9]+/ # Calling String's =~ method.
puts "The String #{s} has a number in it."
else
puts "The String #{s} does not have a number in it."
end
In rails you can use the numeric? method on a String or Integer or Float which does exactly what you need.
123.numeric?
# => true
123.45.numeric?
# => true
"123".numeric?
# => true
"123.45".numeric?
# => true
"a1213".numeric?
# => false
UPDATE
My bad, I had a dirty environment, the above works if mongoid version 3 and above is loaded.

How do I submit a boolean parameter in Rails?

I'm submitting a parameter show_all with the value true. This value isn't associated with a model.
My controller is assigning this parameter to an instance variable:
#show_all = params[:show_all]
However, #show_all.is_a? String, and if #show_all == true always fails.
What values does Rails parse as booleans? How can I explicitly specify that my parameter is a boolean, and not a string?
UPDATE: Rails 5:
ActiveRecord::Type::Boolean.new.deserialize('0')
UPDATE: Rails 4.2 has public API for this:
ActiveRecord::Type::Boolean.new.type_cast_from_user("0") # false
PREVIOUS ANSWER:
ActiveRecord maintains a list of representations for true/false in https://github.com/rails/rails/blob/master/activerecord/lib/active_record/connection_adapters/column.rb
2.0.0-p247 :005 > ActiveRecord::ConnectionAdapters::Column.value_to_boolean("ON")
2.0.0-p247 :006 > ActiveRecord::ConnectionAdapters::Column.value_to_boolean("F")
This is not part of Rails' public API, so I wrapped it into a helper method:
class ApplicationController < ActionController::Base
private
def parse_boolean(value)
ActiveRecord::ConnectionAdapters::Column.value_to_boolean(value)
end
end
and added a basic test:
class ApplicationControllerTest < ActionController::TestCase
test "parses boolean params" do
refute ApplicationController.new.send(:parse_boolean, "OFF")
assert ApplicationController.new.send(:parse_boolean, "T")
end
end
I wanted to comment on zetetic answer but as I can't do that yet I'll post this as an answer.
If you use
#show_all = params[:show_all] == "1"
then you can drop ? true : false because params[:show_all] == "1" statement itself will evaluate to true or false and thus ternary operator is not needed.
This question is rather old, but since I came across this issue a couple of times, and didn't like any of the solutions proposed, I hacked something myself which allows to use multiple strings for true such as 'yes', 'on', 't' and the opposite for false.
Monkey patch the class String, and add a method to convert them to boolean, and put this file in /config/initializers as suggested here: Monkey Patching in Rails 3
class String
def to_bool
return true if ['true', '1', 'yes', 'on', 't'].include? self
return false if ['false', '0', 'no', 'off', 'f'].include? self
return nil
end
end
Notice that if the value is none of the valid ones either for true or false, then it returns nil. It's not the same to search for ?paid=false (return all records not paid) than ?paid= (I don't specify if it has to be paid or not -- so discard this).
Then, following this example, the logic in your controller would look like this:
Something.where(:paid => params[:paid].to_bool) unless params[:paid].try(:to_bool).nil?
It's pretty neat, and helps to keep controllers/models clean.
#show_all = params[:show_all] == "1" ? true : false
This should work nicely if you're passing the value in from a checkbox -- a missing key in a hash generates nil, which evaluates to false in a conditional.
EDIT
As pointed out here, the ternary operator is not necessary, so this can just be:
#show_all = params[:show_all] == "1"
You could change your equality statement to:
#show_all == "true"
If you want it to be a boolean you could create a method on the string class to convert a string to a boolean.
I think the simplest solution is to test "boolean" parameters against their String representation.
#show_all = params[:show_all]
if #show_all.to_s == "true"
# do stuff
end
Regardless of whether Rails delivers the parameter as the String "true" or "false" or an actual TrueClass or FalseClass, this test will always work.
You could just do
#show_all = params[:show_all].downcase == 'true'
It's worth noting that if you're passing down a value to an ActiveModel in Rails > 5.2, the simpler solution is to use attribute,
class Model
include ActiveModel::Attributes
attribute :show_all, :boolean
end
Model.new(show_all: '0').show_all # => false
As can be seen here.
Before 5.2 I use:
class Model
include ActiveModel::Attributes
attribute_reader :show_all
def show_all=(value)
#show_all = ActiveModel::Type::Boolean.new.cast(value)
end
end
Model.new(show_all: '0').show_all # => false
Another approach is to pass only the key without a value. Although using ActiveRecord::Type::Boolean.new.type_cast_from_user(value) is pretty neat, there might be a situation when assigning a value to the param key is redundant.
Consider the following:
On my products index view by default I want to show only scoped collection of products (e.g. those that are in the stock). That is if I want to return all the products, I may send myapp.com/products?show_all=true and typecast the show_all parameter for a boolean value.
However the opposite option - myapp.com/products?show_all=false just makes no sense since it will return the same product collection as myapp.com/products would have returned.
An alternative:
if I want to return the whole unscoped collection, then I send myapp.com/products?all and in my controller define
private
def show_all?
params.key?(:all)
end
If the key is present in params, then regardless of its value, I will know that I need to return all products, no need to typecast value.
You can add the following to your model:
def show_all= value
#show_all = ActiveRecord::ConnectionAdapters::Column.value_to_boolean(value)
end
You could convert all your boolean params to real booleans like this:
%w(show_all, show_featured).each do |bool_param|
params[bool_param.to_sym] = params[bool_param.to_sym] == "true"
end
In this solution, nil parameters would become false.
While not explicitly what the question is about I feel this is appropriately related; If you're trying to pass true boolean variables in a rails test then you're going to want the following syntax.
post :update, params: { id: user.id }, body: { attribute: true }.to_json, as: :json
I arrived at this thread looking for exactly this syntax, so I hope it helps someone looking for this as well. Credit to Lukom

Resources