Switching between two boolean values in Ruby - ruby-on-rails

I have the following code in a controller action, which looks at a user, and changes a boolean value to the opposite value, so if the user is true then it becomes false, and vice versa:
if current_user.enable_access
current_user.update_attribute(:enable_access, false)
else
current_user.update_attribute(:enable_access, true)
end
Is there a neater way of writing this?

How about using the toggle method that was specifically intended for this?
current_user.toggle(:enable_access)
If you want to add persistence in one character, there's also the toggle! method.
current_user.toggle!(:enable_access)

In one line, if current_user.enable_access can be only true`false`:
current_user.update_attribute(:enable_access, !current_user.enable_access)

Here's something to meditate on:
false # => false
true # => true
!false # => true
!true # => false
foo = false # => false
!foo # => true
foo = !foo # => true
foo = nil # => nil
!foo # => true
foo = !nil # => true
foo = !false # => true
Notice !!, which is a convenient way to turn a value into a true/false:
foo = !!nil # => false
foo = !!false # => false
foo = 1 # => 1
!foo # => false
!!foo # => true
foo = 'a' # => "a"
!foo # => false
!!foo # => true
0 == 1 # => false
1 == 1 # => true
'a' == '' # => false
'a' == 'a' # => true
These are the building blocks for comparisons in Ruby.

While the answer by #Зеленый is absolutely correct I wondered, that there is no DRY way to accomplish such a silly task.
The problem is that true and false in Ruby are instances of different classes, TrueClass and FalseClass respectively. That said, one can not just switch the boolean value inplace.
But what we can imagine, mediating at update_attribute source? Probably this is a shortest way to accomplish your task (please, do not use it at home, it’s a joke after all.)
current_user.tap do |cu|
cu.enable_access ^= true
end.save validate: false
I just reinvented toggle!, thanks #Dremni for pointing this out.

current_user.toggle!(:enable_access)
http://api.rubyonrails.org/classes/ActiveRecord/Persistence.html#method-i-toggle-21

I believe the DRY way to accomplish it would be to use:
current_user.enable_access = !current_user.enable_access
Then you could just write a method on a model and call it from any controller.
user.rb
def enable_user_access
self.enable_access = !self.enable_access
end
then calling it from a controller
current_user.enable_user_access

Related

Generic method to set attributes

In my model I have attributes: is_a, is_b and is_c. By default all are null.
I need APIs to set them. These attributes can be set as strictly one or in group. If I am to write APIs, I will be doing following in my model:
def set_as_a # strictly a
self.update_attributes!(:is_a => true, :is_b => false, :is_c => false)
end
def set_as_b # strictly b
self.update_attributes!(:is_a => false, :is_b => true, :is_c => false)
end
... # strictly c
def set_as_a_and_b # a and b
self.update_attributes!(:is_a => true, :is_b => true, :is_c => false)
end
..... # so on
While this works, it does not look elegant. Also if in future if the set has more than 3 attributes, it will result more repetitive code. What is the correct elegant way to achieve this?
class SettableAsABC
ATTRS = [:a, :b, :c]
METHOD_RE = /^set_as_([[:alnum:]]+?(?:_and_[[:alnum:]]+?)*)$/
def method_missing(name, *args)
if name.to_s =~ METHOD_RE
trues = $1.split('_and_').map(&:to_sym)
attrs = Hash[ATTRS.map { |a| ["is_#{a}".to_sym, trues.include?(a)] }]
update_attributes(attrs)
else
super
end
end
def respond_to_missing?(name, include_private = false)
!!(name =~ METHOD_RE) || super
end
end
a = SettableAsABC.new
a.set_as_a_and_c
No defining 2^N methods, just plain Ruby metaprogramming.
EDIT: Good point, #Stefan.
EDIT2: My previous edit introduced a bug. Fixed now.
EDIT3: TIL about respond_to_missing?
I might be misunderstanding something, but why not just write a single method that takes params?:
def set_attributes(opts = {})
update_attributes!(opts) unless opts.none?
end
# usage
set_attributes(is_a: false, is_b: true)
EDIT
To dynamically create methods for combinations of your attributes here is what I came up with:
attributes = %w(a b c d)
(1..attributes.size).flat_map { |size| attributes.combination(size).to_a }.each do |methods|
define_method "set_as_#{methods.join('_and_')}" do
update_attributes!(Hash[methods.map { |v| ["is_#{v}", true] }])
end
end
It will generate the following menthods:
set_as_a
set_as_b
set_as_c
set_as_d
set_as_a_and_b
set_as_a_and_c
set_as_a_and_d
set_as_b_and_c
set_as_b_and_d
set_as_c_and_d
set_as_a_and_b_and_c
set_as_a_and_b_and_d
set_as_a_and_c_and_d
set_as_b_and_c_and_d
set_as_a_and_b_and_c_and_d
How about this?
def set_true(true_fields=[])
attr_hash = {}
true_fields.each { |field| attr_hash[field] = true }
update_attributes(hash)
end
Hope that helps!

Is there a better Ruby or Rails idiom for checking for the presence of values in a nested hash?

If the value:
myhash['first_key']['second_key']
exists, then I need to get it. But 'second_key' may not be present at all in my_hash, and I don't want that line to throw an exception if it is not.
Right now I am wrapping the whole thing in an ugly conditional like so:
if myhash['first_key'].present? and myhash['first_key']['second_key'].present?
...
end
I'm sure there must be something simpler.
You can always use try:
hsh.try(:[], 'first_key').try(:[], 'second_key')
FYI: if you're doing a lot of these checks, you might want to refactor your code to avoid these situations.
When in doubt, write a wrapper:
h = {
first_key: {
second_key: 'test'
}
}
class Hash
def fetch_path(*parts)
parts.reduce(self) do |memo, key|
memo[key] if memo
end
end
end
h.fetch_path(:first_key, :second_key) # => "test"
h.fetch_path(:first_key, :third_key) # => nil
h.fetch_path(:first_key, :third_key, :fourth_key) # => nil
h.fetch_path(:foo, :third_key) # => nil
Try this neat and clean solution. Hash default values:
h = Hash.new( {} ) # sets a hash as default value
Now do what you like:
h[:some_key] # => {}
h[:non_existent_key][:yet_another_non_existent_key] # => nil
Nice?
Say you have an existing hash, which is already populated:
h = { a: 1, b: 2, c: 3 }
So you just set its default to return a new hash:
h.default = {}
And there you go again:
h[:d] # => {}
h[:d][:e] # => nil
I'd point you to the excellent Hashie::Mash
An example:
mash = Hashie::Mash.new
# Note: You used to be able to do : `mash.hello.world` and that would return `nil`
# However it seems that behavior has changed and now you need to use a `!` :
mash.hello!.world # => nil # Note use of `!`
mash.hello!.world = 'Nice' # Multi-level assignment!
mash.hello.world # => "Nice"
# or
mash.hello!.world # => "Nice"
You could set up some default values before processing the hash. Something like:
myhash[:first_key] ||= {}
if myhash[:first_key][:second_key]
# do work
end
Why not define a method for this?
class Hash
def has_second_key?(k1,k2)
self[k1] ? self[k1][k2] : nil
end
end
new_hash = {}
new_hash["a"] = "b"
new_hash["c"] = {"d"=>"e","f"=>"g"}
new_hash[:p] = {q:"r"}
new_hash.has_second_key?("r","p")
# =>nil
new_hash.has_second_key?("c","f")
# =>"g"
new_hash.hash_second_key?(:p,:q)
# =>"r"
To modify your code, it would be:
if myhash.has_second_key?('first-key','second-key')
...
end
This method will return nil, which is Falsey in Ruby, or will return the value of the second key which is Truthy in Ruby.
Obviously you do not have to modify the Hash class if you don't want to. You could have the method except the hash as an argument too. has_second_key?(hash,k1,k2). Then call it as:
has_second_key?(myhash,'first-key','second-key')

access the former value of an attribute in ruby

Lets say I have a model (an ActiveRecord class):
class Sample < ActiveRecord::Base
attr_accessor :x1
end
I know that
Sample.last.x1 == 1 #true
If I set Sample.last.x1 = 3 then Sample.last.x1_was == 1 #true.
But when I set the value of x1 again: Sample.last.x1 = 8 then Sample.last.x1_was == 3 #false, but Sample.last.x1 == 1 #true
I can guess why it happens (Sample.last wasn't saved since the change), but I want to find a way to retrieve the former value (not the db value) of x1. Can you suggest a way to do it?
I can't think of a reason to do that, but if you really need to, you could override the setter to store the various changes as you go.
def x1=( value )
#previous_x1_value = x1
super
end
def previous_x1_value
#previous_x1_value || x1_was
end
IT's all built in to rails. See the documentation for [ActiveRecord#dirty][1]
person.name = 'Bob'
person.changed? # => true
person.name_changed? # => true
person.name_was # => 'uncle bob'
person.name_change # => ['uncle bob', 'Bob']
person.name = 'Bill'
person.name_change # => ['uncle bob', 'Bill']

How to compare two Hashes so to return true if both Hashes have same keys/values?

I am using Ruby on Rails 3.2.2 and Ruby 1.9.3. I would like to compare two Hashes (A and B) so to return true if a Hash (A) include all keys/values of the other Hash (B).
For example, given I have
params.inspect
# => { "action"=>"...", "controller"=>"...", "key_param1"=>"value_param1", , "key_param2"=>"value_param2", "key_param3"=>"value_param3", ... }
my_hash1.inspect
# => { "key_param1"=>"value_param1", "key_param2"=>"value_param2" }
my_hash2.inspect
# => { "key_param4"=>"value_param4", "key_param1"=>"value_param1" }
my_hash3.inspect
# => {}
Then I am looking for a method (or something like that) in order to make
params.has_same_keys_and_values_as?(my_hash1)
# => true
params.has_same_keys_and_values_as?(my_hash2)
# => false
params.has_same_keys_and_values_as?(my_hash3)
# => true
Assuming that Hash#keys and Hash#values return values in the same order:
params.values_at(*my_hash.keys) == my_hash.values
I think you can use:
a.slice(*b.keys) == b
where a and b are your hashes. note that slice is a rails method and not ruby.
in plain ruby you can write:
a.keep_if{|k, v| b[k]} == b
class Hash
def >=(b)
eq = true
b.each { |k, v| eq &= !(self.include? k) ? false : ( ( ((self[k]&&v).is_a? Hash) && !((v||self[k]).empty?) ) ? self[k]>=v : true)}
return eq
end
end
params = { "action"=>"...", "controller"=>"...", "key_param1"=>"value_param1", "key_param2"=>"value_param2", "key_param3"=>"value_param3" }
my_hash1 = { "key_param1"=>"value_param1", "key_param2"=>"value_param2" }
my_hash2 = { "key_param4"=>"value_param4", "key_param1"=>"value_param1" }
my_hash3 = {}
p params >= my_hash1 #true
p params >= my_hash2 #false
p params >= my_hash3 #true
It'll work with "deep" hashes as well:
b = {1 => {2 => {} }, 4 => {} }
a = {1 => {2 => {3 => {} }}, 4 => {}, 5 => "123" }
p a >= b #true
p b >= a #false
P.S.
Whether one hash includes another hash
EDIT: This is assuming that the values/keys are not in the same order in both hashes.
You could iterate over each key in hash1 and use has_key? on hash2. Keep in mind this is naive solution that could be slow for large datasets.
Checkout has_key? and has_value? here: http://www.ruby-doc.org/core-1.9.3/Hash.html#method-i-has_key-3F
You could loop as follows:
hash1.each_key { |key|
if hash2.has_key?(key)
do whatever
endif
}
better way, there's an active support method for this, hash.diff, wrap it with .empty? to check if they are the same
{:one => 1}.diff({:one => 1}).empty?
=> true
{:one => 1}.diff({:one => 2}).empty?
=> false
http://as.rubyonrails.org/classes/ActiveSupport/CoreExtensions/Hash/Diff.html

Could you explain what's !! do? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
What does !! mean in ruby?
I found !! in Paypal gem here: https://github.com/tc/paypal_adaptive/blob/master/lib/paypal_adaptive/config.rb
like 59
but I don't understand what it does.
I know that ! means NOT, but !! doesn't make sense.
here's the screen: http://tinyurl.com/7acklhr
It forces any value to true or false depending on its "truthy" nature.
This is simply because, as you've noted, ! is the Boolean-not operator. For instance:
t = 1
puts !t # => false
puts !!t # => true
f = nil
puts !f # => true
puts !!f # => false
The !! is used to return either true or false on something that returns anything :
In Ruby, everything other than nil and false is interpreted as true. But it will not return true, it will return the value.
So if you use !, you get true or false but the opposite value of what is really is.
If you use !!, you get the true or false corresponding value.
It is used to make sure its the boolean type.
Explanation more detailed
Eg:
!!active
=> true
active = false
=> false
!!active
=> false
active = nil
=> nil
!!active
=> false
This forces a result to be true or false. As in ruby nil is not exactly false this can be useful. For instance:
def try x
if x == 1
return nil
else
return "non-nil"
end
end
p "try1" if try(1) # here you get a string printed
p "try2" if !!try(1) # here you don't

Resources