Syntax for calling and how to write yaml file in ruby - ruby

Here is main.rb file (the driver of the project)
require_relative '../lib/logic'

So first of all, how to make a YAML file? Well, there are many tutorials out there to teach you the basics of a format, but a little shortcut is to just use the .to_yaml method on a Ruby object, and look at the output:
require 'yaml'
checks_to_run = [
"check_alphabetized_constants",
"check_bfr_return_emp_line"
]
puts checks_to_run.to_yaml
Which prints
---
- check_alphabetized_constants
- check_bfr_return_emp_line
--- always goes on the first line, and then you have a list of strings (quotations are optional) - simple enough.
You can write this to a file like so:
File.open("checks.yaml", "w") { |f| f.write checks_to_run.to_yaml }
You can of course write or edit the YAML file by hand as needed.
Now, to read the YAML file:
checks_to_run = YAML.load(File.read("checks.yaml"))
# => ["check_alphabetized_constants", "check_bfr_return_emp_line"]
From this point, you can loop through the checks and call the methods. There are multiple ways to do this, for example you could use send:
checks_to_run.each do |check_to_run|
check.send(check_to_run)
end
Or you could skip the metaprogramming and use something like if:
if checks_to_run.include?("check_alphabetized_constants")
check.check_alphabetized_constants
end
# repeat for the other checks as well

Related

How to write a file that is both valid ruby syntax and valid YAML syntax

In order to have only a single point of configuration for my app I need to make a YAML config file that is also valid ruby code. I.e. a mixed syntax file that can be parsed as YAML and parsed as ruby.
My application is a suite of processes managed by the god gem. I want to load a new group of maintained processes (watches) for each new configuration file.
God allows loading a new app.god (ruby) file with new watches defined, but I don't want an app.god and app.yml, just one file. Simplest might be to just have the app.god file and include the configuration within that, but I preferred the idea of a yml file that was also valid ruby code.
#I found this that might be helpful:
#This is a valid ruby and a valid YAML file
#Comments are the same in YAML and ruby
true ?true:
- <<YAML.to_i
# At this point in ruby it is the contents of a here doc (that will be
# converted to an integer and negated if true happens not to be true)
# In YAML it is a hash with the first entry having key "true ?true"
# representing a list containing the string "- <<YAML.to_i"
# If your YAML object should be a list not a hash you could remove the first line
any_valid_yaml: from here
a_list:
- or
- anything
- really
#Then mark the end of the YAML document with
---
#And YAML is done and ignores anything from here on
#Next terminate the ruby here document
YAML
#Now we're in ruby world
#this = "pure ruby"
def anything(ruby)
"here"
end

Reading and writing to and from files - can you do it the same way? (Ruby)

I'm in the process of learning Ruby and reading through Chris Pine's book. I'm learning how to read (and write) files, and came upon this example:
require 'yaml'
test_array = ['Give Quiche A Chance',
'Mutants Out!',
'Chameleonic Life-Forms, No Thanks']
test_string = test_array.to_yaml
filename = 'whatever.txt'
File.open filename, 'w' do |f|
f.write test_string
end
read_string = File.read filename
read_array = YAML::load read_string
puts(read_string == test_string)
puts(read_array == test_array )
The point of the example was to teach me about YAML, but my question is, if you can read a file with:
File.read filename
Can you write to a file in a similar way?:
File.write filename test_string
Sorry if it's a dumb question. I was just curious why it's written the way it was and if it had to be written that way.
Can you write to a file in a similar way?
Actually, yes. And it's pretty much exactly as you guessed:
File.write 'whatever.txt', test_array.to_yaml
I think it is amazing how intuitive Ruby can be.
See IO.write for more details. Note that IO.binwrite is also available, along with IO.read and IO.binread.
The Ruby File class will give you new and open but it inherits from the IO class so you get the read and write methods too.
I think the right way to write into a file is the following:
File.open(yourfile, 'w') { |file| file.write("your text") }
To brake this line down:
We first open the file setting the access mode ('w' to overwrite, 'a' to append, etc.)
We then actually write into the file
You can read or write to a file by specifying the mode you access it through. The Ruby File class is a subclass of IO.
The File class open or new methods take a path and a mode as arguments:
File.open('path', 'mode') alternatively: File.new('path','mode')
Example: to write to an existing file
somefile = File.open('./dir/subdirectory/file.txt', 'w')
##some code to write to file, eg:
array_of_links.each {|link| somefile.puts link }
somefile.close
See the source documentation as suggested above for more details, or similar question here: How to write to file in Ruby?

Write to file as CSV

I'm using this bit of code that I found on stackoverflow to write data to a file.
begin
file = File.open("/tmp/some_file", "w")
file.write("your text")
rescue IOError => e
#some error occur, dir not writable etc.
ensure
file.close unless file == nil
end
where it says your text if I put one of my variable names it will fill in the correct string used as that variable but ideally what I want is to have more than one variable in the output ideally in CSV. Something like:
file.write(var1 ^ var2 ^ var3)
Where each of my variables are seperated by a carrot symbol. How can I do this?
Actually There is a gem for CSV although there is a CSV library in standard ruby library.
Take a look at examples and you will find out that writing CSV files in ruby is much more easier that you thought.
http://fastercsv.rubyforge.org/classes/FasterCSV.html
http://fastercsv.rubyforge.org/
Ruby Stdlib: http://ruby-doc.org/stdlib-1.9.3/libdoc/csv/rdoc/CSV.html#method-c-generate

A ruby script to run tail on a log file?

I want to write a ruby script that read from a config file that will have filenames, and then when I run the script it will take the tail of each file and output the console.
What's the best way to go about doing this?
Take a look at File::Tail gem.
You can invoke linux tail -number_of_lines file_name command from your ruby script and let it print on console or capture output and print it yourself (if you need to do something with these lines before you print it)
We have a configuration file that contain a list of the log files; for example, like this:
---
- C:\fe\logs\front_end.log
- C:\mt\logs\middle_tier.log
- C:\be\logs\back_end.log
The format of the configuration file is a yaml simple sequence , therefore suppose we named this file 'settings.yaml'
The ruby script that take the tail of each file and output the console could be like this:
require 'yaml'
require 'file-tail'
logs = YAML::load(File.open('settings.yaml'))
threads = []
logs.each do |the_log|
threads << Thread.new(the_log) { |log_filename|
File.open(log_filename) do |log|
log.extend(File::Tail)
log.interval = 10
log.backward(10)
log.tail { |line| p "#{File.basename(the_log,".log")} - #{line}" }
end
}
end
threads.each { |the_thread| the_thread.join }
Note: displaying each line I wanted to prefix it with the name of the file from which it originates, ...this for me is a good option but you can edit the script to change as you like ; is the same for the tails parameters.
if file-tail is missing in your environment, follow the link as #Mark Thomas posts in his answear; i.e you need to:
> gem install file-tail
I found the file-tail gem to be a bit buggy. I would write to a file and it would read the entire file again instead of just thelines appended. This happened even though I had log.backward set to 0. I ended up writing my own and figured that I would share it here in case any one else is looking for a Ruby alternative to the file-tail gem. You can find the repo here. It uses non_blocking io, so it will catch amendments to the file immediately. There is one caveat that can be easily fixed if you can program in the Ruby programming language; log.backward is hard coded to be -1.

Good Way to Handle Many Different Files?

I'm building a specialized pipeline, and basically, every step in the pipeline involves taking one file as input and creating a different file as output. Not all files are in the same directory, all output files are of a different format, and because I'm using several different programs, different actions have to be taken to appease the different programs.
This has led to some complicated file management in my code, and the more I try to organize the file directories, the more ugly it's getting. Just about every class involves some sort of code like the following:
#fileName = File.basename(file)
#dataPath = "#{$path}/../data/"
MzmlToOther.new("mgf", "#{#dataPath}/spectra/#{#fileName}.mzML", 1, false).convert
system("wine readw.exe --mzXML #{#file}.raw #{$path}../data/spectra/#{File.basename(#file + ".raw", ".raw")}.mzXML 2>/dev/null")
fileName = "#{$path}../data/" + parts[0] + parts[1][6..parts[1].length-1].chomp(".pep.xml")
Is there some sort of design pattern, or ruby gem, or something to clean this up? I like writing clean code, so this is really starting to bother me.
You could use a Makefile.
Make is essential a DSL designed for handling converting one type of file to another type via running an external program. As an added bonus, it will handle only performing the steps necessary to incrementally update your output if some set of source files change.
If you really want to use Ruby, try a rakefile. Rake will do this, and it's still Ruby.
You can make this as sophisticated as you want but this basic script will match a file suffix to a method which you can then call with the file path.
# a conversion method can be used for each file type if you want to
# make the code more readable or if you need to rearrange filenames.
def htm_convert file
"HTML #{file}"
end
# file suffix as key, lambda as value, the last uses an external method
routines = {
:log => lambda {|file| puts "LOG #{file}"},
:rb => lambda {|file| puts "RUBY #{file}"},
:haml => lambda {|file| puts "HAML #{file}"},
:htm => lambda {|file| puts htm_convert(file) }
}
# this loops recursively through the directory and sub folders
Dir['**/*.*'].each do |f|
suffix = f.split(".")[-1]
if routine = routines[suffix.to_sym]
routine.call(f)
else
puts "UNPROCESSED -- #{f}"
end
end

Resources