Why this (dead simple) ruby regex behaves like this? - ruby

Why "whatever".gsub(/.*/, "bien") outputs "bienbien" instead of just "bien"?
I'm completely lost here :S Anyone could point me in the right direction?

You can see what's happening using a block:
>> 'foo'.sub(/.*/) { |m| p m; 'bar' }
"foo"
=> "bar"
>> 'foo'.gsub(/.*/) { |m| p m; 'bar' }
"foo"
""
=> "barbar"
>> 'foo'.gsub(/^.*/) { |m| p m; 'bar' }
"foo"
=> "bar"
>> 'foo'.gsub(/^.*$/) { |m| p m; 'bar' }
"foo"
=> "bar"
>> 'foo'.gsub(/.*$/) { |m| p m; 'bar' }
"foo"
""
=> "barbar"
>> 'foo'.gsub(/.+/) { |m| p m; 'bar' }
"foo"
=> "bar"
Put another way, gsub will continue matching, and matches an empty string at the very end a line. (And that is arguably a bug.)

Related

how to break chain with yield_self

Is it possible to break yield_self (then) chain in ruby?
"a".then { |str| <break> if break_condition }.then { |str| str << "b" } # => I need "a"
You could move the whole code into another method and "break" the chain with a simple return:
def foo
"a".then { |str| return str if break_condition ; str }
.then { |str| str << "b" }
end
You could utilize catch and throw:
catch do |brk|
"a".then { |str| throw(brk, str) if break_condition ; str }
.then { |str| str << "b" }
end
Alternatively, you could not use then in the first place:
str = "a"
str << "b" unless break_condition
I don't think that you can "break" the chain without raising an exception but you can pass a tagged value from the first block to the second one:
"a".
then { |str| [str, !break_condition] }.
then { |str,do_it| do_it ? str << "b" : str }
edit: An other form that might be easier to work with:
"a".
then { |str| break [str,true] if break_condition; str }.
then { |str,broken| break str if broken; str << "b" }
You can refactor your chain with using just one then like this
result =
"a".then do |str|
next str if break_condition
str << "b"
end
or
result =
"a".then do |str|
break str if break_condition
str << "b"
end
This might be silly, but since you can't break or return, I guess raise should do it:
def broken break_condition
begin
"a"
.then { |str| break_condition ? raise(str) : str }
.then { |str| str << "b" }
rescue RuntimeError => e
str = e.message
end
end
>> broken true
=> "a"
>> broken false
=> "ab"
One liner works too:
>> "a".then { |str| true ? raise(str) : str }.then { |str| str << "b" } rescue $!.message
=> "a"
>> "a".then { |str| false ? raise(str) : str }.then { |str| str << "b" } rescue $!.message
=> "ab"
Best silly solution I could come up with:
>> "a".then{#a=_1}.then { |s| s if false }&.then { |s| s << "b" }||#a
=> "a"
>> "a".then{#a=_1}.then { |s| s if true }&.then { |s| s << "b" }||#a
=> "ab"

How ||= and unless include? differe?

What are the differences between the 2 following line ?
# settings and globals are Hash
#settings[:xvfb] = globals[:xvfb] unless settings.include?(:xvfb)
#settings[:xvfb] ||= globals[:xvfb]
Are they equivalent ?
Nope, they are not equivalent. They differ in handling falsey values.
globals = { foo: 'bar' }
h = { foo: nil }
h.include?(:foo) # => true
h[:foo] = globals[:foo] unless h.include?(:foo)
h # => {:foo=>nil}
h = { foo: nil }
h[:foo] ||= globals[:foo]
h # => {:foo=>"bar"}

Can I get the object upon which the each function has been called in Ruby?

Is it possible to refer to the object upon which a function has been called in Ruby?
For example, say I want to get the length of an array that I've called each on like so:
[1,2,3].each {|x| if <the_array>.length > 2 then { # do something } }
Is this possible?
You can use instance_eval like below :
[1,2,3].instance_eval { self.each { |e| puts e >= self.size ? "foo" : "bar" } }
# >> bar
# >> bar
# >> foo
Could you use Object#tap?
[1,2,3].tap { |a| a.each {|x| puts "x = #{x}" if a.length > 2 } }
# x = 1
# x = 2
# x = 3
[1,2].tap { |a| a.each {|x| puts "x = #{x}" if a.length > 2 } }
# <nothing printed>

Scanning an array of strings for match in Ruby

I have an array:
a = ["http://design.example.com", "http://www.domcx.com", "http://subr.com"]
and then I want to return true if one of the elements in that array matches the string:
s = "example.com"
I tried with include? and any?.
a.include? s
a.any?{|w| s=~ /#{w}/}
I don't know how to use it here. Any suggestions?
You can use any? like:
[
"http://design.example.com",
"http://www.domcx.com",
"http://subr.com"
].any?{ |s| s['example.com'] }
Substituting your variable names:
a = [
"http://design.example.com",
"http://www.domcx.com",
"http://subr.com"
]
s = "example.com"
a.any?{ |i| i[s] }
You can do it several other ways also, but the advantage using any? is it will stop as soon as you get one hit, so it can be much faster if that hit occurs early in the list.
How is the below:
a=["http://design.example.com", "http://www.domcx.com", "http://subr.com"]
s= "sus"
p a.any? { |w| w.include? s } #=> false
a=["http://design.example.com", "http://www.domcx.com", "http://subr.com"]
s= "design.example"
p a.any? { |w| w.include? s } #=>true
a=["http://design.example.com", "http://www.domcx.com", "http://subr.com"]
s= "desingn.example"
p a.any? { |w| w.include? s } #=>false
a=["http://design.example.com", "http://www.domcx.com", "http://subr.com"]
s= "example"
p a.any? { |w| w.include? s } #=>true
a=["http://design.example.com", "http://www.domcx.com", "http://subr.com"]
s= "example.com"
p a.any? { |w| w.include? s } #=>true

Ruby: Self reference in hash

Is it possible to reference one element in a hash within another element in the same hash?
# Pseudo code
foo = { :world => "World", :hello => "Hello #{foo[:world]}" }
foo[:hello] # => "Hello World"
Indirectly perhaps...
foo = { :world => 'World', :hello => lambda { "Hello #{foo[:world]}" }}
puts foo[:hello].call
If you want to make values of some keys dependent on others:
foo = Hash.new{|h, k|
case k
when :hello; "Hello #{h[:world]}"
when :bye; "Bye #{h[:world]}"
end
}
foo[:world] = 'World'
foo[:hello] # => 'Hello World'
foo[:bye] # => 'Bye World'
foo[:world] = 'Heaven'
foo[:hello] # => 'Hello Heaven'
foo[:bye] # => 'Bye Heaven'
This can't be done directly because "" is strictly evaluated.
Use of a lazy-value generator (e.g. lambda/proc) with later evaluation is required.
Happy coding.
No.
At least not in one step. You could do something like:
foo = {world: "hello"}
foo[:hello] = "Hello #{foo[:world]}"
Sure you can!
options = { :deep => :stuff }
options.merge!(:options => options)
# It's the same at any depth...
options[:options][:options][:options][:options][:options]
#=> {:deep=>:stuff, :options=>{...}}
Neat, huh? The hash object in options has the same object_id as the value assigned to :options.
Object#tap gives you a nice way to complete the Object initialisation with extra self-references.
{ foo: 1 }.tap { |obj| obj[:bar] = obj.fetch(:foo)}

Resources