I first wrote Ruby code, I want a review. Checked rubocop ohm, he has no objections.

UPD: I wrote rubocop rakefile , and it was necessary rubocop Rakefile . Corrected everything, except for too long line. What to do with the string?

Task: write a rake command to create a documentation file template. The markup and content of the file are irrelevant

In the header of the file - the date of the document in the format YYYY Month DD . On the title is an anchor link with a date in the format YYYYMMDD . Next, the contents of the template file are copied.

What causes my doubts:

  • Do I work correctly with reading and writing to files?
    • In particular, instead of line-by-line rewriting of a file, wasn’t it worth it to somehow copy it entirely? Is there any functional for this?
  • Perhaps, instead of templating strings, it would be better to template the entire file? In Python, I would apply Jinja in this case.
  • Should I write some help? Or will everyone use rake --tasks ?

Rakefile code:

 require 'date' desc 'Begin new release notes' task :rnotes, [:date] do |_t, args| if args.date date = args.date else puts 'Enter the date for the release notes in YYYYMMDD format: ' date = STDIN.gets.chomp end filename = "release-notes/#{date}.md" puts "Creating new release notes: #{filename}" d = Date.strptime(date, '%Y%m%d') longdate = d.strftime('%Y %B %d') open(filename, 'w') do |release_notes| release_notes.puts "## #{longdate} <a id=\"#{date}\" class=\"anchor\" href=\"#{date}\"></a>" release_notes.puts '' template = 'templates/rnotes.md' File.readlines(template).each do |line| release_notes.puts line end end end 

    1 answer 1

    Do I work correctly with reading and writing to files?

    Almost good, yes.

    Kernel.open (which is just open ) should be replaced with File.open if you plan to open only files with it. Because the method from Kernel able not only it.

    And readlines reads the entire file into memory and returns the array already collected from strings. You can read a file line by line with a lazier iterator, like what File#each_line :

     File.open(template) do |file| file.each_line do |line| release_notes.puts line end end 

    Perhaps, instead of templating strings, it would be better to template the entire file? In Python, I would apply Jinja in this case.

    This is a good option. In the standard Ruby library there is an ERB, the templates on which consist essentially of Ruby . But with the current formatting complexity, there will be little benefit from this. The transition will be justified only when the pattern becomes more complicated.

    Should I write some help ? Or will everyone use rake --tasks ?

    In addition to desc (which you already have) in Rake no funds. You can put a similar description of the task in the readme or other introductory documentation for the project.


    Now not about the questions:


     task :rnotes 

    It is better to abstain from reduction in favor of release_notes , if it certainly does not carry any special meaning.


      if args.date date = args.date else puts 'Enter the date for the release notes in YYYYMMDD format: ' date = STDIN.gets.chomp end 

    In Ruby, if returns a value (in each scope, the return value becomes the value of the last expression), so patterns like " if X assign one else to assign another" are usually replaced with unconditional assignment, but with a conditional expression inside:

      date = if args.date args.date else puts 'Enter the date for the release notes in YYYYMMDD format: ' STDIN.gets.chomp end 

    ... and since the condition and the first return value are the same, this if successfully replaced by || :

      date = args.date || begin # хватай args.date, а если его нет, то... puts 'Enter the date for the release notes in YYYYMMDD format: ' STDIN.gets.chomp end 

     File.readlines(template).each do |line| release_notes.puts line end 

    "See how I can"

     File.readlines(template).each(&release_notes.method(:puts)) 

    The technique is rather unusual for Ruby, but it works. Wanted functionalism? Here's a pinch of points-free-style :)

    release_notes.method(:puts) is the puts method nailed to the "owner" ( release_notes ). Technically, this is an object of class Method , which has a method call , the call of which will lead to a call to the corresponding method of the corresponding object with the same arguments.

    Any object that has a call method can be transferred to a method instead of a block, providing an ampersand that indicates that it is not just an argument, but a block (which in each call there can be only one).

    I can not recommend this technique, it is not so often encountered, because its not use often makes the code more readable. Compare:

     (0..9).map { |i| 7 + i } (0..9).map(&7.method(:+)) 

    That seems to be all. Maybe even find something.

    • I applied everything except for the functional area, which I just admired, but decided not to apply. - Nick Volynkin
    • File.open(template).each do |file| and why is there each ? I have one file. And I search its lines in a nested loop. - Nick Volynkin
    • @NickVolynkin my cant, there .each superfluous, fixed. - D-side