I have a method to capture the extension as a group using a regex:
def test(str)
word_match = str.match(/\.(\w*)/)
word_scan = str.scan(/\.(\w*)/)
puts word_match, word_scan
end
test("test.rb")
So it will return:
.rb
rb
Why would I get a different answer?
The reason is that match and scan return different objects. match returns either a MatchData object or a String while scan returns an Array. You can see this by calling the class method on your variables
puts word_match.class # => MatchData
puts word_scan.class # => Array
If you take a look at the to_s method on MatchData you'll notice it returns the entire matched string, rather than the captures. If you wanted just the captures you could use the captures method.
puts word_match.captures # => "rb"
puts word_match.captures.class # => Array
If you were to pass a block to the match method you would get a string back with similar results to the scan method.
word_match = str.match(/\.(\w*)/) { |m| m.captures } # => [["rb"]]
puts word_scan.inspect #=> ["rb"]
puts word_match #=> "rb
More information on these methods and how they work can be found in the ruby-doc for the String class.
Don't write your own code for this, take advantage of Ruby's own built-in code:
File.extname("test.rb") # => ".rb"
File.extname("a/b/d/test.rb") # => ".rb"
File.extname(".a/b/d/test.rb") # => ".rb"
File.extname("foo.") # => "."
File.extname("test") # => ""
File.extname(".profile") # => ""
File.extname(".profile.sh") # => ".sh"
You're missing some cases. Compare the above to the output of your attempts:
fnames = %w[
test.rb
a/b/d/test.rb
.a/b/d/test.rb
foo.
test
.profile
.profile.sh
]
fnames.map { |fn|
fn.match(/\.(\w*)/).to_s
}
# => [".rb", ".rb", ".a", ".", "", ".profile", ".profile"]
fnames.map { |fn|
fn.scan(/\.(\w*)/).to_s
}
# => ["[[\"rb\"]]",
# "[[\"rb\"]]",
# "[[\"a\"], [\"rb\"]]",
# "[[\"\"]]",
# "[]",
# "[[\"profile\"]]",
# "[[\"profile\"], [\"sh\"]]"]
The documentation for File.extname says:
Returns the extension (the portion of file name in path starting from the last period).
If path is a dotfile, or starts with a period, then the starting dot is not dealt with the start of the extension.
An empty string will also be returned when the period is the last character in path.
On Windows, trailing dots are truncated.
The File class has many more useful methods to pick apart filenames. There's also the Pathname class which is very useful for similar things.
I have a Model user with the following method:
def number_with_hyphen
number&.insert(8, "-")
end
When I run it several times in my tests I get the following output:
users(:default).number_with_hyphen
"340909-1234"
(byebug) users(:default).number_with_hyphen
"340909--1234"
(byebug) users(:default).number_with_hyphen
"340909---1234"
(byebug) users(:default).number_with_hyphen
"340909----1234"
It changes the number ?Here are the docs https://apidock.com/ruby/v1_9_3_392/String/insert
When I restructure my method to:
def number_with_hyphen
"#{number}".insert(8, "-") if number
end
If works like expected. The output stays the same!
How would you structure the code, how would you perform the insert?
which method should I use instead. Thanks
If you're using the insert method, which in the documentation explicitly states "modifies str", then you will need to avoid doing this twice, rendering it idempotent, or use another method that doesn't mangle data.
One way is a simple regular expression to extract the components you're interested in, ignoring any dash already present:
def number_with_hyphen
if (m = number.match(/\A(\d{8})\-?(\d+)\z/))
[ m[1], m[2] ].join('-')
else
number
end
end
That ends up being really safe. If modified to accept an argument, you can test this:
number = '123456781234'
number_with_hyphen(number)
# => "12345678-1234"
number
# => "123456781234"
number_with_hyphen(number_with_hyphen(number))
# => "12345678-1234"
number_with_hyphen('1234')
# => "1234"
Calling it twice doesn't mangle anything, and any non-conforming data is sent through as-is.
Do a clone of the string:
"#{number}".clone.insert(8, '-')
I'm trying to find a specific text segment within a text file and than a specific line within the text segment. The algorithm should be as follow:
1)First, search for a line which contains the keyword "Macros"
2)The next found line must contain the keyword "Name"
3)And finally print me the next line
As pseudo code I mean something like this:
File.open(file_name) do |f|
f.each_line {|line|
if line.include?("Macros")
and if next line.include?("Name")
print me the line after
end
Any suggestions?
I would use boolean flags to remember that I already matched the parts of the condition:
File.open(file_name) do |file|
marcos_found = false
name_found = false
file.each_line do |line|
if line.include?('Macros')
marcos_found = true
elsif found_marcos && line.include?("Name")
name_found = true
elsif marcos_found && name_found
puts line
break # do not search further or print later matches
end
end
end
You could use a regex:
r = /
\bMacros\b # Match "Macros" surrounded by word breaks
.*?$ # Match anything lazily to the end of the line
[^$]* # Match anything lazily other than a line break
\bName\b # Match "Name" surrounded by word breaks
.*?\n # Match anything lazily to the end of the line
\K # Discard everything matched so far
.*?$ # Match anything lazily to the end of the line
/x # Extended/free-spacing mode
Suppose:
text = <<-_
You can use
Macros in C
to replace code.
Ruby doesn't
have Macros.
"Name That Tune"
was an old TV
show.
_
Let's write this to file:
FName = "test"
File.write(FName, text)
#=> 104
read it back into a string:
str = File.read(FName)
#=> "You can use\nMacros in C\nto replace code.\nRuby doesn't\nhave " +
# "Macros.\n\"Name That Tune\"\nwas an old TV\nshow.\n"
and test the regex:
text.scan r
#=> ["was an old TV"]
I am using Ruby on Rails 3.0.9 and RSpec 2. I am trying to refactoring some spec file in the following way (in order to test with less code similar class object attribute values):
[
:attribute_a,
:attribute_b,
:attribute_c
].each do |attr|
before do
# HERE I would like to set the "current" 'attr' related to the
# class object instance 'attribute_< letter >' (read below for
# more information) each time the iterator is called (note: all
# following attributes are NOT attr_accesible - for that reason
# I use the 'user.attribute_< letter >' in the 'before do'
# statement)
#
# # Iteration 1
# user.attribute_a = 'a_value'
# # No changes to 'user.attribute_b'
# # No changes to 'user.attribute_c'
#
# # Iteration 2
# # No changes to 'user.attribute_a'
# user.attribute_b = 'a_value'
# # No changes to 'user.attribute_c'
#
# # Iteration 3
# # No changes to 'user.attribute_a'
# # No changes to 'user.attribute_b'
# user.attribute_c = 'a_value'
# Maybe I should make something like the following but that, as well
# as the 'send' method must be used, doesn't work (the below code-line
# is just an example of what I would like to do).
#
# user.send(:"#{attr}") = 'a_value'
end
...
end
How can I improve the above code so to accomplish what I aim (I refer to the user.send(:"#{attr}") = 'a_value' part in order to set "programmatically" - that is, set a different attribute value for each iteration made - each user attribute value to 'a_value')?
You should use .send and append an = to the method name to invoke the setter, passing the value as the second argument to send:
[
:attribute_a,
:attribute_b,
:attribute_c
].each do |attr|
before do
user.send("#{attr}=", 'a_value')
end
You're effectively doing this:
user.send('attribute_a=', 'a_value');
Your syntax (user.send(:"#{attr}") = 'a_value') is wrong/weird for a couple reasons:
There's no reason to convert :attr to a string and them immediately back to a symbol
You can't assign a value to the return value of .send
Given any object in Ruby (on Rails), how can I write a method so that it will display that object's instance variable names and its values, like this:
#x: 1
#y: 2
#link_to_point: #<Point:0x10031b298 #y=20, #x=38>
(Update: inspect will do except for large object it is difficult to break down the variables from the 200 lines of output, like in Rails, when you request.inspect or self.inspect in the ActionView object)
I also want to be able to print <br> to the end of each instance variable's value so as to print them out nicely on a webpage.
the difficulty now seems to be that not every instance variable has an accessor, so it can't be called with obj.send(var_name)
(the var_name has the "#" removed, so "#x" becomes "x")
Update: I suppose using recursion, it can print out a more advanced version:
#<Point:0x10031b462>
#x: 1
#y: 2
#link_to_point: #<Point:0x10031b298>
#x=38
#y=20
I would probably write it like this:
class Object
def all_variables(root=true)
vars = {}
self.instance_variables.each do |var|
ivar = self.instance_variable_get(var)
vars[var] = [ivar, ivar.all_variables(false)]
end
root ? [self, vars] : vars
end
end
def string_variables(vars, lb="\n", indent="\t", current_indent="")
out = "#{vars[0].inspect}#{lb}"
current_indent += indent
out += vars[1].map do |var, ivar|
ivstr = string_variables(ivar, lb, indent, current_indent)
"#{current_indent}#{var}: #{ivstr}"
end.join
return out
end
def inspect_variables(obj, lb="\n", indent="\t", current_indent="")
string_variables(obj.all_variables, lb, indent, current_indent)
end
The Object#all_variables method produces an array containing (0) the given object and (1) a hash mapping instance variable names to arrays containing (0) the instance variable and (1) a hash mapping…. Thus, it gives you a nice recursive structure. The string_variables function prints out that hash nicely; inspect_variables is just a convenience wrapper. Thus, print inspect_variables(foo) gives you a newline-separated option, and print inspect_variables(foo, "<br />\n") gives you the version with HTML line breaks. If you want to specify the indent, you can do that too: print inspect_variables(foo, "\n", "|---") produces a (useless) faux-tree format instead of tab-based indenting.
There ought to be a sensible way to write an each_variable function to which you provide a callback (which wouldn't have to allocate the intermediate storage); I'll edit this answer to include it if I think of something. Edit 1: I thought of something.
Here's another way to write it, which I think is slightly nicer:
class Object
def each_variable(name=nil, depth=0, parent=nil, &block)
yield name, self, depth, parent
self.instance_variables.each do |var|
self.instance_variable_get(var).each_variable(var, depth+1, self, &block)
end
end
end
def inspect_variables(obj, nl="\n", indent="\t", sep=': ')
out = ''
obj.each_variable do |name, var, depth, _parent|
out += [indent*depth, name, name ? sep : '', var.inspect, nl].join
end
return out
end
The Object#each_variable method takes a number of optional arguments, which are not designed to be specified by the user; instead, they are used by the recursion to maintain state. The given block is passed (a) the name of the instance variable, or nil if the variable is the root of the recursion; (b) the variable; (c) the depth to which the recursion has descended; and (d), the parent of the current variable, or nil if said variable is the root of the recursion. The recursion is depth-first. The inspect_variables function uses this to build up a string. The obj argument is the object to iterate through; nl is the line separator; indent is the indentation to be applied at each level; and sep separates the name and the value.
Edit 2: This doesn't really add anything to the answer to your question, but: just to prove that we haven't lost anything in the reimplementation, here's a reimplementation of all_variables in terms of each_variables.
def all_variables(obj)
cur_depth = 0
root = [obj, {}]
tree = root
parents = []
prev = root
obj.each_variable do |name, var, depth, _parent|
next unless name
case depth <=> cur_depth
when -1 # We've gone back up
tree = parents.pop(cur_depth - depth)[0]
when +1 # We've gone down
parents << tree
tree = prev
else # We're at the same level
# Do nothing
end
cur_depth = depth
prev = tree[1][name] = [var, {}]
end
return root
end
I feel like it ought to be shorter, but that may not be possible; because we don't have the recursion now, we have to maintain the stack explicitly (in parents). But it is possible, so the each_variable method works just as well (and I think it's a little nicer).
I see... Antal must be giving the advanced version here...
the short version then probably is:
def p_each(obj)
obj.instance_variables.each do |v|
puts "#{v}: #{obj.instance_variable_get(v)}\n"
end
nil
end
or to return it as a string:
def sp_each(obj)
s = ""
obj.instance_variables.each do |v|
s += "#{v}: #{obj.instance_variable_get(v)}\n"
end
s
end
or shorter:
def sp_each(obj)
obj.instance_variables.map {|v| "#{v}: #{obj.instance_variable_get(v)}\n"}.join
end
This is a quick adaptation of a simple JSON emitter I wrote for another question:
class Object
def inspect!(indent=0)
return inspect if instance_variables.empty?
"#<#{self.class}:0x#{object_id.to_s(16)}\n#{' ' * indent+=1}#{
instance_variables.map {|var|
"#{var}: #{instance_variable_get(var).inspect!(indent)}"
}.join("\n#{' ' * indent}")
}\n#{' ' * indent-=1}>"
end
end
class Array
def inspect!(indent=0)
return '[]' if empty?
"[\n#{' ' * indent+=1}#{
map {|el| el.inspect!(indent) }.join(",\n#{' ' * indent}")
}\n#{' ' * indent-=1}]"
end
end
class Hash
def inspect!(indent=0)
return '{}' if empty?
"{\n#{' ' * indent+=1}#{
map {|k, v|
"#{k.inspect!(indent)} => #{v.inspect!(indent)}"
}.join(",\n#{' ' * indent}")
}\n#{' ' * indent-=1}}"
end
end
That's all the magic, really. Now we only need some simple defaults for some types where a full-on inspect doesn't really make sense (nil, false, true, numbers, etc.):
module InspectBang
def inspect!(indent=0)
inspect
end
end
[Numeric, Symbol, NilClass, TrueClass, FalseClass, String].each do |klass|
klass.send :include, InspectBang
end
Like this?
# Get the instance variables of an object
d = Date.new
d.instance_variables.each{|i| puts i + "<br />"}
Ruby Documentation on instance_variables.
The concept is commonly called "introspection", (to look into oneself).