tags:

views:

75

answers:

2

I'm using a rake script to tag the AssemblyInfo.cs files in my .NET projects, but there's a phantom carriage return (or newline, etc.) being introduced that's breaking the compilation. The method that gets the version number is:

def get_version()
  version = ''
  IO.popen("#{SVNVERSION_APPLICATION} #{build_info.root_dir}") do |output|
    output.readlines.each do |line|
      version << line.gsub(/\:.*/, '')
    end
  end
  result = version
end

If there's a better way to do that, please let me know. I'm mostly tinkering here (and this is actually the first Ruby I've ever worked on, so bear with me). As you can see (and my regex skills have never really been ninja-level) I'm just trying to get rid of the first colon and everything after it. Svnversion returns things like "64:67M" and I'm interested only in the first number.

Then I tag the files as such:

contents = contents.gsub(/AssemblyVersion\(\"([\d.*]*)\"\)/, "AssemblyVersion(\"#{helper.build_info.build_number_template}\")")

Naturally, there's more code surrounding these bits. This is just the meat of what's going on. Basically, in the task I call get_version to store the version number on a helper object and that object is later used in a method that reads AssemblyInfo.cs files into "contents" and does the substitution and writes it back out.

Everything is working fine except for a phantom newline in the output:

[assembly: AssemblyVersion("1.1.0.62
")]
[assembly: AssemblyFileVersion("1.1.0.62
")]

I've tried adding another .gsub to try to filter out \n and \r with no luck, I've tried .chomp, etc. It always seems to put that newline in the resulting file.

Am I missing something obvious?

[Edit in response to first answer:]

Here's the method that edits the AssemblyInfo.cs file:

def replace_assembly_strings(file_path, helper)

    if not (File.exists?(file_path) || File.writable?(file_path))
      raise "the file_path \"#{file_path}\" can not be written to.  Does it exist?"
    end

    path = Pathname.new(file_path)
    contents = path.read
    puts "AssemblyVersion(\"#{helper.build_info.build_number_template}\")"
    contents = contents.gsub(/AssemblyVersion\(\"([\d.*]*)\"\)/, "AssemblyVersion(\"#{helper.build_info.build_number_template}\")")
    contents = contents.gsub(/AssemblyFileVersion\(\"([\d.*]*)\"\)/, "AssemblyFileVersion(\"#{helper.build_info.build_number_template}\")")
    contents = contents.gsub(/AssemblyCompany\(\"(.*)\"\)/, "AssemblyCompany(\"#{helper.build_info.company}\")")
    contents = contents.gsub(/AssemblyCopyright\(\"(.*)\"\)/, "AssemblyCopyright(\"#{helper.build_info.copyright}\")")

    File.open(file_path, 'w') {|f| f.write(contents)}

  end

That puts line outputs AssemblyVersion("1.1.0.62") but the resulting file shows AssemblyVersion("1.1.0.>")

+1  A: 

Readlines doesn't implicitly remove the newline from the end of the line. Usually people invoke chomp! on the result. You can figure things like this out with the p method (or debugger, I guess), adding p line before you invoke your gsub. Then you should see (unless I'm astoundingly wrong) that your versions actually look like "64:67M\n" However, you have an easier solution available, String#to_i will convert the string to an int until it finds a non digit. So "64:67M".to_i # => 64 and "64:67M\n".to_i # => 64 Which saves you from having to to be a regex ninja, and resolves your newline issue.

Joshua Cheek
This is interesting. Putting in some p commands to view debug output shows the correct number, but the file output is broken in a new way now. For example, if I say: puts "AssemblyVersion(\"#{helper.build_info.build_number_template}\")" just before my .gsub on the assembly file, it shows the correct output. But in the written file it shows the version number as "1.1.0.>" instead. This text box isn't pretty enough to show this, I'll edit the question...
David
+1  A: 

This doesn't really solve your problem, here is a bit of a refactoring of that second method to make it look a bit more like idiomatic ruby. I know I wrote it the same way when I was learning the language, but there were a lot of thing that made that function look like C# or java written in ruby.

def replace_assembly_strings path, helper
  raise %{the path "#{path}" can not be written to.  Does it exist?} unless File.exists?(path) or File.writable?(path)

  file = Pathname.new(path).read

  methods = {/(AssemblyVersion\(\")[\d.*]*(\"\))/      => helper.build_info.build_number_template, 
             /(AssemblyFileVersion\(\")[\d.*]*(\"\))/  => helper.build_info.build_number_template, 
             /(AssemblyCopyright\(\").*(\"\))/         => helper.build_info.copyright,
             /(AssemblyCompany\(\").*(\"\))/           => helper.build_info.company}

  methods.keys.each do |regex| 
    file.gsub! regex, "\1#{methods[regex]}\2"
  end

  File.open(path, 'w') {|f| f.write(file)}
end

There is a lot of personal style involved in ruby code, this is just the way I would do it. This sort of thing was pure gold to me when I was learning, so I'll go through step by step in detail on why I changed what I changed.

So, starting from the top :)

First I dropped the parens from the method signature. As a general practice, you shouldn't use parens unless either you need to, since excessive punctuation tends to make stuff harder to read. Also went from file_path to path, again, this is just a terseness thing (and personal taste)

Next, you will never see if not in ruby, unless is always used. It takes a bit of getting used to, but the less boolean algebra you need to do while reading, the better. Removed some parens as well (same reasoning as the first time), and switched the || to or.

When it comes to and/or vs &&/||, I like the former quite a bit more (going back to the point on punctuation). That being said, there is a pretty huge gotcha that can happen from using that form because of the difference in operator precedence. Lets say you have something like this

def foo bar
  'foobar!' if bar
end

foobar = foo false || true
# foobar == 'foobar!'

What will happen first is false || true will evaluation to true, then true gets passed into foo. if we go the other way

foobar = foo false or true
# foobar == true ????

first, false will get passed to foo. foo will return nil, and nil is considered false in boolean expressions, so nil or true ends up evaluating to true.

As you can see, this can lead to REALLY strange bugs. Because of that, a lot of rubyists just use the &&/|| forms exclusively. Personally, I just try to keep that issue in mind, because I really like and/or better.

Last point on the guard clause, I swapped quotes for %{...} syntax. There are an absolutely insane amount of ways to make string literals in ruby. Because of that, there is always a way to avoid having to escape your quotes.

Next change was just in the name of terseness. In a general way, I try to minimize the amount of variables I use, although this is also a style thing.

Next change is the biggest.

First thing I did was change all your regexs to have a grouping (()) around the beginning and end bits we wanted to stay the same, and removed the grouping around the stuff we want to change. I did that because with gsub, we can get a reference to whatever was matched by the groups in the other side (\1 is the first group, \2 the second). That helps a lot when it comes to reducing noise, regexs are already hard enough to read ;-)

Next, I tried to address that you were basically applying the same operation to four things in a brute force kind of way. When you find yourself doing that, usually it will make things a lot more clear if you separate the operation from what you want to operate on. The other consideration here is that we are dealing with moderately long regexs, which again, are hard enough to read all by themselves.

Pull that stuff into a hash of the regex and what to replace it with makes that a lot clearer. So now we can loop over that hash and do the substitution.

You may notice that I changed the gsub to gsub!, and got rid of the assignment. In general, methods that end with ! will be variants of the ones that don't end with !, but in a way where you have to pay more attention to using it. In this case, gsub returns a new string with the substitution, gsub! does the substitution in place. Another point here is that in ruby, strings are mutable, which is a subtle difference to C#, but allows for things like in place substitution.

In general, most of the differences can be boiled down to three things; DRY (don't repeat yourself) is taken WAY further in ruby then it is in C#, dont use punctuation unless you need to, and embrace the terseness of the language by doing things with the least typing you can (before reducing readability)

Let me know if you have any comments/questions, and good luck with your further ruby adventures :)

Matt Briggs
This was extremely helpful in my general Ruby programming, thank you. This is really my first attempt at Ruby, so ti's definitely going to look like it is written by a C# programmer :) But I definitely want to approach it not as "a different syntax to do the same thing" but rather "a different way to do things." There are a lot of inherent strengths to the language and I have to take the time to approach the problems differently. Thanks!
David