As it currently stands, this question is not a good fit for our Q&A format. We expect answers to be supported by facts, references, or expertise, but this question will likely solicit debate, arguments, polling, or extended discussion. If you feel that this question can be improved and possibly reopened, visit the help center for guidance.
Closed 10 years ago.
Ok, I'm green, but the following def seems double-negative-y
def signed_in?
!current_user.nil? #the current user is not...um...not
end
Since my patient mentor M.Hartl uses it in his Rails tutorial. I gotta believe it's squeaky, but...
wouldn't something that says "the current user is" be cleaner?
def signed_in?
current_user
current_user.present?
current_user.any?
!!current_user
end
What's the benefit of the bang?
current_user | nil | false | true | "" | [] | [nil] | [0]
-----------------------------------------------------------------------------------
current_user | nil | false | true | "" | [] | [nil] | [0]
!current_user.nil? | false | true | true | true | true | true | true
!!current_user | false | false | true | true | true | true | true
current_user.present? | false | false | true | false | false | true | true
current_user.any? | error | error | error | error | false | false | true
You could:
def signed_in?
current_user
end
if you can stand it being not a boolean returned from the method. If current_user is nil signed_in? will evaluate to false when used in an if statement. To check for nil? is not necessary and I guess it is a matter of style.
Or in your calling code you could:
if current_user
# do stuff
end
and get rid of the extra method.
In that example you don't care who the user is. All you want to know is that a user, any user, is signed in. If so, you will show links for signed-in users.
current_user.nil? is a boolean. It will be false is a user is signed in. The bang in front inverts it, so !current_user.nil? will be true if any user is signed in, false otherwise.
Can you think of another equally succinct way to accomplish exactly that, nothing more and nothing less?
Related
I need to tell Ruby in regex to split before and after the + - * / symbols in my program.
Examples:
I need to turn "1+12" into [1.0, "+", 12.0]
and "6/0.25" into [6.0, "/", 0.25]
There could be cases like "3/0.125" but highly unlikely. If first two I listed above are satisfied it should be good.
On the Ruby docs, "hi mom".split(%r{\s*}) #=> ["h", "i", "m", "o", "m"]
I looked up a cheat-sheet to try to understand %r{\s*}, and I know that the stuff inside %r{} such as \s are skipped and \s means white space in regex.
'1.0+23.7'.scan(/(((\d\.?)+)|[\+\-\*\/])/)
instead of splitting, match with capture groups to parse your inputs:
(?<operand1>(?:\d+(?:\.\d+)?)|(?:\.\d+))\s*(?<operator>[+\/*-])\s*(?<operand2>(?:\d+(?:\.\d+)?)|(?:\.\d+))
explanation:
I've used named groups (?<groupName>regex) but they aren't necessary and could just be ()'s - either way, the sub-captures will still be available as 1,2,and 3. Also note the (?:regex) constructs that are for grouping only and do not "remember" anything, and won't mess up your captures)
(?:\d+(?:\.\d+)?)|(?:\.\d+)) first number: either leading digit(s) followed optionally by a decimal point and digit(s), OR a leading decimal point followed by digit(s)
\s* zero or more spaces in between
[+\/*-] operator: character class meaning a plus, division sign, minus, or multiply.
\s* zero or more spaces in between
(?:\d+(?:\.\d+)?)|(?:\.\d+) second number: same pattern as first number.
regex demo output:
I arrived a little late to this party, and found that many of the good answers had already been taken. So, I set out to expand on the theme slightly and compare the performance and robustness of each of the solutions. It seemed like a fun way to entertain myself this morning.
In addition to the 3 examples given in the question, I added test cases for each of the four operators, as well as for some new edge cases. These edge cases included handling of negative numbers and arbitrary spaces between operands, as well as how each of the algorithms handled expected failures.
The answers revolved around 3 methods: split, scan, and match. I also wrote new solutions using each of these 3 methods, specifically respecting the additional edge cases that I added to here. I ran all of the algorithms against this full set of test cases, and ended up with a table of pass/fail results.
Next, I created a benchmark that created 1,000,000 test strings that each of the solutions would be able to parse properly, and ran each solution against that sample set.
On first benchmarking, Cary Swoveland's solution performed far better than the others, but didn't pass the added test cases. I made very minor changes to his solution to produce a solution that supported both negative numbers and arbitrary spaces, and included that test as Swoveland+.
The final results printed from to the console are here (note: horizontal scroll to see all results):
| Test Case | match | match | scan | scan |partition| split | split | split | split |
| | Gaskill | sweaver | Gaskill | techbio |Swoveland| Gaskill |Swoveland|Swoveland+| Lilue |
|------------------------------------------------------------------------------------------------------|
| "1+12" | Pass | Pass | Pass | Pass | Pass | Pass | Pass | Pass | Pass |
| "6/0.25" | Pass | Pass | Pass | Pass | Pass | Pass | Pass | Pass | Pass |
| "3/0.125" | Pass | Pass | Pass | Pass | Pass | Pass | Pass | Pass | Pass |
| "30-6" | Pass | Pass | Pass | Pass | Pass | Pass | Pass | Pass | Pass |
| "3*8" | Pass | Pass | Pass | Pass | Pass | Pass | Pass | Pass | Pass |
| "20--4" | Pass | -- | Pass | -- | Pass | Pass | -- | Pass | Pass |
| "33+-9" | Pass | -- | Pass | -- | Pass | Pass | -- | Pass | Pass |
| "-12*-2" | Pass | -- | Pass | -- | Pass | Pass | -- | Pass | Pass |
| "-72/-3" | Pass | -- | Pass | -- | Pass | Pass | -- | Pass | Pass |
| "34 - 10" | Pass | Pass | Pass | Pass | Pass | Pass | Pass | Pass | Pass |
| " 15+ 9" | Pass | Pass | Pass | Pass | Pass | Pass | Pass | Pass | Pass |
| "4*6 " | Pass | Pass | Pass | Pass | Pass | Pass | Pass | Pass | Pass |
| "b+0.5" | Pass | Pass | Pass | -- | -- | -- | -- | -- | -- |
| "8---0.5" | Pass | Pass | Pass | -- | -- | -- | -- | -- | -- |
| "8+6+10" | Pass | -- | Pass | -- | -- | -- | -- | -- | -- |
| "15*x" | Pass | Pass | Pass | -- | -- | -- | -- | -- | -- |
| "1.A^ff" | Pass | Pass | Pass | -- | -- | -- | -- | -- | -- |
ruby 2.2.5p319 (2016-04-26 revision 54774) [x86_64-darwin14]
============================================================
user system total real
match (Gaskill): 4.770000 0.090000 4.860000 ( 5.214996)
match (sweaver2112): 4.640000 0.040000 4.680000 ( 4.911849)
scan (Gaskill): 7.360000 0.080000 7.440000 ( 7.719646)
scan (techbio): 12.930000 0.140000 13.070000 ( 13.791613)
partition (Swoveland): 5.390000 0.050000 5.440000 ( 5.648762)
split (Gaskill): 5.150000 0.100000 5.250000 ( 5.455094)
split (Swoveland): 3.860000 0.060000 3.920000 ( 4.040774)
split (Swoveland+): 4.240000 0.040000 4.280000 ( 4.537570)
split (Lilue): 7.540000 0.090000 7.630000 ( 8.022252)
In order to keep this post from being far too long, I've included the complete code for this test at https://gist.github.com/mgaskill/96f04e7e1f72a86446f4939ac690759a
The robustness test cases can be found in the first table above. The Swoveland+ solution is:
f,op,l = formula.split(/\b\s*([+\/*-])\s*/)
return [f.to_f, op, l.to_f]
This includes a \b metacharacter prior to splitting on an operator ensures that the previous character is a word character, giving support for negative numbers in the second operand. The \s* metacharacter expressions support arbitrary spaces between operands and operator. These changes incur less than 10% performance overhead for the additional robustness.
The solutions that I provided are here:
def match_gaskill(formula)
return [] unless (match = formula.match(/^\s*(-?\d+(?:\.\d+)?)\s*([+\/*-])\s*(-?\d+(?:\.\d+)?)\s*$/))
return [match[1].to_f, match[2], match[3].to_f]
end
def scan_gaskill(formula)
return [] unless (match = formula.scan(/^\s*(-?\d+(?:\.\d+)?)\s*([+*\/-])\s*(-?\d+(?:\.\d+)?)\s*$/))[0]
return [match[0][0].to_f, match[0][1], match[0][2].to_f]
end
def split_gaskill(formula)
match = formula.split(/(-?\d+(?:\.\d+)?)\s*([+\/*-])\s*(-?\d+(?:\.\d+)?)/)
return [match[1].to_f, match[2], match[3].to_f]
end
The match and scan solutions are very similar, but perform significantly differently, which is very interesting, because they use the exact same regex to do the work. The split solution is slightly simpler, and only splits on the entire expression, capturing each operand and the operator, separately.
Note that none of the split solutions was able to properly identify failures. Adding this support requires additional parsing of the operands, which significantly increases the overhead of the solution, typically running about 3 times slower.
For both performance and robustness, match is the clear winner. If robustness isn't a concern, but performance is, use split. On the other hand, scan provided complete robustness, but was more than 50% slower than the equivalent match solution.
Also note that using an efficient way to extract the results from the solution into the result array is as important to performance as is the algorithm chosen. The technique of capturing the results array into multiple variables (used in Woveland) outperformed the map solutions dramatically. Early testing showed that the map extraction solution more than doubled the runtimes of even the highest-performing solutions, hence the exceptionally high runtime numbers for Lilue.
I think this could be useful:
"1.2+3.453".split('+').flat_map{|elem| [elem, "+"]}[0...-1]
# => ["1.2", "+", "3.453"]
"1.2+3.453".split('+').flat_map{|elem| [elem.to_f, "+"]}[0...-1]
# => [1.2, "+", 3.453]
Obviously this work only for +. But you can change the split character.
EDIT:
This version work for every operator
"1.2+3.453".split(%r{(\+|\-|\/|\*)}).map do |x|
unless x =~ /(\+|\-|\/|\*)/ then x.to_f else x end
end
# => [1.2, "+", 3.453]
R = /
(?<=\d) # match a digit in a positive lookbehind
[^\d\.] # match any character other than a digit or period
/x # free-spacing regex definition mode
def split_it(str)
f,op,l = str.delete(' ').partition(R)
[convert(f), op, convert(l)]
end
def convert(str)
(str =~ /\./) ? str.to_f : str.to_i
end
split_it "1+12"
#=> [1, "+", 12]
split_it "3/ 5.2"
#=> [3, "/", 5.2]
split_it "-4.1 * 6"
#=> [-4.1, "*", 6]
split_it "-8/-2"
#=> [-8, "/", -2]
The regex can of course be written in the conventional way:
R = /(?<=\d)[^\d\.]/
In my scenario outline I have the below
Examples:
| user | password | from | to | amount | date | message |
| joel10 | lolpw12 | bankA | bankB | $100 | 1/30/2015 | Transfer Success. |
in my step definitions I have the below
And(/^the user inputs fields (.*), (.*), (.*)$/) do |from, to, amount|
on(TransferPage).from = /#{from}/
on(TransferPage).to = /#{to}/
on(TransferPage).amount = /#{amount}/
on(TransferPage).date = /#{date}/
end
The FROM, TO, and AMOUNT all comes out correct from the table but when it inputs the date, it comes out (?-mix:1/30/2015)
why is this happening and how do i fix?
When you do /#{date}/ you are taking the value returned from the parsing of the step definition and then turning it into a regular expression:
/#{date}/.class
#=> Regexp
You presumably want to leave the value in its original String format:
on(TransferPage).date = date
For personal homework I am trying out a digital clock program to display 'big' numbers.
A ruby program that has parts of the clock that come from strings stored in an array and then a routine (not yet fully written) that can display any numbers as 'big numbers'.
This is to help me learn more about manipulating hashes and iterating through arrays, concatenating string, etc.
I am stuck right now with this:
all_nums=''
(0..3).each do |num|
all_nums+= (line_parts[num][0].values).to_s
#puts line_parts[num][0].values
end
puts all_nums
because I am getting back array elements that I can't convert to strings, i.e.
ruby digital_clock,rb
[" == "]["| | "]["| | "][" == "]
but when I just puts them (commented out 3rd line from the bottom) it is OK but, I get strings, i.e.
ruby digital_clock.rb
==
| |
| |
==
I want to be able to build up the string (big 0 in this case) but I can only puts out the pieces and I've been unable to assign them to a variable and print it. How could I do that?
The full program is:
top= ' == | == == | | == | == == == '
top_middle= '| | | __| __| |__| |__ |__ | |__| |__|'
bottom_middle='| | | | | | | | | | | | |'
bottom= ' == | == == | == == | == |'
number_parts=[{},{},{},{},{},{},{},{},{},{}]
line_parts=[[],[],[],[]]
['top','top_middle','bottom_middle','bottom'].each_with_index do |one_line, i|
number_parts=[{},{},{},{},{},{},{},{},{},{}]
(0..9).each do |the_num|
number_parts[the_num] = {one_line.to_sym => eval(one_line)[the_num*5,5].to_s}
end
line_parts[i]= number_parts
end
all_nums=''
(0..3).each do |num|
all_nums+= (line_parts[num][0].values).to_s
#puts line_parts[num][0].values
end
puts all_nums
I think this will fix your immediate problem -- namely, printing a big "0".
all_nums = []
(0..3).each do |num|
all_nums.push(line_parts[num][0].values)
end
puts all_nums.join("\n")
A better way to do that would be with map:
all_nums = (0..3).map { |num| line_parts[num][0].values }
puts all_nums.join("\n")
Another issue: you don't need to use eval. In fact, one almost never needs to use eval.
# Use variables rather than the strings 'top', 'top_middle', etc.
[top,top_middle,bottom_middle,bottom].each_with_index do |one_line, i|
...
# Then the eval() is unnecessary.
number_parts[the_num] = {one_line.to_sym => one_line[the_num*5,5]}
....
But you can really simplify the code along the following lines:
# Use an array, not a set of specifically named variables.
# This makes it a lot easier to iterate over the data.
rows = [
' -- | -- -- | | -- | -- -- -- ',
' | | | __| __| |__| |__ |__ | |__| |__|',
' | | | | | | | | | | | | |',
' -- | -- -- | -- -- | -- |',
]
# Create an array-of-arrays.
# num_parts[NUMBER] = [an array of four strings]
width = 5
num_parts = (0..9).map { |n| rows.map { |r| r[n * width, width] } }
# Inspect.
num_parts.each do |np|
puts
puts np.join("\n")
end
You might also take a look at the Unix command called banner, which will inspire you to make the numbers EVEN BIGGER. That would allow you to start adding curves and other design touches.
I am using a scenario table (multiline step arguments) to check some data from a screen using cucumber, using the in built .diff! method on the Cucumber AST table.
I would like to check the content matches against regular expressions.
Scenario: One
Then the table appears as:
| One | Two | Three |
| /\d+/ | /\d+/ | /\d+/ |
The actual table could look something like
| One | Two | Three |
| 123 | 456 | 789 |
which this scenario is translated to "as long as there are some digits, I don't care"
An example step implementation that fails:
Then /^the table appears as:$/ do |expected_table|
actual_table = [['One','Two', 'Three'],['123', '456', '789']]
expected_table.diff! actual_table
end
Error:
Then the table appears as: # features/step_definitions/my_steps.rb:230
| One | Two | Three |
| /\\d+/ | /\\d+/ | /\\d+/ |
| 123 | 456 | 789 |
Tables were not identical (Cucumber::Ast::Table::Different)
I have tried using step transforms to transform the cells into regular expressions, but they still aren't identical.
Transform code:
expected_table.raw[0].each do |column|
expected_table.map_column! column do |cell|
if cell.respond_to? :start_with?
if cell.start_with? "/"
cell.to_regexp
else
cell
end
else
cell
end
end
end
which provides the eror:
Then the table appears as: # features/step_definitions/my_steps.rb:228
| One | Two | Three |
| (?-mix:\\d+) | (?-mix:\\d+) | (?-mix:\\d+) |
| 123 | 456 | 789 |
Tables were not identical (Cucumber::Ast::Table::Different)
Any ideas? I am stuck.
Using regular expressions in a scenario is almost certainly the wrong approach. Cucumber features are intended to be read and understood by business-focussed stakeholders.
How about writing the step at a higher level, such as as:
Then the first three columns of the table should contain a digit
There is no way to do it without writing your own implementation of diff! method from Ast::Table. Take a look into cucumber/lib/ast/table.rb. Internally it uses diff-lcs library to do an actual comparison which doesn't support regex match.
It seems that you want to write this in a way that provides the cool diff output. Otherwise, I'd look at writing this such that you simply check the rows. It won't be as pretty, and it won't get you the diff of the entire table, but it's something.
Then /^the table appears as:$/ do |expected_table|
actual_table = [['One','Two', 'Three'],['123', '456', '789']]
expected_table.raw.each_with_index { |row, y|
row.each_with_index { |cell, x|
actual_table[x][y].should == cell
}
}
end
How can I print a large variable nicely in an irb prompt? I have a variable that contains many variables which are long and the printout becomes a mess to wade through. What if I just want the variable names without their values? Or, can I print each one on a separate line, tabbed-in depending on depth?
Or, can I print each one on a separate line, tabbed-in depending on depth?
Use pp (pretty print):
require 'pp'
very_long_hash = Hash[(1..23).zip(20..42)]
pp very_long_hash
# Prints:
{1=>20,
2=>21,
3=>22,
4=>23,
5=>24,
6=>25,
7=>26,
8=>27,
9=>28,
10=>29,
11=>30,
12=>31,
13=>32,
14=>33,
15=>34,
16=>35,
17=>36,
18=>37,
19=>38,
20=>39,
21=>40,
22=>41,
23=>42}
If you want something that is even more awesome than "pretty" print, you can use "awesome" print. And for a truly spaced out experience, sprinkle some hirbal medicine on your IRb!
Hirb, for example, renders ActiveRecord objects (or pretty much any database access library) as actual ASCII tables:
+-----+-------------------------+-------------+-------------------+-----------+-----------+----------+
| id | created_at | description | name | namespace | predicate | value |
+-----+-------------------------+-------------+-------------------+-----------+-----------+----------+
| 907 | 2009-03-06 21:10:41 UTC | | gem:tags=yaml | gem | tags | yaml |
| 906 | 2009-03-06 08:47:04 UTC | | gem:tags=nomonkey | gem | tags | nomonkey |
| 905 | 2009-03-04 00:30:10 UTC | | article:tags=ruby | article | tags | ruby |
+-----+-------------------------+-------------+-------------------+-----------+-----------+----------+