I'm writing some code to evaluate the presence of a bunch of strings, and I want to ensure that only 1 is present. They're mutually exclusive.
class MyClass
include ActiveModel::Validations
attr_accessor :a, :b, :c
validate :mutex_values
def initialize(attr = {})
attr.each do |k, v|
send("#{k}=", v)
end
end
private
def mutex_values
# here, I want to do this:
# errors.add(:base, "specify only 1") unless a ^ b ^ c
# instead I do this
errors.add(:base, "specify only 1") unless a.present? ^ b.present? ^ c.present?
end
end
MyClass.new(:a => "A", :b => "B", :c => "C").valid?
=> false
Is there another way that doesn't require repetitive use of the .present?? Monkey patch String to define an operator ^? I'm just so used to being able to do if a that it feels unnatural to need what is basically an explicit cast to boolean. I feel like this use case would 'just work' in Ruby.
Enumerable actually defines a one? method which does exactly what you need.
# So either...
[a, b, c].one?(&:present?)
# or
[a, b, c].one?(&:presence)
# ... would do the trick in this case.
Unfortunately if you want two? or etcera, you're out of luck.
You could do something like this with count:
errors.add(:base 'specify exactly 1') unless [a, b, c].count(&:present?) == 1
If you want at most one instead of exactly one, then:
errors.add(:base 'specify at most 1') unless [a, b, c].count(&:present?) > 1
For example:
> ['',nil,'6',''].count(&:present?)
=> 1
> ['',nil,'6',11].count(&:present?)
=> 2
> ['',nil,''].count(&:present?)
=> 0
If you want to check whether exactly one of methods a, b, and c returns non-null value, you can use:
[a, b, c].compact.count == 1
or even, thanks to numbers,
[a, b, c].one?
Related
Having a hashmap, such as:
{:foo => 1, :bar => 2}
in Ruby, is there an easy way to assign those values as properties of the object, to automatically cause this to happen:
obj.foo = 1
obj.bar = 2
To be precise, some Ruby-idiomatic way of doing:
hashmap.each { |k,v| obj.send("#{k}=", v) }
obj is an object that doesn't inherit ActiveModel and it's not a Struct and I can't control it's type as it's coming from a third party library.
I'm using Rails, so if the answer comes from Rails, that's acceptable.
What you have there is (almost) the most concise, readable, idiomatic solution already:
hashmap.each { |k,v| obj.send("#{k}=", v) }
There is only one thing left to improve:
hashmap.each { |k,v| obj.public_send("#{k}=", v) }
Use public_send instead of send to make it clear to others that you are using it only to pass a method name dynamically and not to circumvent access restrictions.
Maybe you could create an OpenStruct from your hash, do whatever you need with the OpenStruct and its attributes, then convert it back into a hash?
require 'ostruct'
h = {:foo => 1, :bar => 2}
o = OpenStruct.new(h)
o.foo # Output: => 1
o.bar # Output: => 2
# if necessary, convert it back into a hash
h = o.to_h
From the Ruby docs:
An OpenStruct is a data structure, similar to a Hash, that allows the
definition of arbitrary attributes with their accompanying values.
This is accomplished by using Ruby’s metaprogramming to define methods
on the class itself.
An OpenStruct utilizes Ruby’s method lookup structure to and find and
define the necessary methods for properties. This is accomplished
through the method method_missing and define_method.
This should be a consideration if there is a concern about the
performance of the objects that are created, as there is much more
overhead in the setting of these properties compared to using a Hash
or a Struct.
Doing this on mobile so let's give this a try:
class Something
attr_reader :foo, :bar
def initialize(hash = {})
foo = hash[:foo]
bar = hash[:bar]
end
end
obj = Something.new({foo: 1, bar: 2})
obj.foo = 1
obj.bar =2
class Klass
def initialize(h)
h.each { |iv, val| instance_variable_set("##{iv}", val) }
end
end
k = Klass.new(:foo => 1, :bar => 2)
#=> #<Klass:0x007ff1d9073118 #foo=1, #bar=2>
You can use method_missing if you need to set data dynamically.
class Foo
def method_missing(sym, *args)
super unless instance_variables.include?("##{sym}".to_sym)
instance_variable_get("##{sym}")
end
end
obj = Foo.new
h = {:foo => 1, :bar => 2}
h.each { |k, v| obj.instance_variable_set("##{k}", v) }
obj.foo
# => 1
obj.bar
# => 2
If you are using Rails, a minor improvement on your suggested solution would be to use Object#try from Active support extensions
hashmap.each {|k,v| obj.try "#{k}=", v }
As per documentation,
Invokes the public method whose name goes as first argument just like
public_send does, except that if the receiver does not respond to it
the call returns nil rather than raising an exception.
I am trying to cache rails request to an external service which takes a proc.
For me to be able to differentiate 2 requests, its important that the proc is part of the Rails cache key.
However multiple requests with the same proc and the values in the proc code, would still evaluate to a different proc object each time.
Eg:
#<Proc:0x007ff6f675dd08#/Users/app/development/work/runner.rb:10>
#<Proc:0x007ff6ffb50790#/Users/app/development/work/runner.rb:10>
Hence , I get cache miss even for same requests.
How do I use the block/proc in the cache key that evaluates to same if the procs are same in terms of code and variable values.
The service call is something like
def some_method
call_service do
a= 3;
b=6
end
end
def call_service(block)
Rails.cache.fetch(Date.today, block) {
external_service(&block)
}
end
I want to be able to compare blocks :
eg
{a=3, a*a} == {a=3, a*a} => True
{a=3, a*a} == {a=4, a*a} => false
{a=3, a*a} == {a=4, a*a*a} => False
I tried to use,
block.source
RubyVM::InstructionSequence.of(block).disasm
But none of them captures the state of the block, i.e the values of the variables (a=3 etc)
What the best way to achieve this type of caching in rails ?
P.S:
Using Rails4 and reddis as the cache
Each time you define an inline block with do ... end you will get a different Proc object. You will have to go out of your way to be absolutely certain you're sending through exactly the same Proc:
def some_method
#my_call ||= lambda {
a = 3
b = 6
}
call_service(&#my_call)
end
This is probably a bad idea from the outset. What you'd be better off doing is passing in a cache key:
call_service("3/6") do
# ...
end
Then you cache against this consistent key, not the arbitrary Proc.
You might get some mileage this way. This function returns a simple Proc object. The object will have a source location and a binding that contains the state of arguments a, b and c when the Proc was built.
def build_proc(a, b, c)
Proc.new { puts "I was built with params #{a} #{b} #{c}!" }
end
So we can build our Proc objects--
2.0.0-p195 :121 > p1 = build_proc(1, 2, 3)
=> #<Proc:0x98849e8#(irb):118>
2.0.0-p195 :122 > p2 = build_proc(2, 4, 6)
=> #<Proc:0x985e57c#(irb):118>
To get the state of an argument you can call binding.eval('argument') against a Proc object. This runs code in the context of that Proc's binding. So you can say--
p1.binding.eval('[a, b, c]') == p2.binding.eval('[a, b, c]')
2.0.0-p195 :127 > p1.binding.eval('[a, b, c]') == p2.binding.eval('[a, b, c]')
=> false
2.0.0-p195 :128 > p2 = build_proc(1, 2, 3)
=> #<Proc:0x97ae4b0#(irb):118>
2.0.0-p195 :129 > p1.binding.eval('[a, b, c]') == p2.binding.eval('[a, b, c]')
=> true
And obviously you can compare source locations--
p1.source_location == p2.source_location &&
p1.binding.eval('[a, b, c]') == p2.binding.eval('[a, b, c]')
You'd need a way to extract the arguments out of the binding in a universal way and you'd want to combine their hashes rather than build an array for comparison (e.g. 'a.hash ^ b.hash ^ c.hash').
This does feel horrible though! At the very least, overriding :== on Procs is probably a bad idea.
I need to be able to use this call:
h = x.inject({}) {|a, b| a[b.one] = b.two; a}
Where x is a sequence of Couple objects (these just contain two number fields, one and two).
I am not sure how to implement the inject method in Couple.
Define an #each method in Couple, then include Enumerable in it.
class Couple
def each
yield "a"
yield "b"
end
include Enumerable
end
couple = Couple.new
couple.inject("") { |str, obj| str + obj }
# => "ab"
http://www.ruby-doc.org/core-1.9.3/Enumerable.html
This question already has an answer here:
How to implement injection in Ruby?
(1 answer)
Closed 8 years ago.
I have a class called Hsh which basically simulates a hash. It has an array of Couple objects (which hold fields named one and two, one is an int another is a string name of that int).
I am supposed to be able to accept the following call:
h = x.inject({}) {|a, b| a[b.one] = b.two; a}
Where x is the Hsh object.
I am not sure how to implement the inject method within Hsh? Like, what would I write in:
def inject ????
??
??
end
All it's supposed to do is create a hash map.
you shouldn't really need to implement it, just implement Hsh#eachand include Enumerable, you'll get inject for free.
For your specific example something like this should work:
def inject accumulator
#I assume Hsh has some way to iterate over the elements
each do |elem|
accumulator = yield accumulator, elem
end
accumulator
end
But the real implementation of inject is a bit different (e.g. works without providing an accumulator, takes a symbol instead of a block etc)
require 'ostruct'
class Hsh
include Enumerable
def initialize
#arr = (0..9).map{ |i| OpenStruct.new(:one => i, :two => "#{i}")}
end
def each(&block)
#arr.each(&block)
end
end
p Hsh.new.inject({}) {|a, b| a[b.one] = b.two; a}
#=> {5=>"5", 0=>"0", 6=>"6", 1=>"1", 7=>"7", 2=>"2", 8=>"8", 3=>"3", 9=>"9", 4=>"4"}
In this particular case Hsh is actually an array, so unless you use it for something else such a complex code doesn't make sense, it can be done much easier:
p (0..9).map{ |i| OpenStruct.new(:one => i, :two => "#{i}")} \
.inject({}) {|a, b| a[b.one] = b.two; a}
#=> {5=>"5", 0=>"0", 6=>"6", 1=>"1", 7=>"7", 2=>"2", 8=>"8", 3=>"3", 9=>"9", 4=>"4"}
I have used OpenStruct instead of classes. See if this works for you
require 'ostruct'
class Hsh
attr_accessor :arr
def initialize
obj = OpenStruct.new
obj.one = 1
obj.two = "two"
#arr = [obj]
end
def inject(hash)
#arr.each do |arr|
yield hash, arr
end
hash
end
end
x = Hsh.new
p x.inject({}) {|a, b| a[b.one] = b.two} #prints {1 => "two"}
I know these are the basics of rails but i still don't know the full difference between = sign and => and the difference between #some_variable, ##some_variable and :some_variable in rails.
Thanks.
OK.
The difference between the = and the => operators is that the first is assignment, the second represents an association in a hash (associative array). So { :key => 'val' } is saying "create an associative array, with :key being the key, and 'val' being the value". If you want to sound like a Rubyist, we call this the "hashrocket". (Believe it or not, this isn't the most strange operator in Ruby; we also have the <=>, or "spaceship operator".)
You may be confused because there is a bit of a shortcut you can use in methods, if the last parameter is a hash, you can omit the squiggly brackets ({}). so calling render :partial => 'foo' is basically calling the render method, passing in a hash with a single key/value pair. Because of this, you often see a hash as the last parameter to sort of have a poor man's optional parameters (you see something similar done in JavaScript too).
In Ruby, any normal word is a local variable. So foo inside a method is a variable scoped to the method level. Prefixing a variable with # means scope the variable to the instance. So #foo in a method is an instance level.
## means a class variable, meaning that ## variables are in scope of the class, and all instances.
: means symbol. A symbol in Ruby is a special kind of string that implies that it will be used as a key. If you are coming from C#/Java, they are similar in use to the key part of an enum. There are some other differences too, but basically any time you are going to treat a string as any sort of key, you use a symbol instead.
Wow, a that's a lot of different concepts together.
1) = is plain old assignment.
a = 4;
puts a
2) => is used to declare hashes
hash = {'a' => 1, 'b' => 2, 'c' => 3}
puts hash['b'] # prints 2
3) #var lets you access object instance variable.
class MyObject
def set_x(x)
#x = x
end
def get_x
#x
end
end
o = MyObject.new
o.set_x 3
puts o.get_x # prints 3
4) ##var lets you access class ('static') variables.
class MyObject
def set_x(x)
##x = x # now you can access '##x' from other MyObject instance
end
def get_x
##x
end
end
o1 = MyObject.new
o1.set_x 3
o2 = MyObject.new
puts o2.get_x # prints 3, even though 'set_x' was invoked on different object
5) I usually think of :var as special 'label' class. Example 2 can be rephrased like this
hash = {:a => 1, :b => 2, :c => 3}
puts hash[:b] # prints 2